Skip to content
Last updated

Card payments are handled by the Banxa React Native SDK, which presents Primer's card input drawer — a bottom sheet that overlays your app. The user enters their card details, and Primer handles PCI-compliant card capture and 3D Secure authentication. Your app never touches raw card data.

This guide covers payment execution and 3DS SDK installation only. Identity creation, KYC, eligibility, and React Native SDK installation are covered in the Integration Guide — start there if you haven't set those up yet. This guide picks up from paymentReady: true.


Execute payment

After your backend confirms paymentReady: true from eligibility, call createOrderAndShowPrimerCheckout with paymentMethod: 'card':

import {
  isPrimerCheckoutWebViewResult,
} from '@banxa-official/react-native-sdk';

const result = await banxa.buy.createOrderAndShowPrimerCheckout(orderRequest, {
  paymentMethod: 'card',
  callbacks: {
    onCheckoutComplete: () => {
      // Payment confirmed — transaction is in progress
    },
    onError: (error) => {
      // Payment failed — see error handling below
    },
    onDismiss: () => {
      // User dismissed the card drawer without completing
    },
  },
});

if (isPrimerCheckoutWebViewResult(result)) {
  // SDK fell back to hosted WebView
  render(<CheckoutWebView {...result.webViewProps} />);
}

The orderRequest must include externalCustomerId — pass the same identityReference you used for eligibility.


Install the 3DS SDK

Card payments require Primer's 3DS SDK in addition to the Banxa React Native SDK. Install this before invoking createOrderAndShowPrimerCheckout — the card payment sheet will not initialise without it.

The version you install must match the version of @primer-io/react-native in your project — see the interoperability matrix below.

iOS

Adding 3D Secure on iOS requires the Primer3DS pod. The configuration depends on how your project links CocoaPods. Run bundle exec pod install in your ios folder to check:

Static library

The default for most React Native apps.

Add pod 'Primer3DS' to your target and configure the post_install block:

target 'MyApp' do
  # ... other CocoaPods config ...
  pod 'Primer3DS'
  post_install do |installer|
    installer.pods_project.targets.each do |target|
      if target.name == "primer-io-react-native" || target.name == "PrimerSDK"
        target.build_configurations.each do |config|
          config.build_settings['FRAMEWORK_SEARCH_PATHS'] = "$(inherited) ${PODS_CONFIGURATION_BUILD_DIR}/Primer3DS"
          config.build_settings['LIBRARY_SEARCH_PATHS'] = "$(inherited) ${PODS_CONFIGURATION_BUILD_DIR}/Primer3DS"
          config.build_settings['SWIFT_INCLUDE_PATHS'] = "$(inherited) ${PODS_CONFIGURATION_BUILD_DIR}/Primer3DS"
          # Use this line for static libraries:
          config.build_settings['OTHER_SWIFT_FLAGS'] = '$(inherited) -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/Primer3DS/Primer3DS.framework/Modules/Primer3DS.modulemap"'
          config.build_settings['OTHER_LDFLAGS'] = '$(inherited) -weak_library -l"Primer3DS"'
        end
      end
    end
  end
end

Run bundle exec pod install to finish.

Dynamic framework

Add pod 'Primer3DS' to your target — no additional config needed:

target 'MyApp' do
  # ... other RN iOS config ...
  pod 'Primer3DS'
end

Run bundle exec pod install to finish. The 3DS pod is detected automatically at runtime.

Static framework

Static frameworks require the cocoapods-pod-linkage plugin to allow the 3DS pod to be loaded dynamically at runtime.

Add to your Gemfile:

gem 'cocoapods-pod-linkage'

Run bundle install, then add the plugin to your Podfile:

plugin 'cocoapods-pod-linkage'

Configure your target to link PrimerSDK and Primer3DS dynamically:

use_frameworks! :linkage => :static

