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.

How it works
From an implementation perspective, the SDK works with the following components:
- Server-side: A server-side endpoint is required to securely communicate with the Merchant API to create orders.
- Client-side: The SDK is configured in your app with your public API key. When the customer taps the Revolut Pay button, the
onCreateOrdercallback calls your server to fetch an ordertoken, which the SDK uses to initiate the payment. - 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.
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:
- The customer taps the Revolut Pay button.
- Your app calls
onCreateOrder, which fetches an ordertokenfrom your server. - 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.
- The SDK fires the
onCompletioncallback with the payment result. - Your server receives webhook notifications to confirm the payment's final state and securely fulfil the order.
Implementation overview
- Set up an endpoint for creating orders
- Configure deep link
- Install the SDK
- Initialise the SDK
- Add the Revolut Pay button
- Handle the payment result
- Set up webhooks
Before you begin
Before you start your integration, ensure you have:
- Active Revolut Business account with Merchant capabilities
- API credentials for the Merchant API
- A React Native project set up and ready for development
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:
- Receiving the checkout details (e.g.,
amount,currency) from your client-side request. - Securely calling the Merchant API: Create an order endpoint with the checkout details.
- Receiving the order object, including the public
token, in the API response. - Returning the
tokenfrom 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.
{
"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. Configure deep link
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.
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.
<!-- 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 ..
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:
import { RevolutPaymentsSDK } from '@revolut/revolut-pay-lite'
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. Note For the Sandbox environment use the Sandbox API Public key. | string | Yes |
environment | The 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.
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
}
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:
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' },
}}
/>
| Property | Description | Type | Required |
|---|---|---|---|
returnURL | URL the Revolut app redirects to after payment. Must match the scheme and host you registered in step 2. | string | Yes |
onCreateOrder | Called when the customer taps the button. Must return the order token from your server. See step 5.1. | () => Promise<string> | Yes |
onCompletion | Called when the payment flow ends. Receives the payment outcome. Defined in step 6. | (event: RevolutPayCompletionEvent) => void | Yes |
buttonStyle | Visual styling options for the button. See styling options below. | object | No |
The buttonStyle prop accepts the following options:
| Property | Description | Type | Required |
|---|---|---|---|
size | Button size. Defaults to 'large'. | 'extraSmall' | 'small' | 'medium' | 'large' | No |
radius | Corner radius style. | 'small' | 'large' | No |
attachmentStyle | Set to null to hide the currency attachment entirely. | null | No |
attachmentStyle.currency | Currency code displayed alongside the Revolut Pay label. | string | No |
variants.anyMode | Forces the same colour variant in both light and dark display modes. Mutually exclusive with lightMode/darkMode. | 'light' | 'dark' | 'lightOutlined' | 'darkOutlined' | No |
variants.lightMode | Colour variant used when the device is in light mode. Mutually exclusive with anyMode. | 'light' | 'dark' | 'lightOutlined' | 'darkOutlined' | No |
variants.darkMode | Colour variant used when the device is in dark mode. Mutually exclusive with anyMode. | 'light' | 'dark' | 'lightOutlined' | 'darkOutlined' | No |
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.
We recommend implementing the promotional banner. Analysis has shown that having the widget can increase conversion to payment by approximately 5%.
import { RevolutPayPromotionalBanner } from '@revolut/revolut-pay-lite'
<RevolutPayPromotionalBanner
transactionId='<transaction-id>'
amount={1000}
currency='GBP'
/>
| Property | Description | Type | Required |
|---|---|---|---|
transactionId | The unique ID of the payment corresponding to the promotional offer. | string | No |
amount | Order amount in minor currency units (e.g., 1000 = £10.00). | number | No |
currency | ISO 4217 currency code (e.g., 'GBP'). | string | No |
customer | Pre-fills the customer's details on the Revolut sign-up form. | object | No |
style | Optional React Native style applied to the banner container. | ViewStyle | No |
The customer object accepts the following optional fields:
| Property | Description | Type |
|---|---|---|
name | Customer's full name. | string |
email | Customer's email address. | string |
phone | Customer'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.
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:
status | What happened | Suggested response |
|---|---|---|
success | The payment was authorised and completed. | Navigate to an order confirmation screen. |
failure | The payment failed. The error field contains a description. | Show an error message and offer the option to retry or use a different payment method. |
userAbandoned | The customer closed the payment flow without completing. | Return to the checkout screen - this is not an error. |
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.
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.
For instructions on registering webhook URLs and handling the events your server will receive, see: Use webhooks to keep track of the payment lifecycle.
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:
-
Clone the repository:
git clone https://github.com/revolut-mobile/revolut-payments-react-native.git -
Navigate to the demo package:
cd revolut-payments-react-native/packages/revolut-pay-lite-demo -
Install dependencies:
npm install -
For iOS, install CocoaPods dependencies:
bundle install
cd ios && bundle exec pod install && cd .. -
Run the app:
# iOS
npm run ios
# Android
npm run android
Implementation checklist
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-liteand@revolut/revolut-payments-coreare installed.- iOS
Info.plistincludesLSApplicationQueriesSchemeswithrevolut. - iOS
Info.plistincludesCFBundleURLTypeswith your custom URL scheme. - iOS
AppDelegate.swiftcallsRevolutPayKit.handle(url:)in the URL context handler. - Android
AndroidManifest.xmlincludes the<queries>entry and the deep link<intent-filter>. - Android
MainActivity.ktimplementsRevolutPaymentControllerHolderand overridesonNewIntent. RevolutPaymentsSDK.configureis 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
onCompletioncallback.