import { EvervaultProvider, EvervaultProviderProps, ThreeDSecure, useThreeDSecure, } from '@evervault/react-native'; import React from 'react'; import { Modal } from 'react-native'; import { Create3DSecureSessionCallbacks, Create3DSSessionPayload, } from './lib/types'; import { create3DSSessionInDuffelAPI } from './lib/create3DSSessionInDuffelAPI'; import { refresh3DSSessionInDuffelAPI } from './lib/refreshThreeDSecureSession'; import { getLiveModeFromClientKey } from './lib/getLiveModeFromClientKey'; import { getDefaultProviderIds } from './lib/getDefaultProviderIds'; ///////////// // CONTEXT // ///////////// type Create3DSecureSession = ( payload: Create3DSSessionPayload, { onSuccess, onFailure, onError }: Create3DSecureSessionCallbacks, overrideDuffelAPIUrl?: string ) => Promise; const DuffelThreeDSecureContext = React.createContext<{ create3DSecureSession: Create3DSecureSession; }>({ create3DSecureSession: async () => {} }); ///////////////////// // COMPONENT PROPS // ///////////////////// export interface DuffelThreeDSecureProviderProps { children: React.ReactNode; /** * The provider IDs for the Evervault provider. * This is used to identify the Evervault team and app that will be used for 3DS. * A default value exists, but you may override them with this prop. */ providerIds?: Pick; clientKey: string; } interface DuffelThreeDSecureProviderWithEvervaultProps { children: React.ReactNode; clientKey: string; } ////////////////////// // PUBLIC INTERFACE // ////////////////////// export function DuffelThreeDSecureProvider({ children, clientKey, providerIds, }: DuffelThreeDSecureProviderProps) { const liveMode = getLiveModeFromClientKey(clientKey); let { teamId, appId } = getDefaultProviderIds(liveMode); if (providerIds) { teamId = providerIds.teamId; appId = providerIds.appId; } return ( {children} ); } export function useCreate3DSecureSession() { if (!React.useContext(DuffelThreeDSecureContext)) { throw new Error( 'useCreate3DSecureSession must be used within a DuffelThreeDSecureProvider' ); } return React.useContext(DuffelThreeDSecureContext); } export type UseCreate3DSecureSessionHook = typeof useCreate3DSecureSession; //////////////////////////////////////// // `create3DSecureSession` DEFINITION // //////////////////////////////////////// function DuffelThreeDSecureProviderWithEvervault({ children, clientKey, }: DuffelThreeDSecureProviderWithEvervaultProps) { const tds = useThreeDSecure(); const create3DSecureSession = async ( payload: Create3DSSessionPayload, { onSuccess, onFailure, onError }: Create3DSecureSessionCallbacks, // overrideDuffelAPIUrl should only be used for Duffel internal development. _overrideDuffelAPIUrl?: string ) => { if (_overrideDuffelAPIUrl) { console.warn( 'overrideDuffelAPIUrl should not be used in production. It is only intended for Duffel internal development.' ); } // Create a 3DS session on your backend create3DSSessionInDuffelAPI(clientKey, payload, _overrideDuffelAPIUrl) .then((session) => { if (session.status === 'ready_for_payment') { onSuccess(session); return; } if (session.client_id === null) { onError(new Error('Failed to create 3DS session')); return; } // when it goes well, you can call Duffel to update the 3DS session tds.start(session.client_id, { onFailure, onError, onSuccess: () => { refresh3DSSessionInDuffelAPI( clientKey, session.id, _overrideDuffelAPIUrl ) .then(onSuccess) .catch(onFailure); }, }); }) .catch((error) => { onError(error); }); }; return ( {children} tds.cancel()}> ); }