Accept payments via Revolut Merchant Card Form SDK - React Native

The Revolut Merchant Card Form SDK for React Native allows merchants to accept card payments within their cross-platform apps using a prebuilt card form. The form is presented as a modal overlay - no deep linking or app switching is required.

Pay with mobile card form

How it works

From an implementation perspective, the SDK works with the following components:

  1. Server-side: create an order and get a token, using the Merchant API: Create an order endpoint.
  2. Client-side: initialise the SDK with your Merchant Public API key.
  3. Client-side: call RevolutMerchantCardFormKit.pay(orderToken) to present the payment form. The SDK collects card details, handles 3D Secure authentication, and returns the result.
  4. Endpoint for webhooks: set up an endpoint to receive webhook events for reliable payment lifecycle tracking. For more information, see: Use webhooks to keep track of the payment lifecycle.

The order and payment flow:

  1. The customer goes to the checkout screen in your app and decides to pay by card.
  2. Your server creates an order and returns the token to your app.
  3. Your app calls RevolutMerchantCardFormKit.pay(token), which presents the card input form.
  4. The SDK collects the customer's card details, handles 3D Secure authentication, and resolves the returned promise with the payment result.
  5. Your server receives webhook notifications for each subscribed event.
Info

For more information about the order and payment lifecycle, see: Order and payment lifecycle.

Implementation overview

  1. Set up an endpoint for creating orders
  2. Install the SDK
  3. Initialise the SDK
  4. Start the payment process
  5. Handle payment result
  6. Set up webhooks

Before you begin

Ensure you have:

Implement the Revolut Merchant Card Form SDK

1. Set up an endpoint for creating orders

Before the card form can be displayed, your app needs a unique, single-use token that represents the customer's order. This token can only be created on your server by making a secure call to the Revolut Merchant API.

This server-side endpoint is a mandatory security requirement. Your secret API key must never be exposed in your React Native application.

Your endpoint is responsible for:

  1. Receiving the checkout details (e.g., amount, currency) from your client-side request.
  2. Securely calling the Merchant API: Create an order endpoint with the checkout details.
  3. Receiving the order object, including the public token, in the API response.
  4. Returning the token from the response to your app.

Below is an example of the JSON response from the Merchant API. The crucial field to extract and return to your app is the token.

Example response
{
"id": "6516e61c-d279-a454-a837-bc52ce55ed49",
"token": "0adc0e3c-ab44-4f33-bcc0-534ded7354ce",
"type": "payment",
"state": "pending",
"created_at": "2023-09-29T14:58:36.079398Z",
"updated_at": "2023-09-29T14:58:36.079398Z",
"amount": 1000,
"currency": "GBP",
"outstanding_amount": 1000,
"capture_mode": "automatic",
"checkout_url": "https://checkout.revolut.com/payment-link/0adc0e3c-ab44-4f33-bcc0-534ded7354ce",
"enforce_challenge": "automatic"
}

2. Install the SDK

Add the following packages to your project with your preferred package manager:

npm install @revolut/revolut-merchant-card-form @revolut/revolut-payments-core

After installing the packages, complete the platform-specific native setup:

The package includes a CocoaPods podspec that declares the native iOS SDK dependency. Install it by running:

cd ios && bundle exec pod install && cd ..
Note

The minimum iOS version is determined by your React Native version. The SDK's podspec defers to your project's own minimum - no additional iOS version requirement is imposed.

3. Initialise the SDK

Import RevolutPaymentsSDK and call configure once at the module level of your root component file, before the component declaration. This ensures initialisation runs exactly once when the app starts, independent of React's rendering lifecycle:

App.tsx
import { RevolutPaymentsSDK } from '@revolut/revolut-merchant-card-form'

RevolutPaymentsSDK.configure('<yourPublicApiKey>', 'production')
.catch(error => console.error('SDK configuration error:', error))

function App() {
// ...
}
ParameterDescriptionTypeRequired
merchantPublicKeyYour Merchant Public API key used for authorisation, available in the Revolut Business dashboard.

Note
For the Sandbox environment use the Sandbox API Public key.
stringYes
environmentThe API environment to connect to. Use 'sandbox' during development and testing.'sandbox' | 'production'Yes

4. Start the payment process

At this point in the payment flow, your checkout screen displays the order summary - items, total amount, and currency - and a pay button. The customer reviews their order and taps the button to proceed with payment.

When the customer taps the pay button, your handler calls the server-side endpoint from step 1 to create an order and receive the token, then passes it to RevolutMerchantCardFormKit.pay().

The SDK presents the card input form as a modal and handles card entry and 3D Secure authentication. The call returns a Promise that resolves to a RevolutMerchantCardFormCompletionEvent - the TypeScript type with two key properties: status (the outcome: 'success', 'failure', or 'userAbandoned') and error (present only when status is 'failure'). You will handle these in step 5.

