Accept payments via Revolut Pay - React Native

The Revolut Pay SDK for React Native lets merchants add the Revolut Pay button to their cross-platform apps. This guide walks you through integrating Revolut Pay Lite allowing customers to pay via the Revolut app or a secure in-app WebView.

Revolut Pay - Mobile

How it works

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

  1. Server-side: A server-side endpoint is required to securely communicate with the Merchant API to create orders.
  2. Client-side: The SDK is configured in your app with your public API key. When the customer taps the Revolut Pay button, the onCreateOrder callback calls your server to fetch an order token, which the SDK uses to initiate the payment.
  3. Endpoint for webhooks: Your server should listen for webhook events to reliably track the payment lifecycle. For more information, see: Use webhooks to keep track of the payment lifecycle.
Note

Only Revolut Pay Lite is available for React Native. Revolut Pay Native - which renders the entire payment experience inside your app - is not supported. If you need Revolut Pay Native, see the iOS guide or Android guide.

The payment flow:

  1. The customer taps the Revolut Pay button.
  2. Your app calls onCreateOrder, which fetches an order token from your server.
  3. The SDK checks for the Revolut app:
    • If installed: The customer is redirected to the Revolut app to authorise the payment, then redirected to your app via deep link.
    • If not installed: The SDK opens a secure in-app WebView for the customer to complete the payment.
  4. The SDK fires the onCompletion callback with the payment result.
  5. Your server receives webhook notifications to confirm the payment's final state and securely fulfil the order.

Implementation overview

  1. Set up an endpoint for creating orders
  2. Configure deep link
  3. Install the SDK
  4. Initialise the SDK
  5. Add the Revolut Pay button
  6. Handle the payment result
  7. Set up webhooks

Before you begin

Before you start your integration, ensure you have:

Implement Revolut Pay on React Native

1. Set up an endpoint for creating orders

To display the Revolut Pay button in your app, your client-side code needs to fetch a unique, single-use token that represents an 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.

When a customer taps the Revolut Pay button in your app, the SDK calls your onCreateOrder callback. Your endpoint is then 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 your endpoint will receive from the Merchant API after successfully creating an order. 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"
}

Revolut Pay Lite redirects customers to the Revolut app to authorise payment, then back to your app via a custom URL scheme. This step registers that scheme in the native platform files of your React Native project - not in JavaScript code.

Choose a scheme and host for your app (for example, scheme myapp and host revolut-pay). You will pass the resulting URL as returnURL to the Revolut Pay button in step 5.

Note

React Native apps bundle native iOS and Android projects, so both platforms need to be configured before proceeding.

Add two entries to your Info.plist:

  • LSApplicationQueriesSchemes - allows the SDK to check whether the Revolut app is installed.
  • CFBundleURLTypes - registers your custom URL scheme so iOS routes the deep link back into your app.
Info.plist
<!-- Allow the SDK to query whether the Revolut app is installed -->
<key>LSApplicationQueriesSchemes</key>
<array>
<string>revolut</string>
</array>

<!-- Register your app's custom URL scheme to receive deep links -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>

After installing the SDK in step 3, you will add the URL handler to AppDelegate.swift in step 4.

3. Install the SDK

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

npm install @revolut/revolut-pay-lite @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.

4. Initialise the SDK

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

App.tsx
import { RevolutPaymentsSDK } from '@revolut/revolut-pay-lite'

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

5. Add the Revolut Pay button

5.1 Implement the onCreateOrder callback

The onCreateOrder callback is called when the customer taps the button. It must call the server-side endpoint you set up in step 1 and return the order token. The SDK passes this token to the Revolut app to link the payment session to this specific order.

CheckoutScreen.tsx
const handleCreateOrder = async (): Promise<string> => {
// Call the server-side endpoint you set up in step 1 and return the order token.
// For more information, see: Merchant API: Create an order
const order = await yourBackendCall()
return order.token
}
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.2 Render the Revolut Pay button

Import and render RevolutPayButton on your checkout screen:

