Using 3DS hook in React Native
Requirements
- Start from the Pay with customer cards flow so you already know how to capture a card with
DuffelCardForm, obtain a client component key, and pass the resulting card ID into your booking logic. That guide walks through capturing the card, kicking off 3DS, and using the response to create an order, so treat it as the prerequisite for everything below: Paying with customer cards. See the excerpt in References for the exact steps (capture card → initiate 3DS → create order). - Install
@duffel/react-native-componentsand keep an eye on the npm package page for the most recent release notes and version numbers. - Confirm your environment can satisfy the component’s peer dependencies (
react,react-native,react-native-svg,react-native-webview, and@duffel/api) so the provider can render on both iOS and Android builds. - Generate and securely provide the
clientKeycreated on your backend to whichever screen hosts the checkout flow; the provider cannot authenticate a session without it.
What 3DS Is And Why It Matters
3D Secure adds an issuer-backed challenge (or a frictionless confirmation) before the actual authorisation happens. Duffel’s implementation handles the heavy lifting of deciding when a challenge is necessary, guiding the traveller through issuer-branded screens, and handing you back a
three_d_secure_session_id that proves the cardholder authenticated the payment. Running every consumer card through 3DS dramatically reduces fraud exposure, can improve authorisation rates in regions where Strong Customer Authentication is mandatory, and shifts liability back to issuers when a challenge succeeds. For deeper reading and screenshots of the traveller experience, refer to the “Initiate 3DS Session” section at duffel.com/docs/guides/paying-with-customer-cards or the Card Form + 3DSecure Session guide linked from there.How To Add The Provider To Your App
- Wrap the checkout tree Place
DuffelThreeDSecureProvideras high as the screens that will initiate payments. Pass theclientKeyyou fetched from your backend. Under the hood the provider exposes acreate3DSecureSessionfunction via React context, so every descendant can invoke a 3DS challenge without prop drilling. - Build your payment action Call
useCreate3DSecureSession()where you collect final payment consent (for example, the<ConfirmPayWithCardButton />). The hook returns{ create3DSecureSession }, which you invoke with three arguments: - A payload that includes the card you stored earlier (card_id), the resource you’re paying for (resource_id, e.g. offer or order change), optional ancillary services, and anexceptionflag (or null, if none are present) if Duffel cleared you for a Secure Corporate Payment exemption. - Callback handlers foronSuccess,onFailure, andonError. Use these to drive your own UI (enabling retry buttons, showing confirmations, logging errors) and to trigger the backend call that actually creates the order/payment once the session is ready. - Handle outcomes The resolved session object exposes
statusso you can decide what to do next. Aready_for_paymentstatus means you can includethree_d_secure_session_idwhen creating the Duffel order;client_action_required,failed, orexpiredshould keep the traveller on the payment screen and allow another attempt. - Example flow In practice you’ll replace
completeOrderOnBackendwith the API call that books the flight once the traveller has passed 3DS, and you’ll wireservicesto whatever bags/seats the traveller selected.React
import {DuffelThreeDSecureProvider,useCreate3DSecureSession,} from '@duffel/react-native-components';function ConfirmPayWithCardButton({ cardId, offerId, services }) {const { create3DSecureSession } = useCreate3DSecureSession();const confirm = () =>create3DSecureSession({card_id: cardId,resource_id: offerId,services,exception: null,},{onSuccess: () => completeOrderOnBackend(),onFailure: (error) => showRetryToast(error),onError: (error) => notifyOps(error),});return <Button title="Confirm and pay" onPress={confirm} />;}export function CheckoutScreen(props) {return (<DuffelThreeDSecureProvider clientKey={props.clientKey}>{/* ...other checkout UI... */}<ConfirmPayWithCardButton {...props.paymentInputs} /></DuffelThreeDSecureProvider>);}```
FAQ
- How do I stay current on releases? Check npmjs.com/package/@duffel/react-native-components whenever you bump dependencies; it lists the latest version (2.7.0 at the time of writing) along with release notes and tarballs.
- Which dependencies must I install? The package lists
@duffel/api,react,react-native,react-native-svg, andreact-native-webviewas peer dependencies. Align their versions with the ones already used in your app to avoid Metro bundler warnings. - How do I test the flow without charging real cards? Use Duffel’s test mode and test suppliers (Duffel Airways, Duffel Hotel Group) plus the published card numbers to trigger “challenge required” vs “no challenge” paths, and enter
111-111as the verification code to simulate a successful challenge. This lets you cover success, failure, and cancellation states before going live. - When should I create a new 3DS session? Treat every booking attempt as needing a fresh session unless the previous session is still
ready_for_paymentand tied to the same card/resource. If you seefailedorexpired, prompt the traveller to try again, and only allow retries if the status remains actionable.