Accept payments via Revolut Pay - Web
Welcome to the implementation guide for Revolut Pay on web! In this tutorial, we'll walk you through the implementation process of Revolut Pay, powered by the Revolut Checkout Widget.
Revolut Pay allows your customers to pay with their card or Revolut bank account, delivering a streamlined checkout experience to your customers through your website.

How it works
From an implementation perspective, the Revolut Pay 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 uses your public API key to initialise a
RevolutCheckout.payments()instance, which gives you access to therevolutPaymodule. The Revolut Pay button is then configured and mounted, using atokenfrom the created order to initiate the payment. The SDK handles the payment flow, including other actions like redirection or authentication. - Endpoint for webhooks: Your server should listen for webhook events to reliably track the payment lifecycle and handle critical backend processes like updating order status, managing inventory, or initiating shipping. For more information, see: Use webhooks to keep track of the payment lifecycle.
The payment flow is slightly different based on whether your customer has the Revolut app installed:
- The customer goes to the checkout page and taps the Revolut Pay button.
- Your frontend uses your server endpoint to create an order and obtains the order
tokenvia the Merchant API: Create an order. - The Revolut Pay SDK opens the Revolut app, where they review and authorise the payment.
- The SDK processes the payment and presents the payment result to the customer. You can handle this result in different ways, as you will see in the Handle payment results section.
- Your server receives webhook notifications about each event you're subscribed to. While this is optional, it's strongly recommended as the most reliable way to track the payment lifecycle. For more information, see: Use webhooks to keep track of the payment lifecycle.
For more information about the order and payment lifecycle in the Merchant API, see: Order and payment lifecycle.
Implementation overview
Check the following high-level overview on how to implement Revolut Pay on your website:
- Set up an endpoint for creating orders
- Install Revolut Checkout package
- Initialise Revolut Pay SDK
- Configure Revolut Pay SDK
- Mount Revolut Pay button
- Handle payment results
- Verify payment on your redirect page (for redirect flows)
The following sections describe each step in detail, to see working examples you can skip ahead.
Integration options
To handle the payment result, you have two primary integration paths, which are explained in detail in the Handle payment results section:
- Event listening with mobile redirects: Use the
.on()method to handle payment outcomes in your client-side code. This is the standard approach for desktop, you should also providemobileRedirectUrlsto handle redirection on mobile devices. - Redirects only: Use the
redirectUrlsoption. This will disable the.on()event handler entirely and redirect the user to a new URL on both desktop and mobile. This is a simpler integration path suitable for multi-page applications.
Before you begin
Before you start this tutorial, ensure you have completed the following steps:
Implement Revolut Pay
This section walks you through the server- and client-side implementation step by step.
The SDK supports both async/await syntax and the traditional Promise-based .then() syntax. You can see examples of both at each step of the guide.
1. Set up an endpoint for creating orders
Before implementing the client-side widget, you must first create a dedicated endpoint on your server. This is a critical security step, as your secret API key must never be exposed on the client side.
The role of this server-side endpoint is to act as a secure bridge between your frontend and the Merchant API. When a customer initiates a payment on your website, your frontend will call this endpoint. Your endpoint is then responsible for:
- Receiving the checkout details (e.g.,
amount,currency) from the frontend request. - Securely calling the Merchant API: Create an order endpoint with the received details.
- Receiving the order details, including the public
token, back from the Merchant API. - Passing this
tokenback to your frontend in the response.
Later, in the client-side configuration, the createOrder callback function will call this endpoint to fetch the token, which is required to initialise and display the Revolut Pay button.
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 frontend 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. Install Revolut Checkout package
Before you begin the client-side integration, add the Revolut Checkout package to your project using your preferred package manager. This package is necessary to interact with the Revolut Pay SDK.
Make sure you're on the latest version of the @revolut/checkout library.
npm install @revolut/checkout
Alternatively, you can add the widget to your code base by adding the embed script to your page directly. To learn more, see: Adding the embed script.
3. Initialise Revolut Pay SDK
Import RevolutCheckout and call RevolutCheckout.payments() with your Public API key to get a revolutPay instance. You can also pass an optional locale parameter here, which is useful if you want to align the language of the Revolut Pay widget with your site's language selector.
import RevolutCheckout from '@revolut/checkout'
const { revolutPay } = await RevolutCheckout.payments({
locale: 'en', // Optional, defaults to 'auto'
publicToken: '<yourPublicApiKey>', // Merchant public API key
})
// Configuration code will go here
4. Configure Revolut Pay SDK
Next, create a paymentOptions object. This object defines the core details of the payment and will be passed to the revolutPay.mount() method in the next step.
The most critical parameter is createOrder. This is a function you define that calls your backend to create an order via the Merchant API and returns the token (as publicId).
import RevolutCheckout from '@revolut/checkout'
const { revolutPay } = await RevolutCheckout.payments({
locale: 'en',
publicToken: '<yourPublicApiKey>',
})
const paymentOptions = {
currency: 'GBP', // 3-letter currency code
totalAmount: 1000, // in lowest denomination e.g., cents
createOrder: async () => {
// Call your backend here to create an order
// For more information, see: https://developer.revolut.com/docs/merchant/create-order
const order = await yourServerSideCall()
return { publicId: order.token }
},
// You can put other optional parameters here
}
RevolutCheckout.payments(...)initialises the Revolut Checkout module and returns an object containing therevolutPayfactory. You must pass yourlocaleandpublicToken(your merchant public API key) here.revolutPayis the factory that creates a Revolut Pay instance, which exposes two methods:.mount()- renders the Revolut Pay button in a specified container, based on the settings you defined..on()- registers an event handler to listen for payment status changes.
createOrderis a function you define (e.g.yourServerSideCall()) that takes the order details from your frontend (amount,currency, etc.) and passes them to your backend.- Your backend calls the Merchant API: Create an order endpoint with that payload.
- The Merchant API responds with a
token. - You return
{ publicId: order.token }to the widget, so it can start the checkout session.
For a more detailed reference of the Revolut Pay options and button styling, see: Payments.revolutPay reference and Revolut Pay button guidelines.
Additional settings
The Revolut Pay integration also offers a range of options for enhanced functionality. You can include these inside the paymentOptions object.
-
savePaymentMethodForMerchant: Set totrueto save the customer's payment method for future merchant-initiated transactions (e.g., subscriptions). For more information, see: Charge a customer's saved payment method, Manage subscriptions.noteTo save a payment method, you must either create a
customerobject during order creation or provide acustomer.idfor an existing customer. -
requestShipping: Set totrueto enable Fast checkout.This allows Revolut Pay to collect shipping address and delivery methods from the customer, letting your application skip the shipping flow and allowing the customer to use their details already stored in Revolut Pay.
Your backend must support the Fast checkout flow for this feature to work. See the Fast checkout guide for details.
buttonStyle: Customise the look and feel of the button.customer: Pre-fill the customer's details to speed up the checkout process.validate: Provide a function that performs validation before a payment is initiated.
5. Mount Revolut Pay button
Now, render the Revolut Pay button on your page.
5.1 Add a DOM element
First, add an empty <div> container to your HTML file where you want the button to appear.
<...>
<div id="revolut-pay"></div>
<...>
5.2 Mount the button
Call the .mount() method from your revolutPay instance, passing the container's selector and the paymentOptions you configured.
import RevolutCheckout from '@revolut/checkout'
const { revolutPay } = await RevolutCheckout.payments({
locale: 'en',
publicToken: '<yourPublicApiKey>',
})
const paymentOptions = {
currency: 'GBP',
totalAmount: 1000,
createOrder: async () => {
// Call your backend here to create an order
// For more information, see: https://developer.revolut.com/docs/merchant/create-order
const order = await yourServerSideCall()
return { publicId: order.token }
},
// You can put other optional parameters here
}
revolutPay.mount(document.getElementById('revolut-pay'), paymentOptions)
6. Handle payment results
Before choosing your method, it's crucial to understand the roles of client-side events and server-side webhooks:
- Widget events (
.on('payment', ...)): These are perfect for handling frontend logic, such as displaying a success message, updating the UI, or showing an error to the customer. However, their delivery is not guaranteed. Factors like the user's browser performance, network connectivity, or ad-blockers can prevent these events from firing. - Webhooks: These are server-to-server notifications that we guarantee to send for every payment status change. You must rely on webhooks for all critical backend logic, such as confirming the order, releasing digital goods, or starting the shipping process.
Never use widget events or client-side redirects as the sole trigger for critical backend logic.
In this step, you decide how to handle payment results. You have the following integration paths to choose from.
This hybrid approach combines the benefits of both methods. It uses event-driven control for a dynamic UI on desktop, while using redirects on mobile to ensure a robust payment flow, as widget events are not always guaranteed to fire in mobile browser scenarios.
Remember, the .on('payment', ...) handler in this context should only be used to manage the user interface on the frontend (e.g., showing a confirmation modal). Your backend must still rely on webhooks to confirm the payment status before fulfilling the order.
When to implement event listening and mobile redirect URLs:
- You maintain a stateful checkout page and users see it in a different state when they are redirected after payment
- Your checkout page does not initialise Revolut Pay immediately when opened
import RevolutCheckout from '@revolut/checkout'
const { revolutPay } = await RevolutCheckout.payments({
locale: 'en',
publicToken: '<yourPublicApiKey>'
})
const paymentOptions = {
currency: 'GBP',
totalAmount: 1000,
createOrder: async () => {
// Call your backend here to create an order
// For more information, see: https://developer.revolut.com/docs/merchant/create-order
const order = await yourServerSideCall()
return { publicId: order.token }
},
mobileRedirectUrls: {
success: 'https://www.example.com/success',
failure: 'https://www.example.com/failure',
cancel: 'https://www.example.com/cancel'
},
// You can put other optional parameters here
}
revolutPay.mount(document.getElementById('revolut-pay'), paymentOptions)
revolutPay.on('payment', (event) => {
switch (event.type) {
case 'cancel': {
if (event.dropOffState === 'payment_summary') {
// Do something to handle payment cancellation,
// if the payment was abandoned on the payment summary screen
window.alert('The payment was cancelled.')
}
break
}
case 'success':
onSuccess() {
// Do something to handle successful payments
window.alert('The payment was successful. Thank you!')
}
break
case 'error':
onError(event.error) {
// Do something to handle payment errors
window.alert(`Something went wrong. ${error}`)
}
break
}
})
For more information about listening to payment events, see: Payments.revolutPay: on(event, callback).
7. Verify payment on your redirect page (for redirect flows)
This step is necessary if you are using redirectUrls or mobileRedirectUrls.
When a customer is redirected to your success, failure, or cancel page, you must verify the final state of the payment before fulfilling the order.
-
Retrieve the order ID: On your redirect page, get the order's public ID from the URL. Revolut Pay appends it as a query parameter named
_rp_oid.// On your success/failure page:
import { getRevolutPayOrderIdURLParam } from '@revolut/checkout'
const revolutPublicOrderId = getRevolutPayOrderIdURLParam() -
Verify with your backend: Send this ID to your server.
-
Check order status: From your server, make a secure API call to the Merchant API: Retrieve an order endpoint using the order ID.
-
Confirm fulfillment: Only fulfill the order (e.g., ship goods, grant access) if the
statein the API response iscompleted. Display a corresponding success or failure message to the customer.
This server-side verification can be a crucial security step to prevent fraud.
Examples
Looking for more inspiration?
- Check out our live Revolut Pay demo for a practical demonstration of this integration.
- Explore our integration examples repository to discover all available examples and see how different payment solutions are implemented.
The examples below assume you added the DOM container on your webpage where you want to render the Revolut Pay button:
<...>
<div id='revolut-pay'></div>
<...>
Example with event listening and mobile redirect URLs
// 1. Import and initialise the SDK
import RevolutCheckout from '@revolut/checkout'
const { revolutPay } = await RevolutCheckout.payments({
publicToken: '<yourPublicAPIKey>'
})
// 2. Configure payment options with mobile redirects
const paymentOptions = {
currency: 'GBP',
totalAmount: 1000,
mobileRedirectUrls: {
success: 'https://www.example.com/success',
failure: 'https://www.example.com/failure',
cancel: 'https://www.example.com/cancel'
},
createOrder: async () => {
// Call your backend here to create an order
// For more information, see: https://developer.revolut.com/docs/merchant/create-order
const order = await yourServerSideCall()
return { publicId: order.token }
},
// You can put other optional parameters here
}
// 3. Mount the button
revolutPay.mount(document.getElementById('revolut-pay'), paymentOptions)
// 4. Hanlde payment events on desktop
// This event handler is for UI updates on desktop ONLY
revolutPay.on('payment', (event) => {
switch (event.type) {
case 'cancel': {
if (event.dropOffState === 'payment_summary') {
log('what a shame, please complete your payment')
}
break
}
case 'success':
onSuccess()
break
case 'error':
onError(event.error)
break
}
})
Example with redirect URLs
// 1. Import and initialise the SDK
import RevolutCheckout from '@revolut/checkout'
const { revolutPay } = await RevolutCheckout.payments({
publicToken: '<yourPublicApiKey>'
})
// 2. Configure payment options with redirects
const paymentOptions = {
currency: 'GBP',
totalAmount: 1000,
redirectUrls: {
success: 'https://www.example.com/success',
failure: 'https://www.example.com/failure',
cancel: 'https://www.example.com/cancel'
},
createOrder: async () => {
// Call your backend here to create an order
// For more information, see: https://developer.revolut.com/docs/merchant/create-order
const order = await yourServerSideCall()
return { publicId: order.token }
},
// You can put other optional parameters here
}
// 3. Mount the button
revolutPay.mount(document.getElementById('revolut-pay'), paymentOptions)
Implementation checklist
Before deploying your implementation to your production environment, complete the checklist below to see if everything works as expected, using Merchant API's Sandbox environment. To test in Sandbox environment, set the base URL of your API calls to: sandbox-merchant.revolut.com/.
As Revolut Pay has more complex user flows compared to other payment methods, the following user journey paths need to be tested (besides general tests):
To see if your implementation is able to manage all flows, check each flow against the checks below.
For more information about Revolut Pay payment flows in Sandbox, see: Test flows.
Desktop flow
This is the least complex of user flows. Steps are the following:
- User clicks Revolut Pay button on merchant's site.
- User journey is completed in a popup.
- Payment completed or failed, respective message is shown to customer.
Mobile flow (without Revolut app installed)
This user flow represents the case where the Revolut app is not installed on the customer's phone:
- User clicks Revolut Pay button on merchant's site using their mobile browser.
- Client SDK attempts to open the Revolut app.
- If it is not installed, user is redirected to a standalone page where the payment flow continues.
- Payment completed or failed, the customer should be redirected to the merchant site and respective message is shown to customer.
You can test this flow with your browser's built-in mobile browser simulator. Available via the browser's developer tools.
Mobile flow (with Revolut app installed)
This user flow represent the case, where the Revolut app is installed on the customer's phone:
- User clicks Revolut Pay button on merchant's site, on his mobile browser.
- Client SDK attempts to open the Revolut app.
- If it is installed, Revolut app is opened where the payment flow continues.
- Payment completed or failed, Revolut app attempts to redirect user to the same browser where the flow started.
Only Google Chrome, Safari, Samsung Internet Browser, Firefox, and Opera are supported. If the user begins on any other browser, successful redirection to the same browser is not guaranteed. The user will be redirected to their default mobile browser.
- A self-closing page is opened, user arrives at the tab where the flow started.
General checks
- Revolut Pay button is rendered at the intended DOM element.
- Your backend creates the order successfully when clicking the button.
- Order
tokenis successfully fetched. - Checkout is successful with test cards for successful payment.
- Checkout errors are handled as expected by your application.
- Check if your backend handles all cases of
cancelevents, and you manage everydropOffStateas intended. For more information about the cancellation events, see: Payments.revolutPay: Event object. - Failed payments with test cards for error cases.
New customer
- Customer is created in the Merchant API upon completing checkout.
- Customer details (email, payment methods, shipping details) saved in the Merchant API.
- On next checkout customer is already logged in.
Existing customer
- After first login, customer is already logged in on next checkout.
- Existing customer has their details returned.
- New payment method is saved to customer successfully.
- Shipping details are returned/saved to the customer successfully.
Event listening with mobile redirect URLs
If you implemented Revolut Pay with event listening and mobile redirect URLs:
- All payment events are listened to, when payment was made on desktop.
- Payment events trigger expected behavior on your backend.
- Redirect pages are always opened on mobile.
- Success or failure messaging work on your redirect pages on mobile.
Redirect URLs
If you implemented Revolut Pay with redirect URLs:
- Redirect pages are always opened, both on desktop and mobile.
- Success or failure messaging work on your redirect pages, both on desktop and mobile.
If your implementation handles all these cases as you expect in Sandbox, it is advised you also test your implementation in production before going live. Once your implementation passes all the checks in both environments, you can safely go live with your implementation.
These checks only cover the implementation path described in this tutorial, if your application handles more features of the Merchant API, see the Merchant API: Implementation checklists.
Congratulations! You've successfully implemented Revolut Pay and are ready to accept the first payment.