CheckoutScreen.tsx
import { RevolutPayButton } from '@revolut/revolut-pay-lite'

const handleCreateOrder = async (): Promise<string> => {
// Call the server-side endpoint you set up in step 1 and return the order token.
const order = await yourBackendCall()
return order.token
}

<RevolutPayButton
returnURL='myapp://revolut-pay'
onCreateOrder={handleCreateOrder}
onCompletion={handleCompletion} // defined in step 6
buttonStyle={{
size: 'medium',
radius: 'small',
attachmentStyle: { currency: 'GBP' },
}}
/>
PropertyDescriptionTypeRequired
returnURLURL the Revolut app redirects to after payment. Must match the scheme and host you registered in step 2.stringYes
onCreateOrderCalled when the customer taps the button. Must return the order token from your server. See step 5.1.() => Promise<string>Yes
onCompletionCalled when the payment flow ends. Receives the payment outcome. Defined in step 6.(event: RevolutPayCompletionEvent) => voidYes
buttonStyleVisual styling options for the button. See styling options below.objectNo

The buttonStyle prop accepts the following options:

PropertyDescriptionTypeRequired
sizeButton size. Defaults to 'large'.'extraSmall' | 'small' | 'medium' | 'large'No
radiusCorner radius style.'small' | 'large'No
attachmentStyleSet to null to hide the currency attachment entirely.nullNo
attachmentStyle.currencyCurrency code displayed alongside the Revolut Pay label.stringNo
variants.anyModeForces the same colour variant in both light and dark display modes. Mutually exclusive with lightMode/darkMode.'light' | 'dark' | 'lightOutlined' | 'darkOutlined'No
variants.lightModeColour variant used when the device is in light mode. Mutually exclusive with anyMode.'light' | 'dark' | 'lightOutlined' | 'darkOutlined'No
variants.darkModeColour variant used when the device is in dark mode. Mutually exclusive with anyMode.'light' | 'dark' | 'lightOutlined' | 'darkOutlined'No
Info

For button guidelines and marketing assets, see: Revolut Pay button guidelines.

(Optional) Add the promotional banner

The RevolutPayPromotionalBanner widget offers rewards to customers who create a new Revolut account after checkout.

Boost your conversions

We recommend implementing the promotional banner. Analysis has shown that having the widget can increase conversion to payment by approximately 5%.

CheckoutScreen.tsx
import { RevolutPayPromotionalBanner } from '@revolut/revolut-pay-lite'

<RevolutPayPromotionalBanner
transactionId='<transaction-id>'
amount={1000}
currency='GBP'
/>
PropertyDescriptionTypeRequired
transactionIdThe unique ID of the payment corresponding to the promotional offer.stringNo
amountOrder amount in minor currency units (e.g., 1000 = £10.00).numberNo
currencyISO 4217 currency code (e.g., 'GBP').stringNo
customerPre-fills the customer's details on the Revolut sign-up form.objectNo
styleOptional React Native style applied to the banner container.ViewStyleNo

The customer object accepts the following optional fields:

PropertyDescriptionType
nameCustomer's full name.string
emailCustomer's email address.string
phoneCustomer's phone number.string

6. Handle the payment result

When the customer finishes payment in the Revolut app, the Revolut app opens the deep link you registered in step 2 (for example, myapp://revolut-pay). The OS recognises your URL scheme and automatically delivers the link to a native callback in your project.

You pass that URL or URI to the SDK, which extracts the payment result from it and fires your onCompletion callback - which you will define in the next section.

Connect the URL handler

In AppDelegate.swift (in your ios/ folder), import the RevolutPayLite framework and add the URL handler. You do not construct this URL - iOS delivers it because you registered the scheme in Info.plist in step 2. Calling RevolutPayKit.handle(url:) passes it to the SDK, which resolves the payment result and fires onCompletion.

Your implementation depends on your app's lifecycle management. Use the SceneDelegate method for modern, scene-based apps (the default since iOS 13). Use the AppDelegate method if your app has explicitly opted out of the scene-based lifecycle.

When the customer returns from the Revolut app, iOS automatically calls scene(_:openURLContexts:) - a standard UISceneDelegate system callback - and delivers the deep link URL in URLContexts.

AppDelegate.swift
import RevolutPayLite

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if let url = URLContexts.first?.url {
RevolutPayKit.handle(url: url)
}
}

