Skip to content

Commit

Permalink
Merge pull request #65 from degica/task/MOB-56
Browse files Browse the repository at this point in the history
added background listner for the response
  • Loading branch information
tharindu-rhino authored Aug 27, 2024
2 parents e47553d + 6268275 commit f17df2e
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 7 deletions.
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 @@ -45,6 +45,8 @@
"PAYMENT_CANCELLED_MSG": "We noticed that you’ve canceled the payment process. If this was a mistake, you can try again to complete your purchase.",
"BACK_TO_STORE": "Back to store",
"UPDATE_PAYMENT_METHOD": "Update payment method",
"SESSION_EXPIRED": "Session Expired",
"SESSION_EXPIRED_MSG": "This session has already expired. Please try again.",

"PAYMENT_VIA_ALI_PAY": "Payment via Alipay",
"ALI_PAY_REDIRECT_MESSAGE": "You will be redirected to Alipay to complete the payment",
Expand Down
4 changes: 3 additions & 1 deletion payment_sdk/src/assets/languages/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@
"PAYMENT_RE_TRY_MSG": "入力していただいたカードで支払いを行うことができませんでした。別の支払い方法を試してみてください。",
"PAYMENT_CANCELLED_MSG": "お支払いプロセスがキャンセルされたことを確認しました。もしこれが誤りであれば、再度お試しいただき、購入を完了してください。",
"BACK_TO_STORE": "ストアに戻る",
"UPDATE_PAYMENT_METHOD": "支払い方法を変更する",
"UPDATE_PAYMENT_METHOD": "支払い方法を変更する",
"SESSION_EXPIRED": "セッションが期限切れ",
"SESSION_EXPIRED_MSG": "このセッションは既に期限切れです。再度お試しください。",

"PAYMENT_VIA_ALI_PAY": "Alipayによる支払い",
"ALI_PAY_REDIRECT_MESSAGE": "支払いを完了するには、Alipay にリダイレクトされます。",
Expand Down
6 changes: 5 additions & 1 deletion payment_sdk/src/components/PaymentModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const PaymentModal = ({
case ResponseScreenStatuses.SUCCESS:
case ResponseScreenStatuses.COMPLETE:
case ResponseScreenStatuses.CANCELLED:
case ResponseScreenStatuses.EXPIRED:
return paymentSuccessCtaText;
case ResponseScreenStatuses.FAILED:
return paymentFailedCtaText;
Expand All @@ -95,6 +96,8 @@ const PaymentModal = ({
case ResponseScreenStatuses.COMPLETE:
case ResponseScreenStatuses.CANCELLED:
return closeSheet(false);
case ResponseScreenStatuses.EXPIRED:
return closeSheet(false);
case ResponseScreenStatuses.FAILED:
return dispatch({
type: Actions.SET_PAYMENT_STATE,
Expand All @@ -110,7 +113,8 @@ const PaymentModal = ({
!(
paymentState === ResponseScreenStatuses.SUCCESS ||
paymentState === ResponseScreenStatuses.CANCELLED ||
paymentState === ResponseScreenStatuses.COMPLETE
paymentState === ResponseScreenStatuses.COMPLETE ||
paymentState === ResponseScreenStatuses.EXPIRED
)
);
};
Expand Down
6 changes: 6 additions & 0 deletions payment_sdk/src/components/ResponseScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ const statusConfigs: Partial<Record<ResponseScreenStatuses, StatusConfig>> = {
defaultMessage: "PAYMENT_CANCELLED_MSG",
image: require("../assets/images/awaitingPayment.png"),
},
[ResponseScreenStatuses.EXPIRED]: {
title: "SESSION_EXPIRED",
defaultMessage: "SESSION_EXPIRED_MSG",
image: require("../assets/images/error.png"),
},
};

type Props = {
Expand Down Expand Up @@ -111,6 +116,7 @@ const getStyles = (theme: ThemeSchemeType) => {
marginBottom: responsiveScale(16),
textAlign: "center",
paddingHorizontal: responsiveScale(32),
color: theme.TEXT_COLOR,
},
bottomButton: {
position: "absolute",
Expand Down
13 changes: 11 additions & 2 deletions payment_sdk/src/components/SubmitButton.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React from "react";
import React, { useContext } from "react";

import { 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 @@ -20,11 +22,18 @@ 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 });
onPress();
}

return (
<TouchableOpacity
testID={testID}
style={styles.buttonWrapper}
onPress={onPress}
onPress={onSubmit}
>
<Text style={styles.label}>
{labelSuffix ? `${t(label)} ${labelSuffix}` : t(label)}
Expand Down
74 changes: 71 additions & 3 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, Linking } from "react-native";
import { Alert, AppState, Linking } from "react-native";

import i18next from "i18next";

Expand All @@ -30,11 +30,12 @@ import {
import { validateSessionResponse } from "@util/validator";

import "@assets/languages/i18n";
import { Actions, DispatchContext, KomojuContext } from "./state";
import { Actions, DispatchContext, KomojuContext, StateContext } 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 @@ -56,6 +57,65 @@ export const MainStateProvider = (props: KomojuProviderIprops) => {
};
}, [props]);

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

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

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

if (processedPayment) {
// 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);
}

// 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
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) {
onPaymentCancelled();
} else if (sessionResponse?.expired) {
onSessionExpired()
} else {
onPaymentFailed();
}
}
dispatch({ type: Actions.SET_PROCEED_PAYMENT, payload: false });
// after all api calls are done stopping the loading indicator
stopLoading();
};

const openPaymentSheet = () => {
if (props?.useBottomSheet) {
sheetRef?.current?.open();
Expand Down Expand Up @@ -111,6 +171,13 @@ export const MainStateProvider = (props: KomojuProviderIprops) => {
});
};

// when payment is failed invoking the error screen
const onSessionExpired = () =>
dispatch({
type: Actions.SET_PAYMENT_STATE,
payload: ResponseScreenStatuses.EXPIRED,
});

const onUserCancel = async () => {
if (onDismissCallback.current) {
const sessionShowPayload = {
Expand Down Expand Up @@ -233,6 +300,7 @@ 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 Down Expand Up @@ -302,7 +370,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: 6 additions & 0 deletions payment_sdk/src/context/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ 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 @@ -123,6 +124,11 @@ 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
9 changes: 9 additions & 0 deletions payment_sdk/src/util/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export enum PaymentStatuses {
SUCCESS = "completed",
PENDING = "pending",
CANCELLED = "cancelled",
EXPIRED = "expired",
}

export enum TokenResponseStatuses {
Expand All @@ -93,6 +94,8 @@ export enum ResponseScreenStatuses {
COMPLETE = "complete",
/** For displaying payment instruction screens for cancelled by the user */
CANCELLED = "cancelled",
/** For displaying payment instruction screens for expired user session */
EXPIRED = "expired",
}

export enum CurrencySign {
Expand Down Expand Up @@ -219,6 +222,10 @@ 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 @@ -246,6 +253,7 @@ export type State = CardDetailsType &
| ResponseScreenStatuses.COMPLETE
| ResponseScreenStatuses.FAILED
| ResponseScreenStatuses.CANCELLED
| ResponseScreenStatuses.EXPIRED
| "";
/**
* States of the Bank transfer and Pay Easy fields.
Expand All @@ -262,6 +270,7 @@ 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 f17df2e

Please sign in to comment.