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-components and 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 clientKey created 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 DuffelThreeDSecureProvider as high as the screens that will initiate payments. Pass the clientKey you fetched from your backend. Under the hood the provider exposes a create3DSecureSession function 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 an exception flag (or null, if none are present) if Duffel cleared you for a Secure Corporate Payment exemption. - Callback handlers for onSuccess, onFailure, and onError. 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 status so you can decide what to do next. A ready_for_payment status means you can include three_d_secure_session_id when creating the Duffel order; client_action_required, failed, or expired should keep the traveller on the payment screen and allow another attempt.

  • Example flow In practice you’ll replace completeOrderOnBackend with the API call that books the flight once the traveller has passed 3DS, and you’ll wire services to 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, and react-native-webview as 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-111 as 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_payment and tied to the same card/resource. If you see failed or expired, prompt the traveller to try again, and only allow retries if the status remains actionable.