Skip to content

Commit

Permalink
Merge pull request #66 from degica/task/MOB-57
Browse files Browse the repository at this point in the history
Main state provider functionality update
  • Loading branch information
tharindu-rhino authored Aug 28, 2024
2 parents e35b73b + d962513 commit da28c2a
Show file tree
Hide file tree
Showing 11 changed files with 61 additions and 65 deletions.
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

0 comments on commit da28c2a

Please sign in to comment.