Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Main state provider functionality update #66

Merged
merged 3 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions example/PaymentScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
Pressable,
Alert,
} from 'react-native';
import {KomojuSDK} from '@komoju/komoju-react-native';
import {KomojuSDK, SessionShowResponseType} from '@komoju/komoju-react-native';
import createSession from './services/sessionService';

export enum CurrencyTypes {
Expand Down Expand Up @@ -51,7 +51,7 @@ const PaymentScreen = ({
};

// when the payment is complete pass a callback to get the final results of response
const onPaymentComplete = (response: any) => {
const onPaymentComplete = (response: SessionShowResponseType) => {
console.log(`Transaction Status: ${response?.status}`);
setAmount('');
};
Expand Down
2 changes: 2 additions & 0 deletions payment_sdk/src/assets/languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
"UPDATE_PAYMENT_METHOD": "Update payment method",
"SESSION_EXPIRED": "Session Expired",
"SESSION_EXPIRED_MSG": "This session has already expired. Please try again.",
"PAYMENT_WAITING": "Awaiting Payment",
"PAYMENT_WAITING_MSG": "Your payment has not been completed yet. We have e-mailed you the instructions on how to complete your purchase.",

"PAYMENT_VIA_ALI_PAY": "Payment via Alipay",
"ALI_PAY_REDIRECT_MESSAGE": "You will be redirected to Alipay to complete the payment",
Expand Down
6 changes: 4 additions & 2 deletions payment_sdk/src/assets/languages/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@
"UPDATE_PAYMENT_METHOD": "支払い方法を変更する",
"SESSION_EXPIRED": "セッションが期限切れ",
"SESSION_EXPIRED_MSG": "このセッションは既に期限切れです。再度お試しください。",

"PAYMENT_WAITING": "支払い待ち",
"PAYMENT_WAITING_MSG": "お支払いはまだ完了していません。購入を完了するための手順を電子メールで送信しました。",

"PAYMENT_VIA_ALI_PAY": "Alipayによる支払い",
"ALI_PAY_REDIRECT_MESSAGE": "支払いを完了するには、Alipay にリダイレクトされます。",
"CONTINUE_TO_ALI_PAY": "アリペイに進む",
Expand Down Expand Up @@ -86,4 +88,4 @@
"EMAIL_ERROR": "有効なメールアドレスを入力してください",
"": ""
}
}
}
1 change: 1 addition & 0 deletions payment_sdk/src/components/Loader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ const styles = StyleSheet.create({
...StyleSheet.absoluteFillObject,
alignItems: "center",
justifyContent: "center",
zIndex: 6,
},
});
1 change: 1 addition & 0 deletions payment_sdk/src/components/SheetContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ const SheetContent = () => {
</>
}
ListFooterComponentStyle={styles.footerContent}
keyboardShouldPersistTaps="handled"
/>
</View>
);
Expand Down
14 changes: 5 additions & 9 deletions payment_sdk/src/components/SubmitButton.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import React, { useContext } from "react";
import React from "react";

import { StyleSheet, Text, TouchableOpacity } from "react-native";
import { Keyboard, StyleSheet, Text, TouchableOpacity } from "react-native";

import { useTranslation } from "react-i18next";

import { Actions, DispatchContext } from "@context/state";

import { ThemeSchemeType } from "@util/types";

import { resizeFonts, responsiveScale } from "@theme/scalling";
Expand All @@ -22,12 +20,11 @@ const SubmitButton = ({ label, labelSuffix, onPress, testID }: Props) => {
const { t } = useTranslation();
const theme = useCurrentTheme();
const styles = getStyles(theme);
const dispatch = useContext(DispatchContext);

const onSubmit = () => {
dispatch({ type: Actions.SET_PROCEED_PAYMENT, payload: true });
Keyboard.dismiss();
onPress();
}
};

return (
<TouchableOpacity
Expand Down Expand Up @@ -61,5 +58,4 @@ const getStyles = (theme: ThemeSchemeType) => {
fontWeight: "bold",
},
});

}
};
75 changes: 36 additions & 39 deletions payment_sdk/src/context/MainStateProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import React, {
useState,
} from "react";

import { Alert, AppState, Linking } from "react-native";
import { Alert, AppState, AppStateStatus, Linking } from "react-native";

import i18next from "i18next";

