From 3143a091346c0ac0a322bcfc9a7daa7efb2f6ef7 Mon Sep 17 00:00:00 2001 From: chathurapathiranage Date: Wed, 4 Sep 2024 19:27:11 +0530 Subject: [PATCH] improve the readablity of the Main State --- src/context/MainStateProvider.tsx | 372 ++++++----------------------- src/hooks/useBackgroundHandler.tsx | 94 ++++++++ src/hooks/useDeepLinkHandler.tsx | 89 +++++++ src/hooks/useMainStateUtils.tsx | 134 +++++++++++ src/hooks/usePaymentHandler.tsx | 124 ++++++++++ 5 files changed, 508 insertions(+), 305 deletions(-) create mode 100644 src/hooks/useBackgroundHandler.tsx create mode 100644 src/hooks/useDeepLinkHandler.tsx create mode 100644 src/hooks/useMainStateUtils.tsx create mode 100644 src/hooks/usePaymentHandler.tsx diff --git a/src/context/MainStateProvider.tsx b/src/context/MainStateProvider.tsx index 94c134d..8b6289a 100644 --- a/src/context/MainStateProvider.tsx +++ b/src/context/MainStateProvider.tsx @@ -1,37 +1,26 @@ import { useCallback, useContext, - useEffect, useMemo, useRef, useState, } from 'react'; -import { Alert, AppState, AppStateStatus, Linking } from 'react-native'; - -import i18next from 'i18next'; - import PaymentModal from '../components/PaymentModal'; import Sheet, { SheetRefProps } from '../components/Sheet'; -import payForSession from '../services/payForSessionService'; -import sessionShow from '../services/sessionShow'; - -import { parsePaymentMethods } from '../util/helpers'; import { CreatePaymentFuncType, initialState, InitPrams, - KomojuProviderIprops, - PaymentStatuses, - ResponseScreenStatuses, - sessionPayProps, - TokenResponseStatuses -} from '../util/types'; -import { validateSessionResponse } from '../util/validator'; + KomojuProviderIprops} from '../util/types'; import '../assets/languages/i18n'; import { Actions, DispatchContext, KomojuContext } from './state'; +import useBackgroundHandler from '../hooks/useBackgroundHandler'; +import useDeepLinkHandler from '../hooks/useDeepLinkHandler'; +import usePaymentHandler from '../hooks/usePaymentHandler'; +import useMainStateUtils from '../hooks/useMainStateUtils'; export const MainStateProvider = (props: KomojuProviderIprops) => { const dispatch = useContext(DispatchContext); @@ -45,293 +34,65 @@ export const MainStateProvider = (props: KomojuProviderIprops) => { // ref to hold client provided session Id const sessionIdRef = useRef(''); - useEffect(() => { - // Add event listener for deep links - const subscription = Linking.addEventListener( - 'url', - handleDeepLinkStateChange - ); - - // Add event listener for deep links - const windowChangeListener = AppState.addEventListener( - "change", - handleBackgroundStateChange - ); - - return () => { - subscription.remove(); - windowChangeListener.remove(); - }; - }, [props]); - - // This callback method is used to check the background state - const handleBackgroundStateChange = async (status: AppStateStatus) => { - startLoading(); - - 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 - 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?.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 - ) { - onPaymentCancelled(); - } else if (sessionResponse?.expired) { - onSessionExpired(); - } else if ( - sessionResponse?.status === PaymentStatuses.ERROR || - sessionResponse?.payment?.status === PaymentStatuses.ERROR || - sessionResponse?.secure_token?.verification_status === - TokenResponseStatuses.ERROR - ) { - onPaymentFailed(); - } - } - - // after all api calls are done stopping the loading indicator - stopLoading(); - }; - - const openPaymentSheet = () => { - if (props?.useBottomSheet) { - sheetRef?.current?.open(); - } else { - setModalVisible(true); - } - }; - - const closePaymentSheet = () => { - // TODO: Fix this type error - // @ts-expect-error - Object is possibly 'null'. - sheetRef?.current?.close(false); - setModalVisible(false); - }; - - const resetGlobalStates = () => - dispatch({ - type: Actions.RESET_STATES, - payload: initialState, - }); - - // when payment is success global state is rest and invoking the success screen - const onPaymentSuccess = () => { - resetGlobalStates(); - dispatch({ - type: Actions.SET_PAYMENT_STATE, - payload: ResponseScreenStatuses.SUCCESS, - }); - }; - - // when payment is failed invoking the error screen - const onPaymentFailed = () => - dispatch({ - type: Actions.SET_PAYMENT_STATE, - payload: ResponseScreenStatuses.FAILED, - }); - - // when payment is cancelled by the user - const onPaymentCancelled = () => { - resetGlobalStates(); - dispatch({ - type: Actions.SET_PAYMENT_STATE, - payload: ResponseScreenStatuses.CANCELLED, - }); - }; - - // when payment is completed but awaiting payment - const onPaymentAwaiting = () => { - resetGlobalStates(); - dispatch({ - type: Actions.SET_PAYMENT_STATE, - payload: ResponseScreenStatuses.COMPLETE, - }); - }; - - // 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 = { - publishableKey: props?.publishableKey, - sessionId: sessionIdRef.current, - }; - - // fetch session status to check if the payment is completed - const sessionResponse = await sessionShow(sessionShowPayload); - // invoking client provided onDismiss callback - // TODO: Fix this type error - // @ts-expect-error - Argument of type 'PaymentSessionResponse' is not assignable to parameter of type 'string'. - onDismissCallback.current(sessionResponse); - } - }; - - // showing overlay loading indicator disabling all interactions - const startLoading = () => - dispatch({ - type: Actions.SET_LOADING, - payload: true, - }); - - // Hiding overlay loading indicator - const stopLoading = () => - dispatch({ - type: Actions.SET_LOADING, - payload: false, - }); - - const openURL = async (url: string) => { - try { - await Linking.openURL(url); - } catch (err) { - Alert.alert('Redirection not working. Please contact support!'); - } - }; - - // validating the provided session ID by user before proceeding - const validateSession = async (sessionId: string) => { - startLoading(); - - // Fetching session data from given session ID - const sessionData = await sessionShow({ - sessionId, - publishableKey: props.publishableKey, - }); - - // validating the session data and closing the payment gateway if data is not valid - if (validateSessionResponse(sessionData)) { - closePaymentSheet(); - Alert.alert('Error', 'Invalid Session'); - } else { - // if explicitly language is not set. set to the localization from session - if (props?.language) { - i18next.changeLanguage(props?.language); - } else { - i18next.changeLanguage(sessionData?.default_locale); - } - - // if session is valid setting amount, currency type at global store for future use - dispatch({ type: Actions.SET_AMOUNT, payload: sessionData?.amount }); - dispatch({ type: Actions.SET_CURRENCY, payload: sessionData?.currency }); - - // if user provided explicitly payments methods via props, will give priority to that over session payment methods - const paymentMethods = parsePaymentMethods( - props?.paymentMethods, - sessionData?.payment_methods - ); - - // setting the payment methods in global state - dispatch({ - type: Actions.SET_PAYMENT_METHODS, - payload: paymentMethods, - }); - // setting the current selected payment method as the first payment method on the list - dispatch({ - type: Actions.SET_PAYMENT_OPTION, - payload: paymentMethods ? paymentMethods[0]?.type : '', - }); - } - stopLoading(); - }; - - // This callback method is used to intercept Web View urls - const handleDeepLinkStateChange = async () => { - startLoading(); - - // 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?.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?.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) { - onPaymentCancelled(); - } else { - onPaymentFailed(); - } - - // after all api calls are done stopping the loading indicator - stopLoading(); - }; - - // Session pay callback. this method handles all the payment logic and APIs - const sessionPay = ({ sessionId }: CreatePaymentFuncType) => { - return async ({ paymentType, paymentDetails }: sessionPayProps) => { - // Start of the payment handling method - startLoading(); - - // initiate payment for the session ID with payment details - const response = await payForSession({ - paymentType, - sessionId, - publishableKey: props.publishableKey, - paymentDetails, - }); - - stopLoading(); - - if (response?.status === PaymentStatuses.PENDING) { - openURL(response.redirect_url); - } else if (response?.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); - onPaymentAwaiting(); - } - } else { - onPaymentFailed(); - } - }; - }; + // Get all the util functions that needs to function the Main State + const { + startLoading, + stopLoading, + onPaymentAwaiting, + onPaymentCancelled, + onSessionExpired, + onPaymentFailed, + onPaymentSuccess, + closePaymentSheet, + onUserCancel, + openPaymentSheet, + resetGlobalStates + } = useMainStateUtils({ + props: props, + sheetRef: sheetRef, + sessionIdRef: sessionIdRef, + toggleUIVisibility: (value: boolean) => setModalVisible(value), + initialState: initialState, + onDismissCallback: onDismissCallback, + }) + + // Handle events when module goes foreground + useBackgroundHandler({ + props: props, + startLoading: startLoading, + stopLoading: stopLoading, + sessionIdRef: sessionIdRef, + onCompleteCallback: onCompleteCallback, + onPaymentAwaiting: onPaymentAwaiting, + onPaymentCancelled: onPaymentCancelled, + onSessionExpired: onSessionExpired, + onPaymentFailed: onPaymentFailed, + onPaymentSuccess: onPaymentSuccess + }) + + // Handle deep-links of the module + useDeepLinkHandler({ + props: props, + startLoading: startLoading, + stopLoading: stopLoading, + sessionIdRef: sessionIdRef, + onCompleteCallback: onCompleteCallback, + onPaymentAwaiting: onPaymentAwaiting, + onPaymentCancelled: onPaymentCancelled, + onPaymentFailed: onPaymentFailed, + onPaymentSuccess: onPaymentSuccess + }) + + // Handle validations of the session and pay for session + const { sessionPay, validateSession } = usePaymentHandler({ + props: props, + startLoading: startLoading, + stopLoading: stopLoading, + onPaymentAwaiting: onPaymentAwaiting, + onPaymentFailed: onPaymentFailed, + onPaymentSuccess: onPaymentSuccess, + closePaymentSheet: closePaymentSheet + }) const createPayment = useCallback( ({ sessionId, onComplete, onDismiss }: CreatePaymentFuncType) => { @@ -366,9 +127,10 @@ 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) => { }, []); + // Conditionally rendering the payment ui const renderPaymentUI = useMemo(() => { const UI = props?.useBottomSheet ? ( diff --git a/src/hooks/useBackgroundHandler.tsx b/src/hooks/useBackgroundHandler.tsx new file mode 100644 index 0000000..f115686 --- /dev/null +++ b/src/hooks/useBackgroundHandler.tsx @@ -0,0 +1,94 @@ +import React, { MutableRefObject, useEffect } from 'react' +import { View, Text, AppState, AppStateStatus } from 'react-native' +import { KomojuProviderIprops, PaymentStatuses, TokenResponseStatuses } from '../util/types'; +import sessionShow from '../services/sessionShow'; + + +type Props = { + props: KomojuProviderIprops, + startLoading: () => void + stopLoading: () => void + sessionIdRef: MutableRefObject + onCompleteCallback: MutableRefObject + onPaymentSuccess: () => void + onPaymentAwaiting: () => void + onPaymentCancelled: () => void + onSessionExpired: () => void + onPaymentFailed: () => void +} + +const useBackgroundHandler = ({ + props, + startLoading, + stopLoading, + sessionIdRef, + onCompleteCallback, + onPaymentAwaiting, + onPaymentCancelled, + onPaymentFailed, + onSessionExpired, + onPaymentSuccess +}: Props) => { + + useEffect(() => { + // Add event listener for deep links + const windowChangeListener = AppState.addEventListener( + "change", + handleBackgroundStateChange + ); + + return () => { + windowChangeListener.remove(); + }; + }, [props]); + + const handleBackgroundStateChange = async (status: AppStateStatus) => { + startLoading(); + + 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 + 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?.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 + ) { + onPaymentCancelled(); + } else if (sessionResponse?.expired) { + onSessionExpired(); + } else if ( + sessionResponse?.status === PaymentStatuses.ERROR || + sessionResponse?.payment?.status === PaymentStatuses.ERROR || + sessionResponse?.secure_token?.verification_status === + TokenResponseStatuses.ERROR + ) { + onPaymentFailed(); + } + } + + // after all api calls are done stopping the loading indicator + stopLoading(); + }; + + return undefined; +} + +export default useBackgroundHandler \ No newline at end of file diff --git a/src/hooks/useDeepLinkHandler.tsx b/src/hooks/useDeepLinkHandler.tsx new file mode 100644 index 0000000..327ac57 --- /dev/null +++ b/src/hooks/useDeepLinkHandler.tsx @@ -0,0 +1,89 @@ +import { Linking } from 'react-native' +import { MutableRefObject, useEffect } from 'react' +import { KomojuProviderIprops, PaymentStatuses, TokenResponseStatuses } from '../util/types' +import sessionShow from '../services/sessionShow' + +type Props = { + props: KomojuProviderIprops, + startLoading: () => void + stopLoading: () => void + sessionIdRef: MutableRefObject + onCompleteCallback: MutableRefObject + onPaymentSuccess: () => void + onPaymentAwaiting: () => void + onPaymentCancelled: () => void + onPaymentFailed: () => void +} + +const useDeepLinkHandler = ({ + props, + startLoading, + stopLoading, + sessionIdRef, + onCompleteCallback, + onPaymentAwaiting, + onPaymentCancelled, + onPaymentFailed, + onPaymentSuccess +}: Props) => { + + useEffect(() => { + // Add event listener for deep links + const subscription = Linking.addEventListener( + 'url', + handleDeepLinkStateChange + ); + + return () => { + subscription.remove(); + }; + }, [props]); + + const handleDeepLinkStateChange = async () => { + startLoading(); + + // 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?.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?.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) { + onPaymentCancelled(); + } else { + onPaymentFailed(); + } + + // after all api calls are done stopping the loading indicator + stopLoading(); + }; + + return undefined; +} + +export default useDeepLinkHandler \ No newline at end of file diff --git a/src/hooks/useMainStateUtils.tsx b/src/hooks/useMainStateUtils.tsx new file mode 100644 index 0000000..c01457c --- /dev/null +++ b/src/hooks/useMainStateUtils.tsx @@ -0,0 +1,134 @@ +import { MutableRefObject, RefObject, useContext } from 'react' + +import { Actions, DispatchContext } from '../context/state'; +import { SheetRefProps } from '../components/Sheet'; +import { KomojuProviderIprops, ResponseScreenStatuses, State } from '../util/types'; +import sessionShow from '../services/sessionShow'; + +type Props = { + props: KomojuProviderIprops, + sheetRef: RefObject, + sessionIdRef: MutableRefObject + toggleUIVisibility: (value: boolean) => void, + initialState: State, + onDismissCallback: MutableRefObject, +} + +const useMainStateUtils = ({ + props, + sheetRef, + sessionIdRef, + toggleUIVisibility, + initialState, + onDismissCallback + +}: Props) => { + const dispatch = useContext(DispatchContext); + const openPaymentSheet = () => { + if (props?.useBottomSheet) { + sheetRef?.current?.open(); + } else { + toggleUIVisibility(true); + } + }; + + const closePaymentSheet = () => { + // TODO: Fix this type error + // @ts-expect-error - Object is possibly 'null'. + sheetRef?.current?.close(false); + toggleUIVisibility(false); + }; + + const resetGlobalStates = () => + dispatch({ + type: Actions.RESET_STATES, + payload: initialState, + }); + + // when payment is success global state is rest and invoking the success screen + const onPaymentSuccess = () => { + resetGlobalStates(); + dispatch({ + type: Actions.SET_PAYMENT_STATE, + payload: ResponseScreenStatuses.SUCCESS, + }); + }; + + // when payment is failed invoking the error screen + const onPaymentFailed = () => + dispatch({ + type: Actions.SET_PAYMENT_STATE, + payload: ResponseScreenStatuses.FAILED, + }); + + // when payment is cancelled by the user + const onPaymentCancelled = () => { + resetGlobalStates(); + dispatch({ + type: Actions.SET_PAYMENT_STATE, + payload: ResponseScreenStatuses.CANCELLED, + }); + }; + + // when payment is completed but awaiting payment + const onPaymentAwaiting = () => { + resetGlobalStates(); + dispatch({ + type: Actions.SET_PAYMENT_STATE, + payload: ResponseScreenStatuses.COMPLETE, + }); + }; + + // 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 = { + publishableKey: props?.publishableKey, + sessionId: sessionIdRef.current, + }; + + // fetch session status to check if the payment is completed + const sessionResponse = await sessionShow(sessionShowPayload); + // invoking client provided onDismiss callback + // TODO: Fix this type error + // @ts-expect-error - Argument of type 'PaymentSessionResponse' is not assignable to parameter of type 'string'. + onDismissCallback.current(sessionResponse); + } + }; + + // showing overlay loading indicator disabling all interactions + const startLoading = () => + dispatch({ + type: Actions.SET_LOADING, + payload: true, + }); + + // Hiding overlay loading indicator + const stopLoading = () => + dispatch({ + type: Actions.SET_LOADING, + payload: false, + }); + + return { + openPaymentSheet, + closePaymentSheet, + onPaymentSuccess, + onPaymentFailed, + onPaymentCancelled, + onPaymentAwaiting, + onSessionExpired, + onUserCancel, + startLoading, + stopLoading, + resetGlobalStates + } +} + +export default useMainStateUtils \ No newline at end of file diff --git a/src/hooks/usePaymentHandler.tsx b/src/hooks/usePaymentHandler.tsx new file mode 100644 index 0000000..368235f --- /dev/null +++ b/src/hooks/usePaymentHandler.tsx @@ -0,0 +1,124 @@ +import { useContext } from 'react' +import { Alert, Linking } from 'react-native' +import i18next from 'i18next'; + +import { CreatePaymentFuncType, KomojuProviderIprops, PaymentStatuses, sessionPayProps, TokenResponseStatuses } from '../util/types' +import sessionShow from '../services/sessionShow' +import { validateSessionResponse } from '../util/validator' +import { Actions, DispatchContext } from '../context/state'; +import { parsePaymentMethods } from '../util/helpers'; +import payForSession from '../services/payForSessionService'; + +type Props = { + props: KomojuProviderIprops, + startLoading: () => void + stopLoading: () => void + onPaymentSuccess: () => void + onPaymentAwaiting: () => void + onPaymentFailed: () => void + closePaymentSheet: () => void +} + +const usePaymentHandler = ({ + props, + startLoading, + stopLoading, + onPaymentAwaiting, + onPaymentFailed, + onPaymentSuccess, + closePaymentSheet +}: Props) => { + const dispatch = useContext(DispatchContext); + + // validating the provided session ID by user before proceeding + const validateSession = async (sessionId: string) => { + startLoading(); + + // Fetching session data from given session ID + const sessionData = await sessionShow({ + sessionId, + publishableKey: props.publishableKey, + }); + + // validating the session data and closing the payment gateway if data is not valid + if (validateSessionResponse(sessionData)) { + closePaymentSheet(); + Alert.alert('Error', 'Invalid Session'); + } else { + // if explicitly language is not set. set to the localization from session + if (props?.language) { + i18next.changeLanguage(props?.language); + } else { + i18next.changeLanguage(sessionData?.default_locale); + } + + // if session is valid setting amount, currency type at global store for future use + dispatch({ type: Actions.SET_AMOUNT, payload: sessionData?.amount }); + dispatch({ type: Actions.SET_CURRENCY, payload: sessionData?.currency }); + + // if user provided explicitly payments methods via props, will give priority to that over session payment methods + const paymentMethods = parsePaymentMethods( + props?.paymentMethods, + sessionData?.payment_methods + ); + + // setting the payment methods in global state + dispatch({ + type: Actions.SET_PAYMENT_METHODS, + payload: paymentMethods, + }); + // setting the current selected payment method as the first payment method on the list + dispatch({ + type: Actions.SET_PAYMENT_OPTION, + payload: paymentMethods ? paymentMethods[0]?.type : '', + }); + } + stopLoading(); + }; + + // Session pay callback. this method handles all the payment logic and APIs + const sessionPay = ({ sessionId }: CreatePaymentFuncType) => { + return async ({ paymentType, paymentDetails }: sessionPayProps) => { + // Start of the payment handling method + startLoading(); + + // initiate payment for the session ID with payment details + const response = await payForSession({ + paymentType, + sessionId, + publishableKey: props.publishableKey, + paymentDetails, + }); + + stopLoading(); + + if (response?.status === PaymentStatuses.PENDING) { + openURL(response.redirect_url); + } else if (response?.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); + onPaymentAwaiting(); + } + } else { + onPaymentFailed(); + } + }; + }; + + const openURL = async (url: string) => { + try { + await Linking.openURL(url); + } catch (err) { + Alert.alert('Redirection not working. Please contact support!'); + } + }; + + return { + sessionPay, + validateSession + } +} + +export default usePaymentHandler \ No newline at end of file