CheckoutScreen.tsx
import {
RevolutMerchantCardFormKit,
RevolutMerchantCardFormCompletionEvent,
} from '@revolut/revolut-merchant-card-form'

const handlePayPress = async () => {
// Call the server-side endpoint from step 1 to create an order and get the `token`.
// For more information, see: Merchant API: Create an order
const order = await yourBackendCall()

// Present the card form - the SDK handles card entry and 3D Secure
const result: RevolutMerchantCardFormCompletionEvent =
await RevolutMerchantCardFormKit.pay(order.token)

// Handle the result - see step 5
}
Note

yourBackendCall() is a placeholder. In production, wrap your backend call in a try/catch block to handle network failures and server errors gracefully. Implement appropriate logging and retry logic before surfacing an error state to the customer.

5. Handle payment result

With the return type established, add the result handling logic to handlePayPress. Handle each case to lead the customer forward in the UI:

statusWhat happenedSuggested response
successThe payment was successfully authorised.Navigate to an order confirmation screen.
failureThe payment failed. Inspect result.error for details.Show an error message and offer the option to retry or use a different payment method.
userAbandonedThe customer closed the form without completing payment.Return to the checkout screen - this is not an error.
CheckoutScreen.tsx
import {
RevolutMerchantCardFormKit,
RevolutMerchantCardFormCompletionEvent,
} from '@revolut/revolut-merchant-card-form'

const handlePayPress = async () => {
const order = await yourBackendCall()
const result: RevolutMerchantCardFormCompletionEvent =
await RevolutMerchantCardFormKit.pay(order.token)

switch (result.status) {
case 'success':
// Navigate to a confirmation screen
break
case 'failure':
// Show an error message; inspect result.error for details
console.error('Payment failed:', result.error)
break
case 'userAbandoned':
// Handle gracefully - the customer closed the form
break
}
}

When result.status is 'failure', result.error is an object with code and message fields. Some codes also include a reason field. Log the full object for debugging purposes and to set up your error handling logic.

6. Set up webhooks

While the promise result from RevolutMerchantCardFormKit.pay() gives you the immediate outcome for your UI, it should not be used to trigger fulfilment logic. Network conditions, the customer force-quitting the app, or other interruptions can all prevent the result from being processed reliably.

Webhooks provide a guaranteed, server-to-server notification that gives you the authoritative final state of every payment.

Info

For instructions on registering webhook URLs and handling the events your server will receive, see: Use webhooks to keep track of the payment lifecycle.

Success

Congratulations! You have successfully integrated the Revolut Merchant Card Form SDK into your React Native application.

Example

A fully functional demo app is available in the revolut-mobile/revolut-payments-react-native public repository on GitHub. The packages/revolut-merchant-card-form-demo directory demonstrates a complete Revolut Merchant Card Form integration.

To run the demo:

  1. Clone the repository:

    git clone https://github.com/revolut-mobile/revolut-payments-react-native.git
  2. Navigate to the demo package:

    cd revolut-payments-react-native/packages/revolut-merchant-card-form-demo
  3. Install dependencies:

    npm install
  4. For iOS, install CocoaPods dependencies:

    bundle install
    cd ios && bundle exec pod install && cd ..
  5. Run the app:

    # iOS
    npm run ios

    # Android
    npm run android

Implementation checklist

Use this checklist to verify your integration is production-ready. Work through it twice: once in the Sandbox environment and again in Production. Both passes are required - Sandbox catches logic errors, but Production may surface environment-specific behaviour (real card networks, real 3DS flows) that Sandbox cannot replicate.

To test in Sandbox, set your API base URL to https://sandbox-merchant.revolut.com/ and initialise the SDK with 'sandbox':

await RevolutPaymentsSDK.configure('<yourPublicApiKey>', 'sandbox')

For test card numbers that simulate success, failure, and 3DS scenarios, see: Test cards.

Project setup

  • @revolut/revolut-merchant-card-form and @revolut/revolut-payments-core are installed.
  • Android MainActivity.kt implements CardPaymentLauncherHolder with a declared launcher.
  • RevolutPaymentsSDK.configure is called before any payment is initiated.

Payment flow

  • The card form is displayed correctly using the order token. The order must be created in the same environment where the SDK is initialised.
  • Test cards produce a successful payment.
  • Error scenario test cards trigger appropriate error handling.
  • The user closing the payment form is handled gracefully.

Backend verification

  • Webhook URL is registered and your server receives events (e.g., ORDER_COMPLETED, ORDER_PAYMENT_FAILED, ORDER_CANCELLED).
  • Order fulfilment logic is only triggered after receiving a successful webhook, not from the client-side promise result.

What's next