Expand All @@ -26,16 +26,16 @@ import {
PaymentStatuses,
ResponseScreenStatuses,
sessionPayProps,
TokenResponseStatuses,
} from "@util/types";
import { validateSessionResponse } from "@util/validator";

import "@assets/languages/i18n";
import { Actions, DispatchContext, KomojuContext, StateContext } from "./state";
import { Actions, DispatchContext, KomojuContext } from "./state";

export const MainStateProvider = (props: KomojuProviderIprops) => {
const dispatch = useContext(DispatchContext);
const [modalVisible, setModalVisible] = useState(false);
const { processedPayment } = useContext(StateContext)

const sheetRef = useRef<SheetRefProps>(null);
// ref to hold client provided onComplete callback
Expand All @@ -52,66 +52,61 @@ export const MainStateProvider = (props: KomojuProviderIprops) => {
handleDeepLinkStateChange
);

return () => {
subscription.remove();
};
}, [props]);

useEffect(() => {
// Add event listener for deep links
const subscription = AppState.addEventListener(
const windowChangeListener = AppState.addEventListener(
"change",
handleBackgroundStateChange
);

return () => {
subscription.remove();
windowChangeListener.remove();
};
}, [processedPayment]);
}, [props]);

// This callback method is used to check the background state
const handleBackgroundStateChange = async () => {
const handleBackgroundStateChange = async (status: AppStateStatus) => {
startLoading();

if (processedPayment) {
if (status === "active") {
// if this is a session flow, check until session response changes from 'pending' to 'completed' or 'error'
const sessionShowPayload = {
publishableKey: props.publishableKey,
sessionId: sessionIdRef.current,
};

// fetch session status to check if the payment is completed
let sessionResponse = await sessionShow(sessionShowPayload);

// Polling until session verification status changes
while (
sessionResponse?.status === PaymentStatuses.PENDING &&
sessionResponse?.payment?.status !== PaymentStatuses.CANCELLED &&
!sessionResponse?.expired
) {
sessionResponse = await sessionShow(sessionShowPayload);
}
const sessionResponse = await sessionShow(sessionShowPayload);

// if payment success showing success screen or if failed showing error screen
if (sessionResponse?.status === PaymentStatuses.SUCCESS) {
if (sessionResponse?.payment?.payment_details?.instructions_url) {
openURL(sessionResponse?.payment?.payment_details?.instructions_url);
}
onPaymentSuccess();
// calling user passed onComplete method with session response data
if (
sessionResponse?.payment?.status === TokenResponseStatuses.CAPTURED
) {
onPaymentSuccess();
} else {
onPaymentAwaiting();
} // calling user passed onComplete method with session response data
onCompleteCallback.current &&
// TODO: Fix this type error
// @ts-expect-error - Argument of type 'PaymentSessionResponse' is not assignable to parameter of type 'string'.
onCompleteCallback.current(sessionResponse);
} else if (sessionResponse?.payment?.status === PaymentStatuses.CANCELLED) {
} else if (
sessionResponse?.payment?.status === PaymentStatuses.CANCELLED
) {
onPaymentCancelled();
} else if (sessionResponse?.expired) {
onSessionExpired()
} else {
onSessionExpired();
} else if (
sessionResponse?.status === PaymentStatuses.ERROR ||
sessionResponse?.payment?.status === PaymentStatuses.ERROR ||
sessionResponse?.secure_token?.verification_status ===
TokenResponseStatuses.ERROR
) {
onPaymentFailed();
}
}
dispatch({ type: Actions.SET_PROCEED_PAYMENT, payload: false });

// after all api calls are done stopping the loading indicator
stopLoading();
};
Expand Down Expand Up @@ -278,17 +273,20 @@ export const MainStateProvider = (props: KomojuProviderIprops) => {
// Polling until session verification status changes
while (
sessionResponse?.status === PaymentStatuses.PENDING &&
sessionResponse?.payment?.status !== PaymentStatuses.CANCELLED
sessionResponse?.payment?.status !== PaymentStatuses.CANCELLED &&
sessionResponse?.secure_token?.verification_status !==
TokenResponseStatuses.ERROR
) {
sessionResponse = await sessionShow(sessionShowPayload);
}

// if payment success showing success screen or if failed showing error screen
if (sessionResponse?.status === PaymentStatuses.SUCCESS) {
if (sessionResponse?.payment?.payment_details?.instructions_url) {
openURL(sessionResponse?.payment?.payment_details?.instructions_url);
if (sessionResponse?.payment?.status === TokenResponseStatuses.CAPTURED) {
onPaymentSuccess();
} else {
onPaymentAwaiting();
}
onPaymentSuccess();
// calling user passed onComplete method with session response data
onCompleteCallback.current &&
// TODO: Fix this type error
Expand All @@ -300,7 +298,6 @@ export const MainStateProvider = (props: KomojuProviderIprops) => {
onPaymentFailed();
}

dispatch({ type: Actions.SET_PROCEED_PAYMENT, payload: false });
// after all api calls are done stopping the loading indicator
stopLoading();
};
Expand All @@ -324,7 +321,7 @@ export const MainStateProvider = (props: KomojuProviderIprops) => {
if (response?.status === PaymentStatuses.PENDING) {
openURL(response.redirect_url);
} else if (response?.status === PaymentStatuses.SUCCESS) {
if (response?.payment?.status === PaymentStatuses.SUCCESS) {
if (response?.payment?.status === TokenResponseStatuses.CAPTURED) {
onPaymentSuccess();
} else if (response?.payment?.payment_details?.instructions_url) {
openURL(response?.payment?.payment_details?.instructions_url);
Expand Down Expand Up @@ -370,7 +367,7 @@ export const MainStateProvider = (props: KomojuProviderIprops) => {

// TODO: Fix this type error
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const initializeKomoju = useCallback((params: InitPrams) => { }, []);
const initializeKomoju = useCallback((params: InitPrams) => {}, []);

const renderPaymentUI = useMemo(() => {
const UI = props?.useBottomSheet ? (
Expand Down
6 changes: 0 additions & 6 deletions payment_sdk/src/context/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export const Actions = {
SET_PAYMENT_STATE: "SET_PAYMENT_STATE",
SET_PAYMENT_METHODS: "SET_PAYMENT_METHODS",
SESSION_PAY: "SESSION_PAY",
SET_PROCEED_PAYMENT: "SET_PROCEED_PAYMENT",
};

/**
Expand Down Expand Up @@ -124,11 +123,6 @@ export function reducer(state: State, action: ActionType) {
...state,
paymentMethods: action.payload,
};
case Actions.SET_PROCEED_PAYMENT:
return {
...state,
processedPayment: action.payload,
};
default:
throw new Error();
}
Expand Down
10 changes: 9 additions & 1 deletion payment_sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { useContext } from "react";
import { KomojuProvider } from "@context/KomojuProvider";
import { KomojuContext } from "@context/state";

import { LanguageTypes, PaymentType } from "@util/types";
import {
LanguageTypes,
PaymentType,
SessionShowResponseType,
} from "@util/types";

/**
* KomojuSDK provides context utilities for the Komoju payment system.
Expand Down Expand Up @@ -33,4 +37,8 @@ export {
* Supported languages to parse for language prop.
*/
LanguageTypes,
/**
* Response payload for onComplete and onDismiss callbacks.
*/
SessionShowResponseType,
};
2 changes: 1 addition & 1 deletion payment_sdk/src/util/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const API_HEADER = (publishableKey: string) => ({
export const paymentSuccessCtaText = "BACK_TO_STORE";
export const paymentFailedCtaText = "UPDATE_PAYMENT_METHOD";

export const emailRegex = /^[a-zA-Z09._-]+@[a-zA-Z09.-]+\.[a-zA-Z]{2,4}$/;
export const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/;
export const cardTypeRegex = {
amex: /^3[47]\d{0,13}/,
diner: /^3(?:0([0-5]|9)|[689]\d?)\d{0,11}/,
Expand Down
5 changes: 0 additions & 5 deletions payment_sdk/src/util/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,10 +222,6 @@ export type State = CardDetailsType &
* Global loading state. to display loading animation over sdk and disable buttons.
*/
loading: boolean;
/**
* Global processedPayment state. to indicate whether the payment was processed
*/
processedPayment: boolean;
/**
* Callback function to call relevant api for each payment type.
*/
Expand Down Expand Up @@ -270,7 +266,6 @@ export type sessionPayProps = {
export const initialState: State = {
paymentType: PaymentType.CREDIT,
loading: false,
processedPayment: false,

/** credit card payment related states start */
cardholderName: "",
Expand Down
Loading