target 'MyApp' do
  # ... other CocoaPods config ...
  pod 'PrimerSDK', :linkage => :dynamic
  pod 'Primer3DS', :linkage => :dynamic

  post_install do |installer|
    installer.pods_project.targets.each do |target|
      if target.name == "primer-io-react-native" || target.name == "PrimerSDK"
        target.build_configurations.each do |config|
          config.build_settings['FRAMEWORK_SEARCH_PATHS'] = "$(inherited) ${PODS_CONFIGURATION_BUILD_DIR}/Primer3DS"
          config.build_settings['LIBRARY_SEARCH_PATHS'] = "$(inherited) ${PODS_CONFIGURATION_BUILD_DIR}/Primer3DS"
          config.build_settings['SWIFT_INCLUDE_PATHS'] = "$(inherited) ${PODS_CONFIGURATION_BUILD_DIR}/Primer3DS"
          config.build_settings['OTHER_LDFLAGS'] = '$(inherited) -framework Primer3DS'
        end
      end
    end
  end
end

Run bundle exec pod install to finish.


Android

Add the io.primer:3ds-android library to the dependencies block in your app's build.gradle. Replace {version} with the correct version from the interoperability matrix:

dependencies {
  // ... other dependencies ...
  implementation "io.primer:3ds-android:{version}"
}

Interoperability matrix

The version of io.primer:3ds-android and Primer3DS you install must match the version of @primer-io/react-native in your project.

@primer-io/react-nativeio.primer:3ds-androidPrimer3DS (iOS)
>= 2.42.01.8.0>= 2.7.0
2.41.0 – 2.42.01.7.02.6.0 – 2.7.0
2.35.0 – 2.40.01.6.22.4.2 – 2.5.x
2.34.1 – 2.35.01.6.22.3.0 – 2.4.2
2.27.2 – 2.34.01.5.02.3.0 – 2.4.2
2.24.0 – 2.27.11.4.32.3.0 – 2.4.2
2.21.0 – 2.23.01.4.22.3.0 – 2.4.2
2.20.01.4.02.1.0 – 2.2.1
2.19.0 – 2.19.21.3.02.1.0 – 2.2.1
2.17.2 – 2.18.01.2.02.0.0 – 2.0.2
2.16.0 – 2.17.11.1.22.0.0 – 2.0.2
2.15.0 – 2.15.11.1.12.0.0 – 2.0.2

OOB redirects (optional)

From 3DS protocol version 2.2.0, you can enable automatic redirect back to your app after an out-of-band (OOB) authentication challenge completes in another app. Without this, users must return to your app manually — which adds friction at the payment step.

To enable OOB redirects, pass threeDsAppRequestorUrl in your Primer settings. The iOS value must be an iOS Universal Link and the Android value must be an Android App Link.

import { Banxa } from '@banxa-official/react-native-sdk';

const banxa = new Banxa({
  apiKey: 'YOUR_API_KEY',
  partner: 'your-partner-id',
  environment: 'production',
  primerSettings: {
    paymentMethodOptions: {
      threeDsOptions: {
        iOS: {
          threeDsAppRequestorUrl: 'https://yourdomain.com/3ds',
        },
        android: {
          threeDsAppRequestorUrl: 'https://yourdomain.com/3ds',
        },
      },
    },
  },
});

See Apple's Universal Links and Google's Android App Links documentation for setup steps.


Testing

Use Banxa's sandbox environment during development:

const banxa = new Banxa({
  environment: 'sandbox',
  // ...
});

If your app is not installed from a trusted source — a debug build, not installed from the store, or running on an emulator — 3DS initialisation may fail due to security checks. Disable the sanity check in development:

const banxa = new Banxa({
  // ...
  primerSettings: {
    debugOptions: {
      is3DSSanityCheckEnabled: false,
    },
  },
});
Development only

Set is3DSSanityCheckEnabled: false only in development builds. Do not ship this to production.

For test cards and 3DS challenge scenarios, see Primer's 3DS Testing Guide.


Going live

Before releasing to production:

  • Remove is3DSSanityCheckEnabled: false from your configuration
  • Switch to production credentials in the SDK
  • Confirm with your Banxa integration contact that your account is provisioned for production card payments