# 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](/img/accept-payments/payment-methods/card-payments/mobile-card-form.png "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](/docs/api/merchant#create-order) endpoint.
1. **Client-side:** initialise the SDK with your Merchant Public API key.
1. **Client-side:** call `RevolutMerchantCardFormKit.pay(orderToken)` to present the payment form. The SDK collects card details, handles 3D Secure authentication, and returns the result.
1. **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](/docs/guides/merchant/monitor-and-observe/webhooks/using-webhooks).

The order and payment flow:

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

:::info
For more information about the order and payment lifecycle, see: [Order and payment lifecycle](/docs/guides/merchant/reference/order-lifecycle).
:::

### Implementation overview

1. [Set up an endpoint for creating orders](#1-set-up-an-endpoint-for-creating-orders)
1. [Install the SDK](#2-install-the-sdk)
1. [Initialise the SDK](#3-initialise-the-sdk)
1. [Start the payment process](#4-start-the-payment-process)
1. [Handle payment result](#5-handle-payment-result)
1. [Set up webhooks](#6-set-up-webhooks)

### Before you begin

Ensure you have:

- [ ] [Active Revolut Business account with Merchant capabilities](/docs/guides/merchant/get-started)
- [ ] [API credentials for the Merchant API](/docs/guides/merchant/get-started)
- [ ] A React Native project set up and ready for development

## 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.
1. Securely calling the [Merchant API: Create an order endpoint](/docs/api/merchant#create-order) with the checkout details.
1. Receiving the order object, including the public `token`, in the API response.
1. 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`.

```json[Example response] {3}
{
  "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:

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

```

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

- ![iOS]

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

  ```bash
    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.
  :::
- ![Android]

  React Native's autolinking automatically includes the SDK's Android module - you do not need to add a Gradle dependency manually. Ensure your project-level `build.gradle` has `mavenCentral()` in the `repositories` block (included by default in React Native projects), then sync your Gradle files in Android Studio.

  After syncing, add one more one-time change to `MainActivity.kt`. The card form is presented using Android's `ActivityResultLauncher` API, which must be registered before the Activity's `onCreate` completes. 

  `CardPaymentLauncherWrapper` handles this registration - it must be declared as a class-level property (not called lazily) so it is ready before any payment is initiated.

  ```kotlin
    package your.app.package

    import com.facebook.react.ReactActivity
    import com.facebook.react.ReactActivityDelegate
    import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
    import com.facebook.react.defaults.DefaultReactActivityDelegate
    import com.revolut.merchantcardform.api.CardPaymentLauncherHolder
    import com.revolut.merchantcardform.api.CardPaymentLauncherWrapper

    class MainActivity : ReactActivity(), CardPaymentLauncherHolder {

      override fun getMainComponentName(): String = "YourAppName"

      override fun createReactActivityDelegate(): ReactActivityDelegate =
          DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)

      override val launcher: CardPaymentLauncherWrapper = CardPaymentLauncherWrapper(this)
    }

  ```

  :::note
  The minimum supported Android SDK version is API 24 (Android 7.0, Nougat). 

  No deep linking or custom URL scheme configuration is required.
  :::

### 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:

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

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

function App() {
  // ...
}

```

| Parameter           | Description                                                                                                                                                                                                                                                                                         | Type                            | Required |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | -------- |
| `merchantPublicKey` | Your Merchant Public API key used for authorisation, available in the [Revolut Business dashboard](https://business.revolut.com/settings/apis?tab=merchant-api). For the Sandbox environment use the [Sandbox API Public key](https://sandbox-business.revolut.com/settings/apis?tab=merchant-api). | `string`                        | Yes      |
| `environment`       | The 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](#1-set-up-an-endpoint-for-creating-orders) 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](#5-handle-payment-result).

```tsx[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:

| `status`        | What happened                                            | Suggested response                                                                     |
| --------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| `success`       | The payment was successfully authorised.                 | Navigate to an order confirmation screen.                                              |
| `failure`       | The payment failed. Inspect `result.error` for details.  | Show an error message and offer the option to retry or use a different payment method. |
| `userAbandoned` | The customer closed the form without completing payment. | Return to the checkout screen - this is not an error.                                  |

```tsx[CheckoutScreen.tsx] {11-23}
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](/docs/guides/merchant/monitor-and-observe/webhooks/using-webhooks).
:::

:::tip
**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`](https://github.com/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:

    ```bash
        git clone https://github.com/revolut-mobile/revolut-payments-react-native.git

    ```

1. Navigate to the demo package:

    ```bash
        cd revolut-payments-react-native/packages/revolut-merchant-card-form-demo

    ```

1. Install dependencies:

    ```bash
        npm install

    ```

1. For iOS, install CocoaPods dependencies:

    ```bash
        bundle install
        cd ios && bundle exec pod install && cd ..

    ```

1. Run the app:

    ```bash
        # 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'`:

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

```

For test card numbers that simulate success, failure, and 3DS scenarios, see: [Test cards](/docs/guides/merchant/test-and-go-live/testing/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](/docs/guides/merchant/test-and-go-live/testing/test-cards#test-for-successful-payments) produce a successful payment.
- [ ] [Error scenario test cards](/docs/guides/merchant/test-and-go-live/testing/test-cards#test-for-error-cases) 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

- [Order and payment lifecycle](/docs/guides/merchant/reference/order-lifecycle)
- [Sandbox test cards](/docs/guides/merchant/test-and-go-live/testing/test-cards)
- [Refund payments](/docs/guides/merchant/operations/refunds)
- [Manual capture](/docs/guides/merchant/operations/capture-and-settlement/capture-later)
- [Advanced authorisation](/docs/guides/merchant/operations/capture-and-settlement/advanced-authorisation/introduction)
- [Using webhooks](/docs/guides/merchant/monitor-and-observe/webhooks/using-webhooks)
- [Charge a saved payment method](/docs/guides/merchant/optimise-checkout/save-payment-methods/charge-saved-payment-method)
- [Card Form iOS SDK reference](/docs/sdks/merchant-ios-sdk/revolut-card-form-ios-sdk/methods-parameters)
- [Card Form Android SDK reference](/docs/sdks/merchant-android-sdk/revolut-card-form-android-sdk/methods-parameters)