diff --git a/src/assets/images/loader_animation.gif b/src/assets/images/loader_animation.gif
new file mode 100644
index 0000000..d4f4377
Binary files /dev/null and b/src/assets/images/loader_animation.gif differ
diff --git a/src/assets/images/lock_image.png b/src/assets/images/lock_image.png
new file mode 100644
index 0000000..0cd562e
Binary files /dev/null and b/src/assets/images/lock_image.png differ
diff --git a/src/assets/images/success_animation.gif b/src/assets/images/success_animation.gif
new file mode 100644
index 0000000..3579ee9
Binary files /dev/null and b/src/assets/images/success_animation.gif differ
diff --git a/src/assets/languages/en.json b/src/assets/languages/en.json
index f46ee07..033e897 100644
--- a/src/assets/languages/en.json
+++ b/src/assets/languages/en.json
@@ -51,6 +51,7 @@
"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.",
+ "LOADING_TEXT": "Processing...",
"PAYMENT_VIA_ALI_PAY": "Payment via Alipay",
"ALI_PAY_REDIRECT_MESSAGE": "You will be redirected to Alipay to complete the payment",
diff --git a/src/assets/languages/ja.json b/src/assets/languages/ja.json
index ab2fc53..40f104b 100644
--- a/src/assets/languages/ja.json
+++ b/src/assets/languages/ja.json
@@ -51,6 +51,7 @@
"SESSION_EXPIRED_MSG": "このセッションは既に期限切れです。再度お試しください。",
"PAYMENT_WAITING": "支払い待ち",
"PAYMENT_WAITING_MSG": "支払いは完了していません。支払い方法の詳細をメールアドレス宛にお送りしていますのでご確認ください。",
+ "LOADING_TEXT": "処理...",
"PAYMENT_VIA_ALI_PAY": "Alipayによる支払い",
"ALI_PAY_REDIRECT_MESSAGE": "支払いを完了するには、Alipay にリダイレクトされます。",
diff --git a/src/components/Button.tsx b/src/components/Button.tsx
new file mode 100644
index 0000000..57adab7
--- /dev/null
+++ b/src/components/Button.tsx
@@ -0,0 +1,73 @@
+import { StyleSheet, Text, TouchableOpacity, Keyboard } from "react-native";
+
+import { useTranslation } from "react-i18next";
+
+import { ThemeSchemeType } from "../util/types";
+
+import { resizeFonts, responsiveScale } from "../theme/scalling";
+import { useCurrentTheme } from "../theme/useCurrentTheme";
+import { ReactNode } from "react";
+
+type Props = {
+ onPress: () => void;
+ label: string;
+ labelSuffix?: string;
+ testID?: string;
+ style?: object;
+ disabled?: boolean;
+ children?: ReactNode;
+};
+
+const Button = ({
+ label,
+ labelSuffix,
+ onPress,
+ testID,
+ style,
+ disabled = false,
+ children,
+}: Props) => {
+ const { t } = useTranslation();
+ const theme = useCurrentTheme();
+ const styles = getStyles(theme);
+
+ const onSubmit = () => {
+ Keyboard.dismiss();
+ onPress();
+ };
+
+ return (
+
+
+ {labelSuffix ? `${t(label)} ${labelSuffix}` : t(label)}
+
+ {children}
+
+ );
+};
+
+export default Button;
+
+const getStyles = (theme: ThemeSchemeType) => {
+ return StyleSheet.create({
+ buttonWrapper: {
+ backgroundColor: theme.PRIMARY_COLOR,
+ borderRadius: responsiveScale(8),
+ height: responsiveScale(50),
+ marginHorizontal: responsiveScale(16),
+ flex: 1,
+ justifyContent: "center",
+ alignItems: "center",
+ },
+ label: {
+ color: "white",
+ fontSize: resizeFonts(16),
+ fontWeight: "bold",
+ },
+ });
+};
diff --git a/src/components/Input.tsx b/src/components/Input.tsx
index 626b5a8..b5b8b5a 100644
--- a/src/components/Input.tsx
+++ b/src/components/Input.tsx
@@ -1,4 +1,4 @@
-import React from "react";
+import React, { useContext } from "react";
import {
View,
@@ -13,10 +13,11 @@ import {
import { useTranslation } from "react-i18next";
-import { ThemeSchemeType } from "../util/types";
+import { ResponseScreenStatuses, ThemeSchemeType } from "../util/types";
import { resizeFonts, responsiveScale } from "../theme/scalling";
import { useCurrentTheme } from "../theme/useCurrentTheme";
+import { StateContext } from "../context/state";
interface InputProps extends TextInputProps {
value: string;
@@ -42,6 +43,8 @@ const Input: React.FC = ({
keyboardType,
...rest
}: InputProps) => {
+ const { loading, paymentState } = useContext(StateContext);
+
const { t } = useTranslation();
const theme = useCurrentTheme();
const styles = getStyles(theme);
@@ -57,11 +60,14 @@ const Input: React.FC = ({
placeholderTextColor={theme.INPUT_PLACEHOLDER}
style={[
styles.input,
- styles.withBorder,
error && styles.withErrorBorder,
+ (loading?.payment ||
+ paymentState === ResponseScreenStatuses.SUCCESS) &&
+ styles.disabledStyles,
inputStyle,
]}
testID={testID}
+ editable={!loading?.payment}
{...rest}
/>
{error && errorText && (
@@ -99,7 +105,9 @@ const getStyles = (theme: ThemeSchemeType) => {
color: theme.ERROR,
zIndex: 2,
},
- withBorder: {},
+ disabledStyles: {
+ color: theme.INPUT_PLACEHOLDER,
+ },
});
};
diff --git a/src/components/PaymentModal.tsx b/src/components/PaymentModal.tsx
index 747cda3..d88243c 100644
--- a/src/components/PaymentModal.tsx
+++ b/src/components/PaymentModal.tsx
@@ -1,14 +1,8 @@
import { Dispatch, SetStateAction } from "react";
-import {
- TouchableOpacity,
- Modal,
- View,
- Image,
- StyleSheet,
-} from "react-native";
+import { TouchableOpacity, Modal, View, Image, StyleSheet } from "react-native";
-import { ThemeSchemeType } from "../util/types";
+import { PaymentMode, sessionDataType, ThemeSchemeType } from "../util/types";
import closeIcon from "../assets/images/close.png";
@@ -34,6 +28,7 @@ const PaymentModal: React.FC = ({
const {
paymentState,
paymentType,
+ sessionData,
closeSheet,
getCtaText,
ctaOnPress,
@@ -43,6 +38,9 @@ const PaymentModal: React.FC = ({
const theme = useCurrentTheme();
const styles = getStyles(theme);
+ const SessionData = sessionData as sessionDataType;
+ const isCustomerMode = SessionData?.mode === PaymentMode.Customer;
+
const handleClose = () => {
closeSheet(shouldShowAlert(), () => setModalVisible(false));
};
@@ -59,13 +57,13 @@ const PaymentModal: React.FC = ({
- {!paymentState ? "PAYMENT_OPTIONS" : ""}
+ {!paymentState || isCustomerMode ? "PAYMENT_OPTIONS" : ""}
- {paymentState ? (
+ {paymentState && !isCustomerMode ? (
ctaOnPress(() => setModalVisible(false))}
diff --git a/src/components/ResponseScreen.tsx b/src/components/ResponseScreen.tsx
index a371b42..d43d380 100644
--- a/src/components/ResponseScreen.tsx
+++ b/src/components/ResponseScreen.tsx
@@ -12,7 +12,7 @@ import { resizeFonts, responsiveScale } from "../theme/scalling";
import { useCurrentTheme } from "../theme/useCurrentTheme";
import KomojuText from "./KomojuText";
-import SubmitButton from "./SubmitButton";
+import Button from "./Button";
import successIcon from "../assets/images/success.png";
import errorIcon from "../assets/images/error.png";
@@ -104,7 +104,7 @@ const ResponseScreen = ({
{renderIcon}
{renderMessageContent}
-
+
);
diff --git a/src/components/SheetContent.tsx b/src/components/SheetContent.tsx
index 1efad27..0bf3fc2 100644
--- a/src/components/SheetContent.tsx
+++ b/src/components/SheetContent.tsx
@@ -45,8 +45,6 @@ const SheetContent = () => {
dispatch({ type: Actions.SET_PAYMENT_OPTION, payload: type });
};
- const renderLoading = loading ? : null;
-
const getBaseScreenByPaymentType = (paymentType: PaymentType) => {
switch (paymentType) {
case PaymentType.CREDIT:
@@ -76,9 +74,17 @@ const SheetContent = () => {
const renderItem = () => {
return (
-
- {getBaseScreenByPaymentType(paymentType)}
- {renderLoading}
+ {loading?.app ? (
+
+ ) : (
+ <>
+
+ {getBaseScreenByPaymentType(paymentType)}
+ >
+ )}
);
};
diff --git a/src/components/SubmitButton.tsx b/src/components/SubmitButton.tsx
index 5d4f5e6..84819b2 100644
--- a/src/components/SubmitButton.tsx
+++ b/src/components/SubmitButton.tsx
@@ -1,11 +1,17 @@
-import { StyleSheet, Text, TouchableOpacity, Keyboard } from "react-native";
+import { useContext, useEffect, useRef, useState } from "react";
+import { Animated, Dimensions, Image, StyleSheet } from "react-native";
-import { useTranslation } from "react-i18next";
+import { ResponseScreenStatuses } from "../util/types";
+import { StateContext } from "../context/state";
+import { useCurrentTheme } from "../theme/useCurrentTheme";
+import Button from "./Button";
+import { responsiveScale } from "../theme/scalling";
-import { ThemeSchemeType } from "../util/types";
+import Lock from "../assets/images/lock_image.png";
+import SuccessAnimation from "../assets/images/success_animation.gif";
+import LoadingAnimation from "../assets/images/loader_animation.gif";
-import { resizeFonts, responsiveScale } from "../theme/scalling";
-import { useCurrentTheme } from "../theme/useCurrentTheme";
+const { width: SCREEN_WIDTH } = Dimensions.get("window");
type Props = {
onPress: () => void;
@@ -15,45 +21,80 @@ type Props = {
};
const SubmitButton = ({ label, labelSuffix, onPress, testID }: Props) => {
- const { t } = useTranslation();
const theme = useCurrentTheme();
- const styles = getStyles(theme);
+ const { loading, paymentState } = useContext(StateContext);
+ const [animationComplete, setAnimationComplete] = useState(false);
+
+ const moveAnim = useRef(new Animated.Value(responsiveScale(20))).current;
+
+ const styles = getStyles();
+
+ const isPaymentSuccess = paymentState === ResponseScreenStatuses.SUCCESS;
+
+ useEffect(() => {
+ if (paymentState === ResponseScreenStatuses.SUCCESS) {
+ moveToCenter();
+ }
+ }, [paymentState]);
- const onSubmit = () => {
- Keyboard.dismiss();
- onPress();
+ const moveToCenter = () => {
+ Animated.timing(moveAnim, {
+ toValue: (SCREEN_WIDTH - responsiveScale(60)) * 0.5,
+ duration: 400,
+ useNativeDriver: false,
+ }).start(() => setAnimationComplete(true));
};
return (
-
-
- {labelSuffix ? `${t(label)} ${labelSuffix}` : t(label)}
-
-
+ {loading?.payment || isPaymentSuccess ? (
+
+
+
+ ) : (
+
+ )}
+
);
};
-export default SubmitButton;
-
-const getStyles = (theme: ThemeSchemeType) => {
+const getStyles = () => {
return StyleSheet.create({
- buttonWrapper: {
- backgroundColor: theme.PRIMARY_COLOR,
- borderRadius: responsiveScale(8),
- height: responsiveScale(50),
- marginHorizontal: responsiveScale(16),
- flex: 1,
- justifyContent: "center",
- alignItems: "center",
+ indicatorContainer: {
+ position: "absolute",
+ },
+ iconLock: {
+ position: "absolute",
+ right: responsiveScale(20),
+ width: responsiveScale(15),
+ height: responsiveScale(15),
+ resizeMode: "contain",
},
- label: {
- color: "white",
- fontSize: resizeFonts(16),
- fontWeight: "bold",
+ iconAnimation: {
+ width: responsiveScale(30),
+ height: responsiveScale(30),
+ resizeMode: "contain",
},
});
};
+
+export default SubmitButton;
diff --git a/src/components/sections/CardSection.tsx b/src/components/sections/CardSection.tsx
index 044644f..f748535 100644
--- a/src/components/sections/CardSection.tsx
+++ b/src/components/sections/CardSection.tsx
@@ -60,7 +60,7 @@ const CardSection = (): JSX.Element => {
if (isValid) {
const cardDataObj: CardDetailsType = {
- cardholderName: cardData?.cardNumber,
+ cardholderName: cardData?.cardholderName,
cardCVV: cardData?.cardCVV,
cardNumber: cardData?.cardNumber,
cardExpiredDate: cardData?.cardExpiredDate,
diff --git a/src/context/MainStateProvider.tsx b/src/context/MainStateProvider.tsx
index b9c97b2..9ea8a47 100644
--- a/src/context/MainStateProvider.tsx
+++ b/src/context/MainStateProvider.tsx
@@ -56,10 +56,14 @@ export const MainStateProvider = (props: KomojuProviderIprops) => {
useMainStateUtils();
// Handle deep-links of the module
- useDeepLinkHandler(setIsDeepLinkOpened);
+ useDeepLinkHandler(setIsDeepLinkOpened, closePaymentSheet);
// Handle events when module goes foreground
- useBackgroundHandler(isDeepLinkOpened, setIsDeepLinkOpened);
+ useBackgroundHandler(
+ isDeepLinkOpened,
+ setIsDeepLinkOpened,
+ closePaymentSheet
+ );
// Handle validations of the session
const { validateSession } = useValidationHandler({
@@ -118,6 +122,7 @@ export const MainStateProvider = (props: KomojuProviderIprops) => {
createPayment,
showPaymentSheetUI,
initializeKomoju,
+ closePaymentSheet,
}}
>
{renderChildren}
diff --git a/src/context/state.ts b/src/context/state.ts
index e899770..bf1e876 100644
--- a/src/context/state.ts
+++ b/src/context/state.ts
@@ -140,6 +140,10 @@ const defaultValue = {
// TODO: Fix this type error
// eslint-disable-next-line @typescript-eslint/no-unused-vars
initializeKomoju: (_data: InitPrams) => {},
+ /**
+ * Function to close the payment sheet manually.
+ */
+ closePaymentSheet: noop,
};
/**
* State context for the Komoju payment system.
diff --git a/src/hooks/useBackgroundHandler.tsx b/src/hooks/useBackgroundHandler.tsx
index 72a7579..f377620 100644
--- a/src/hooks/useBackgroundHandler.tsx
+++ b/src/hooks/useBackgroundHandler.tsx
@@ -13,9 +13,12 @@ import { getTokenResult } from "../services/secureTokenService";
import payForSession from "../services/payForSessionService";
import useMainStateUtils from "./useMainStateUtils";
+let timeoutId: NodeJS.Timeout;
+
const useBackgroundHandler = (
isDeepLinkOpened: boolean,
- setIsDeepLinkOpened: Dispatch>
+ setIsDeepLinkOpened: Dispatch>,
+ closePaymentSheet: () => void
) => {
const { paymentType, tokenId, providerPropsData, sessionData } =
useContext(StateContext);
@@ -23,7 +26,7 @@ const useBackgroundHandler = (
const SessionData = sessionData as CreatePaymentFuncType;
const {
- startLoading,
+ startPaymentLoading,
stopLoading,
onPaymentAwaiting,
onPaymentCancelled,
@@ -44,6 +47,14 @@ const useBackgroundHandler = (
};
}, [providerPropsData, paymentType, tokenId, SessionData, isDeepLinkOpened]);
+ useEffect(() => {
+ () => {
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+ };
+ }, []);
+
const handleSessionPaymentResponse = async () => {
// if this is a session flow, check until session response changes from 'pending' to 'completed' or 'error'
const sessionShowPayload = {
@@ -61,6 +72,9 @@ const useBackgroundHandler = (
sessionResponse.mode === PaymentMode.Customer
) {
onPaymentSuccess();
+ if (sessionResponse.mode === PaymentMode.Customer) {
+ timeoutId = setTimeout(() => closePaymentSheet(), 2000);
+ }
} else {
onPaymentAwaiting();
} // calling user passed onComplete method with session response data
@@ -110,7 +124,7 @@ const useBackgroundHandler = (
const handleBackgroundStateChange = async (status: AppStateStatus) => {
if (status === "active" && !isDeepLinkOpened) {
- startLoading();
+ startPaymentLoading();
if (paymentType === PaymentType.CREDIT) {
await handleSecureTokenPaymentResponse();
diff --git a/src/hooks/useDeepLinkHandler.tsx b/src/hooks/useDeepLinkHandler.tsx
index 317d62e..5cc9407 100644
--- a/src/hooks/useDeepLinkHandler.tsx
+++ b/src/hooks/useDeepLinkHandler.tsx
@@ -14,15 +14,18 @@ import { extractParameterFromUrl } from "../util/helpers";
import payForSession from "../services/payForSessionService";
import useMainStateUtils from "./useMainStateUtils";
+let timeoutId: NodeJS.Timeout;
+
const useDeepLinkHandler = (
- setIsDeepLinkOpened: Dispatch>
+ setIsDeepLinkOpened: Dispatch>,
+ closePaymentSheet: () => void
) => {
const { paymentType, providerPropsData, sessionData } =
useContext(StateContext);
const SessionData = sessionData as CreatePaymentFuncType;
const {
- startLoading,
+ startPaymentLoading,
stopLoading,
onPaymentAwaiting,
onPaymentCancelled,
@@ -42,8 +45,16 @@ const useDeepLinkHandler = (
};
}, [paymentType, providerPropsData, sessionData]);
+ useEffect(() => {
+ () => {
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+ };
+ }, []);
+
const handleSessionPaymentResponse = async () => {
- startLoading();
+ startPaymentLoading();
// if this is a session flow, check until session response changes from 'pending' to 'completed' or 'error'
const sessionShowPayload = {
@@ -71,6 +82,9 @@ const useDeepLinkHandler = (
sessionResponse.mode === PaymentMode.Customer
) {
onPaymentSuccess();
+ if (sessionResponse.mode === PaymentMode.Customer) {
+ timeoutId = setTimeout(() => closePaymentSheet(), 2000);
+ }
} else {
onPaymentAwaiting();
}
@@ -87,7 +101,7 @@ const useDeepLinkHandler = (
};
const handleSecureTokenPaymentResponse = async (token: string) => {
- startLoading();
+ startPaymentLoading();
const tokenResponse = await getTokenResult({
publishableKey: providerPropsData.publishableKey,
diff --git a/src/hooks/useMainStateUtils.tsx b/src/hooks/useMainStateUtils.tsx
index 34d54d7..1a49ff9 100644
--- a/src/hooks/useMainStateUtils.tsx
+++ b/src/hooks/useMainStateUtils.tsx
@@ -1,27 +1,53 @@
-import { useContext } from "react";
+import { useContext, useEffect } from "react";
import { Actions, DispatchContext, StateContext } from "../context/state";
import {
- initialState,
ResponseScreenStatuses,
CreatePaymentFuncType,
+ sessionDataType,
+ PaymentMode,
} from "../util/types";
import sessionShow from "../services/sessionShow";
+import { getInitialStateWithoutProviderPropsData } from "../util/helpers";
+
+let timeoutId: NodeJS.Timeout;
const useMainStateUtils = () => {
const dispatch = useContext(DispatchContext);
const { providerPropsData, sessionData } = useContext(StateContext);
- const SessionData = sessionData as CreatePaymentFuncType;
+ const SessionData = sessionData as CreatePaymentFuncType & sessionDataType;
- const resetGlobalStates = () =>
- dispatch({
- type: Actions.RESET_STATES,
- payload: initialState,
- });
+ const isCustomerMode = SessionData?.mode === PaymentMode.Customer;
+
+ useEffect(() => {
+ () => {
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+ };
+ }, []);
+
+ const resetGlobalStates = (withTimeout = false) => {
+ if (withTimeout) {
+ timeoutId = setTimeout(
+ () =>
+ dispatch({
+ type: Actions.RESET_STATES,
+ payload: getInitialStateWithoutProviderPropsData(),
+ }),
+ 3000
+ );
+ } else {
+ dispatch({
+ type: Actions.RESET_STATES,
+ payload: getInitialStateWithoutProviderPropsData(),
+ });
+ }
+ };
// when payment is success global state is rest and invoking the success screen
const onPaymentSuccess = () => {
- resetGlobalStates();
+ resetGlobalStates(isCustomerMode);
dispatch({
type: Actions.SET_PAYMENT_STATE,
payload: ResponseScreenStatuses.SUCCESS,
@@ -37,7 +63,7 @@ const useMainStateUtils = () => {
// when payment is cancelled by the user
const onPaymentCancelled = () => {
- resetGlobalStates();
+ resetGlobalStates(isCustomerMode);
dispatch({
type: Actions.SET_PAYMENT_STATE,
payload: ResponseScreenStatuses.CANCELLED,
@@ -80,14 +106,29 @@ const useMainStateUtils = () => {
const startLoading = () =>
dispatch({
type: Actions.SET_LOADING,
- payload: true,
+ payload: {
+ app: true,
+ payment: false,
+ },
+ });
+
+ const startPaymentLoading = () =>
+ dispatch({
+ type: Actions.SET_LOADING,
+ payload: {
+ app: false,
+ payment: true,
+ },
});
// Hiding overlay loading indicator
const stopLoading = () =>
dispatch({
type: Actions.SET_LOADING,
- payload: false,
+ payload: {
+ app: false,
+ payment: false,
+ },
});
return {
@@ -98,6 +139,7 @@ const useMainStateUtils = () => {
onSessionExpired,
onUserCancel,
startLoading,
+ startPaymentLoading,
stopLoading,
resetGlobalStates,
};
diff --git a/src/hooks/usePaymentUiUtils.tsx b/src/hooks/usePaymentUiUtils.tsx
index 2db1f7c..4999693 100644
--- a/src/hooks/usePaymentUiUtils.tsx
+++ b/src/hooks/usePaymentUiUtils.tsx
@@ -2,86 +2,90 @@ import { useCallback, useContext } from "react";
import { Alert, Keyboard } from "react-native";
import { useTranslation } from "react-i18next";
import { Actions, DispatchContext, StateContext } from "../context/state";
-import {
- paymentFailedCtaText,
- paymentSuccessCtaText,
-} from "../util/constants";
+import { paymentFailedCtaText, paymentSuccessCtaText } from "../util/constants";
import { ResponseScreenStatuses } from "../util/types";
export const usePaymentUiUtils = (onDismiss?: () => void) => {
- const { t } = useTranslation();
- const { paymentState, paymentType } = useContext(StateContext);
- const dispatch = useContext(DispatchContext);
+ const { t } = useTranslation();
+ const { paymentState, paymentType, sessionData } = useContext(StateContext);
+ const dispatch = useContext(DispatchContext);
- const closeSheet = useCallback((showAlert = true, onClose: () => void) => {
- Keyboard.dismiss();
+ const closeSheet = useCallback(
+ (showAlert = true, onClose: () => void) => {
+ Keyboard.dismiss();
- if (showAlert) {
- Alert.alert(`${t("CANCEL_PAYMENT")}?`, "", [
- {
- text: t("NO"),
- style: "cancel",
- },
- {
- text: t("YES"),
- onPress: () => {
- onDismiss && onDismiss();
- onClose();
- },
- },
- ]);
- } else {
- onDismiss && onDismiss();
- onClose();
- }
- }, [onDismiss, t]);
+ if (showAlert) {
+ Alert.alert(`${t("CANCEL_PAYMENT")}?`, "", [
+ {
+ text: t("NO"),
+ style: "cancel",
+ },
+ {
+ text: t("YES"),
+ onPress: () => {
+ onDismiss && onDismiss();
+ onClose();
+ },
+ },
+ ]);
+ } else {
+ onDismiss && onDismiss();
+ onClose();
+ }
+ },
+ [onDismiss, t]
+ );
- const getCtaText = useCallback(() => {
- switch (paymentState) {
- case ResponseScreenStatuses.SUCCESS:
- case ResponseScreenStatuses.COMPLETE:
- case ResponseScreenStatuses.CANCELLED:
- case ResponseScreenStatuses.EXPIRED:
- return paymentSuccessCtaText;
- case ResponseScreenStatuses.FAILED:
- return paymentFailedCtaText;
- default:
- return "";
- }
- }, [paymentState]);
+ const getCtaText = useCallback(() => {
+ switch (paymentState) {
+ case ResponseScreenStatuses.SUCCESS:
+ case ResponseScreenStatuses.COMPLETE:
+ case ResponseScreenStatuses.CANCELLED:
+ case ResponseScreenStatuses.EXPIRED:
+ return paymentSuccessCtaText;
+ case ResponseScreenStatuses.FAILED:
+ return paymentFailedCtaText;
+ default:
+ return "";
+ }
+ }, [paymentState]);
- const ctaOnPress = useCallback((onClose: () => void) => {
- switch (paymentState) {
- case ResponseScreenStatuses.SUCCESS:
- case ResponseScreenStatuses.COMPLETE:
- case ResponseScreenStatuses.CANCELLED:
- case ResponseScreenStatuses.EXPIRED:
- return closeSheet(false, onClose);
- case ResponseScreenStatuses.FAILED:
- return dispatch({
- type: Actions.SET_PAYMENT_STATE,
- payload: "",
- });
- default:
- return "";
- }
- }, [paymentState, dispatch, closeSheet]);
+ const ctaOnPress = useCallback(
+ (onClose: () => void) => {
+ switch (paymentState) {
+ case ResponseScreenStatuses.SUCCESS:
+ case ResponseScreenStatuses.COMPLETE:
+ case ResponseScreenStatuses.CANCELLED:
+ case ResponseScreenStatuses.EXPIRED:
+ return closeSheet(false, onClose);
+ case ResponseScreenStatuses.FAILED:
+ return dispatch({
+ type: Actions.SET_PAYMENT_STATE,
+ payload: "",
+ });
+ default:
+ return "";
+ }
+ },
+ [paymentState, dispatch, closeSheet]
+ );
- const shouldShowAlert = useCallback(() => {
- return !(
- paymentState === ResponseScreenStatuses.SUCCESS ||
- paymentState === ResponseScreenStatuses.CANCELLED ||
- paymentState === ResponseScreenStatuses.COMPLETE ||
- paymentState === ResponseScreenStatuses.EXPIRED
- );
- }, [paymentState]);
+ const shouldShowAlert = useCallback(() => {
+ return !(
+ paymentState === ResponseScreenStatuses.SUCCESS ||
+ paymentState === ResponseScreenStatuses.CANCELLED ||
+ paymentState === ResponseScreenStatuses.COMPLETE ||
+ paymentState === ResponseScreenStatuses.EXPIRED
+ );
+ }, [paymentState]);
- return {
- paymentState,
- paymentType,
- closeSheet,
- getCtaText,
- ctaOnPress,
- shouldShowAlert,
- };
-};
\ No newline at end of file
+ return {
+ paymentState,
+ paymentType,
+ sessionData,
+ closeSheet,
+ getCtaText,
+ ctaOnPress,
+ shouldShowAlert,
+ };
+};
diff --git a/src/hooks/useSessionPayHandler.tsx b/src/hooks/useSessionPayHandler.tsx
index e8ce87e..2a4dd45 100644
--- a/src/hooks/useSessionPayHandler.tsx
+++ b/src/hooks/useSessionPayHandler.tsx
@@ -1,4 +1,4 @@
-import { useContext } from "react";
+import { useContext, useEffect } from "react";
import {
CreatePaymentFuncType,
@@ -7,17 +7,29 @@ import {
sessionPayProps,
TokenResponseStatuses,
} from "../util/types";
-import { StateContext } from "../context/state";
+import { StateContext, KomojuContext } from "../context/state";
import { openURL } from "../util/helpers";
import payForSession from "../services/payForSessionService";
import useMainStateUtils from "./useMainStateUtils";
+let timeoutId: NodeJS.Timeout;
+
const useSessionPayHandler = () => {
const { sessionData, providerPropsData } = useContext(StateContext);
+ const { closePaymentSheet } = useContext(KomojuContext);
+
const SessionData = sessionData as CreatePaymentFuncType;
+ useEffect(() => {
+ () => {
+ if (timeoutId) {
+ clearTimeout(timeoutId);
+ }
+ };
+ }, []);
+
const {
- startLoading,
+ startPaymentLoading,
stopLoading,
onPaymentAwaiting,
onPaymentFailed,
@@ -30,7 +42,7 @@ const useSessionPayHandler = () => {
paymentDetails,
}: sessionPayProps) => {
// Start of the payment handling method
- startLoading();
+ startPaymentLoading();
// initiate payment for the session ID with payment details
const response = await payForSession({
@@ -50,6 +62,9 @@ const useSessionPayHandler = () => {
response?.customer?.resource === PaymentMode.Customer
) {
onPaymentSuccess();
+ if (response?.customer?.resource === PaymentMode.Customer) {
+ timeoutId = setTimeout(() => closePaymentSheet(), 2000);
+ }
} else if (response?.payment?.payment_details?.instructions_url) {
openURL(response?.payment?.payment_details?.instructions_url);
onPaymentAwaiting();
diff --git a/src/hooks/useThreeDSecureHandler.tsx b/src/hooks/useThreeDSecureHandler.tsx
index 9515119..de8e8ce 100644
--- a/src/hooks/useThreeDSecureHandler.tsx
+++ b/src/hooks/useThreeDSecureHandler.tsx
@@ -15,11 +15,12 @@ const useThreeDSecureHandler = () => {
const dispatch = useContext(DispatchContext);
const { sessionData, providerPropsData } = useContext(StateContext);
- const { startLoading, stopLoading, onPaymentFailed } = useMainStateUtils();
+ const { startPaymentLoading, stopLoading, onPaymentFailed } =
+ useMainStateUtils();
const SessionData = sessionData as sessionDataType;
const threeDSecurePayment = async (paymentDetails: CardDetailsType) => {
- startLoading();
+ startPaymentLoading();
const { month, year } = getMonthYearFromExpiry(
paymentDetails?.cardExpiredDate || ""
diff --git a/src/theme/defaultColorTheme.ts b/src/theme/defaultColorTheme.ts
index adfc3de..59d3dc8 100644
--- a/src/theme/defaultColorTheme.ts
+++ b/src/theme/defaultColorTheme.ts
@@ -1,6 +1,7 @@
const darkTheme = {
PRIMARY_COLOR: "#0071D7",
BACKGROUND_COLOR: "#1E1E1E",
+ SUCCESS_COLOR: "#3CC239",
ERROR: "#fc4747",
TEXT_COLOR: "#fff",
INPUT_BACKGROUND: "#2C2C2C",
@@ -18,6 +19,7 @@ const darkTheme = {
const lightTheme = {
PRIMARY_COLOR: "#0B82EE",
BACKGROUND_COLOR: "#FFFFFF",
+ SUCCESS_COLOR: "#3CC239",
ERROR: "#fc5d5d",
TEXT_COLOR: "#172E44",
INPUT_BACKGROUND: "#FFFFFF",
diff --git a/src/util/helpers.ts b/src/util/helpers.ts
index b8235bb..5d6c5c5 100644
--- a/src/util/helpers.ts
+++ b/src/util/helpers.ts
@@ -6,6 +6,7 @@ import {
CardTypes,
CurrencySign,
CurrencyTypes,
+ initialState,
KonbiniType,
PaymentType,
sessionShowPaymentMethodType,
@@ -222,3 +223,8 @@ export function fromUserFriendlyTheme(
return acc;
}, {} as Partial);
}
+
+export const getInitialStateWithoutProviderPropsData = () => {
+ const { providerPropsData, ...stateWithoutProviderProps } = initialState;
+ return stateWithoutProviderProps;
+};
diff --git a/src/util/types.ts b/src/util/types.ts
index c9ba9de..449b5a1 100644
--- a/src/util/types.ts
+++ b/src/util/types.ts
@@ -240,6 +240,11 @@ export type brandType = {
icon: string;
};
+export type LoadingTypes = {
+ app?: boolean;
+ payment?: boolean;
+};
+
export type sessionDataType = {
/**
* Amount for the payment
@@ -267,7 +272,7 @@ export type State = KonbiniDetailsType & {
/**
* Global loading state. to display loading animation over sdk and disable buttons.
*/
- loading: boolean;
+ loading: LoadingTypes;
/**
* All credit card related data
*/
@@ -315,7 +320,10 @@ export type sessionPayProps = {
// Define the initial state
export const initialState: State = {
paymentType: PaymentType.CREDIT,
- loading: false,
+ loading: {
+ app: false,
+ payment: false,
+ },
/** credit card payment related states start */
cardData: {
@@ -363,6 +371,7 @@ export type ActionType = { type: string; payload: State[keyof State] };
export interface ThemeSchemeType {
PRIMARY_COLOR: string;
BACKGROUND_COLOR: string;
+ SUCCESS_COLOR: string;
ERROR: string;
TEXT_COLOR: string;
INPUT_BACKGROUND: string;