Getting Started
How it works
From an implementation perspective, the SDK works with the following components:
- Client-side: initialise the SDK by providing your Merchant Public API key and selecting the preferred environment to prepare for accepting payments.
- Server-side: create an order and get
token, using the Merchant API: Create an order endpoint.
- Client-side: In your app, use the
RevolutPayButton component which handles the complete payment flow, including order creation and payment processing.
- Client-side: The SDK opens the Revolut Pay interface, where customers can complete their payment using their Revolut account or card.
- Endpoint for webhooks: optionally, you can set up an endpoint which receives webhook events from the Merchant API to track the payment lifecycle. For more information, see: Use webhooks to keep track of the payment lifecycle.
Build and run demo app
-
Clone the repository:
git clone https://github.com/revolut-mobile/revolut-payments-react-native.git
-
Navigate to the merchant card form demo:
cd revolut-payments-react-native/packages/revolut-pay-lite-demo
-
Install dependencies:
npm install
yarn install
-
For iOS, install CocoaPods dependencies:
bundle install
cd ios
bundle exec pod install
cd ..
For more information, please visit CocoaPods Getting Started guide.
-
Run the application:
npm run ios
yarn ios
npm run android
yarn android
If everything is set up correctly, you should see the demo app running in the Android Emulator, iOS Simulator, or your connected device.
This is one way to run the demo app — you can also build it directly from Android Studio or Xcode.
Implementation overview
Before you begin
Before you start this tutorial, ensure you have completed the following steps:
Implement the Revolut Pay Lite SDK
1. Install the SDK
Add the Revolut Pay Lite SDK dependencies to your React Native project:
npm install @revolut/revolut-pay-lite @revolut/revolut-payments-core
yarn add @revolut/revolut-pay-lite @revolut/revolut-payments-core
2. Initialise the SDK
Before you can process payments, you need to configure the SDK with your merchant public key and the desired environment. This should be done once, typically in your app's main component or during app initialization.
import { RevolutPaymentsSDK } from '@revolut/revolut-pay-lite';
const initializeSDK = async () => {
try {
await RevolutPaymentsSDK.configure(
'MERCHANT_PUBLIC_KEY',
'production'
);
console.log('SDK configured successfully');
} catch (error) {
console.error('SDK Configuration Error:', error);
}
};
initializeSDK();
3. Create an order
Before the payment form can be displayed in your app, your client-side code 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.
Setting up 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 proceeds to checkout in your app, the app will call this endpoint. 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
token from the response to your app.
Your app will then use this token to launch the card payment interface provided by the SDK.
4. Implement Revolut Pay button
Here's a complete example of how to implement the Revolut Pay button in your React Native component:
import React, { useState, useEffect } from 'react';
import { View, Text, Alert, SafeAreaView } from 'react-native';
import {
RevolutPaymentsSDK,
RevolutPayButton,
RevolutPayCompletionEvent
} from '@revolut/revolut-pay-lite';
const CheckoutScreen = () => {
const [sdkConfigured, setSdkConfigured] = useState<boolean>(false);
useEffect(() => {
const initializeSDK = async () => {
try {
await RevolutPaymentsSDK.configure(
'your_merchant_public_key_here',
'sandbox'
);
setSdkConfigured(true);
} catch (error) {
console.error('SDK Configuration Error:', error);
Alert.alert('Error', 'Failed to initialize payment SDK');
}
};
initializeSDK();
}, []);
const handleCreateOrder = async (): Promise<string> => {
try {
const response = await fetch('https://your-backend.com/create-order', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: 1000,
currency: 'GBP',
}),
});
const orderData = await response.json();
return orderData.token;
} catch (error) {
console.error('Error creating order:', error);
throw new Error('Failed to create order');
}
};
const handlePaymentCompletion = (event: RevolutPayCompletionEvent) => {
const { status, error } = event.nativeEvent;
console.log(`Payment completed with status: ${status}`);
switch (status) {
case 'success':
Alert.alert('Success!', 'Payment was successful.');
break;
case 'failure':
Alert.alert('Payment Failed', error || 'Unknown error occurred');
break;
case 'userAbandoned':
Alert.alert('Cancelled', 'User cancelled the payment.');
break;
}
};
if (!sdkConfigured) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Initializing payment SDK...</Text>
</View>
);
}
return (
<SafeAreaView style={{ flex: 1, padding: 20, justifyContent: 'center' }}>
<Text style={{ fontSize: 24, fontWeight: 'bold', marginBottom: 20, textAlign: 'center' }}>
Checkout
</Text>
{/* Order summary here */}
<View style={{ marginBottom: 30 }}>
<Text style={{ fontSize: 18, marginBottom: 10 }}>Order Total: £10.00</Text>
<Text style={{ color: '#666' }}>Pay securely with Revolut Pay</Text>
</View>
{/* Revolut Pay Button */}
<RevolutPayButton
buttonStyle={{
size: 'medium',
radius: 'small',
attachmentStyle: {
currency: 'GBP',
},
}}
returnURL={'your-app://payment-return'} // Your app's deep link URL
onCreateOrder={handleCreateOrder}
onCompletion={handlePaymentCompletion}
/>
</SafeAreaView>
);
};
export default CheckoutScreen;
5. Button Styling Options
The RevolutPayButton component supports various styling options to match your app's design:
<RevolutPayButton
buttonStyle={{
size: 'small' | 'medium' | 'large' | 'extraSmall',
radius: 'small' | 'large',
attachmentStyle: {
currency: 'GBP',
} | null,
variants: {
anyMode: 'light' | 'dark',
},
}}
returnURL={'your-app://payment-return'}
onCreateOrder={handleCreateOrder}
onCompletion={handlePaymentCompletion}
/>
6. Handle payment results
The onCompletion callback receives a RevolutPayCompletionEvent with the payment result:
const handlePaymentCompletion = (event: RevolutPayCompletionEvent) => {
const { status, error } = event.nativeEvent;
switch (status) {
case 'success':
console.log('Payment successful');
break;
case 'failure':
console.log('Payment failed:', error);
break;
case 'userAbandoned':
console.log('Payment cancelled by user');
break;
}
};
7. Deep Link Configuration
iOS Configuration
When the user is redirected from the Revolut app back to yours, you must pass the incoming URL to the SDK.
Your implementation depends on your app's lifecycle management. Use the UISceneDelegate method for modern, scene-based apps (the default since iOS 13), which support features like multiple windows on iPad. Use the AppDelegate method for older apps or if you have explicitly opted out of the scene-based lifecycle.
func scene(
_ scene: UIScene,
openURLContexts URLContexts: Set<UIOpenURLContext>
) {
if let url = URLContexts.first?.url {
RevolutPayKit.handle(url: url)
}
}
Android Configuration
Step 1: Configure the App Manifest
You need to declare necessary permissions and app-querying capabilities in your
AndroidManifest.xml.
Add internet permission: The SDK required network access.
<uses-permission android:name="android.permission.INTERNET" />
Declare Revolut app query: To allow the SDK to check if the Revolut app is installed, add the
<queries> element.
<queries>
<package android:name="com.revolut.revolut" />
</queries>
Step 2: Set up a deep link for redirection
A deep link is required for the Revolut app to redirect the user back to your app after payment
authorisation.
In your AndroidManifest.xml, add an <intent-filter> to the activity that will handle the result.
<activity android:name=".MainActivity" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="payment-return" android:scheme="myapp" />
</intent-filter>
</activity>
Step 3: Register payment launcher and propagate uri
Update your MainActivity.kt to implement RevolutPaymentControllerHolder:
package your.app.name
import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
import com.facebook.react.defaults.DefaultReactActivityDelegate
import android.content.Intent
import com.revolut.revolutpaylite.api.RevolutPaymentControllerHolder
import com.revolut.revolutpaylite.api.RevolutPaymentControllerWrapper
class MainActivity : ReactActivity(), RevolutPaymentControllerHolder {
override fun getMainComponentName(): String = "YourAppName"
override fun createReactActivityDelegate(): ReactActivityDelegate =
DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)
override val paymentController: RevolutPaymentControllerWrapper =
RevolutPaymentControllerWrapper(this)
}
Step 3: Handle deep link redirect
After the customer authorises the payment (either in the Revolut app or the web flow), they are
redirected back to your app using the deep link you configured before
You need to catch this redirect in the Activity you configured with the <intent-filter>. To do
this, override the onNewIntent() method and pass the incoming URI to the Revolut Pay SDK for
processing.
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
intent?.data?.let { uri ->
paymentController.handle(uri)
}
}
8. Error handling
The SDK provides detailed error information when payments fail. You can access error details from the result.error property:
if (result.status === 'failure' && result.error) {
const { code, message } = result.error;
console.log(`Payment failed with code: ${code}, message: ${message}`);
switch (code) {
case 'networkError':
Alert.alert('Network Error', 'Please check your internet connection');
break;
case 'cardDeclined':
Alert.alert(
'Card Declined',
'Your card was declined. Please try a different card.'
);
break;
default:
Alert.alert('Payment Error', message);
break;
}
}
9. (Optional) Promotional Banner
You can also display a Revolut Pay promotional banner to increase payment method awareness:
import { RevolutPayPromotionalBanner } from '@revolut/revolut-pay-lite';
<RevolutPayPromotionalBanner
bannerStyle={{
size: 'small' | 'medium' | 'large',
variants: {
anyMode: 'light' | 'dark',
},
}}
/>
10. (Optional) Set up webhooks
While your app receives the immediate result of the payment, this only confirms the initial status to the client. For a reliable and complete payment system, you should also use webhooks to receive server-to-server notifications about the order and payment lifecycle.
Webhooks are essential for a few key reasons:
- Source of truth: They provide a reliable way for your backend to track the final state of a payment, such as when it's been captured.
- Resilience: They ensure your system can handle cases where the user closes your app or loses connection after paying but before the client-side result is processed.
- Asynchronous updates: They notify you of events that happen after the initial payment, such as refunds or chargebacks.
By utilising both in-app payment result handling for immediate user feedback and webhooks for backend reconciliation, you create a robust and responsive payment processing system.
To learn how to implement webhooks, see our tutorial: Use webhooks to track order and payment lifecycle.