Handle the onCompletion callback

Once the URL handler above has delivered the deep link to the SDK, the SDK calls your onCompletion callback. Handle each outcome to move the customer forward in the UI:

statusWhat happenedSuggested response
successThe payment was authorised and completed.Navigate to an order confirmation screen.
failureThe payment failed. The error field contains a description.Show an error message and offer the option to retry or use a different payment method.
userAbandonedThe customer closed the payment flow without completing.Return to the checkout screen - this is not an error.
Caution

The onCompletion callback is for UI updates only. Its delivery is not guaranteed under all network conditions. You must rely on server-to-server webhooks to get the final, authoritative payment status before fulfilling the order. See step 7 for details.

CheckoutScreen.tsx
import { 
RevolutPayButton,
RevolutPayCompletionEvent
} from '@revolut/revolut-pay-lite'

const handleCreateOrder = async (): Promise<string> => {
// Call the server-side endpoint you set up in step 1 and return the order token.
const order = await yourBackendCall()
return order.token
}

const handleCompletion = (event: RevolutPayCompletionEvent) => {
const { status, error } = event.nativeEvent

switch (status) {
case 'success':
// Navigate to a confirmation screen
break
case 'failure':
// Show an error message to the customer
console.error('Payment failed:', error)
break
case 'userAbandoned':
// Handle gracefully — the customer cancelled
break
}
}

<RevolutPayButton
returnURL='myapp://revolut-pay'
onCreateOrder={handleCreateOrder}
onCompletion={handleCompletion}
buttonStyle={{
size: 'medium',
radius: 'small',
attachmentStyle: { currency: 'GBP' }
}}
/>

When status is 'failure', the error field on event.nativeEvent is a string describing the failure from the underlying native SDK. Log it for debugging purposes.

7. Set up webhooks

While the onCompletion callback confirms the initial payment outcome to your app, it should not be used to trigger fulfilment logic. Network conditions, app backgrounding, or the customer force-quitting the app can all prevent the callback from firing 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

If all the steps above have been followed correctly, you have successfully implemented Revolut Pay on React Native!

Example

A fully functional demo app is available in the revolut-payments-react-native public repository on GitHub. The packages/revolut-pay-lite-demo directory demonstrates a complete Revolut Pay Lite 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-pay-lite-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

Note

The Sandbox environment replicates production behaviour. The Revolut retail app is not available in Sandbox, so the SDK will always fall back to the in-app WebView flow during testing.

For more information about Revolut Pay payment flows in Sandbox, see: Test flows.

Project setup

  • @revolut/revolut-pay-lite and @revolut/revolut-payments-core are installed.
  • iOS Info.plist includes LSApplicationQueriesSchemes with revolut.
  • iOS Info.plist includes CFBundleURLTypes with your custom URL scheme.
  • iOS AppDelegate.swift calls RevolutPayKit.handle(url:) in the URL context handler.
  • Android AndroidManifest.xml includes the <queries> entry and the deep link <intent-filter>.
  • Android MainActivity.kt implements RevolutPaymentControllerHolder and overrides onNewIntent.
  • RevolutPaymentsSDK.configure is called before any payment component renders.

Payment flow

  • The Revolut Pay button appears correctly on the checkout screen.
  • Tapping the button calls your server to create an order and receives a token.
  • Test case 1 (Revolut app not installed): Payment completes via the in-app WebView.
  • Test case 2 (Revolut app installed): Payment completes via redirect to the Revolut app and back.
  • A successful payment triggers the expected UI update.
  • A failed payment shows an appropriate error message.
  • The user cancelling the payment flow 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 onCompletion callback.

What's next