Sandbox
Help

Accept payments via Revolut Pay - Web

Add the Revolut Pay button to your checkout, confirm payment results in your UI, and fulfil orders on authoritative webhooks.

Revolut Pay allows your customers to pay with their card or Revolut bank account, delivering a streamlined checkout experience through your website.

In this tutorial, we'll guide you through integrating Revolut Pay, powered by the Revolut Checkout Widget. The SDK handles the entire payment flow, including redirection and authentication, reducing friction and improving conversion rates.

Revolut Pay - Web

How it works

From an implementation perspective, the Revolut Pay 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 uses your public API key to initialise a RevolutCheckout.payments() instance, which gives you access to the revolutPay module. The Revolut Pay button is then configured and mounted, using a token from the created order to initiate the payment. The SDK handles the payment flow, including other actions like redirection or authentication.
  3. Endpoint for webhooks: Your server listens for webhook events to reliably track the payment lifecycle. Webhooks are mandatory for 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:

  1. The customer goes to the checkout page and taps the Revolut Pay button.
  2. Your frontend uses your server endpoint to create an order and obtains the order token via the Merchant API: Create an order.
  3. The Revolut Pay SDK opens the Revolut app, where they review and authorise the payment.
  4. 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.
  5. Your server receives webhook notifications about each event you're subscribed to. Webhooks are the authoritative source of the final payment status — use them to drive critical backend logic such as order fulfilment, shipping, or inventory updates. 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

The high-level implementation steps are the following:

  1. Set up endpoint creating orders
  2. Install Revolut Checkout package
  3. Initialise Revolut Pay SDK
  4. Configure Revolut Pay SDK
  5. Mount Revolut Pay button
  6. Handle payment results

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 integration paths, explained in detail in step 6:

  • Redirect URLs: Add redirectUrls to your paymentOptions and the widget redirects the customer to your success, failure, or cancel pages on all devices. The simplest path, suitable for multi-page applications.
  • Event listening with mobile redirects: Handle payment outcomes in your client-side code with the .on() method for a dynamic UI, and provide mobileRedirectUrls to redirect customers on mobile devices when the payment continues on a standalone Revolut page (without the Revolut app installed).

Before you begin

Before you start this tutorial, make sure you have the following:

  • An active Merchant account with Revolut Business. If you don't have one yet, apply for a Merchant account.
  • Your API keys - both your public key (for initialising the SDK) and secret key (for creating orders on your backend). See Generate the API keys.
  • A backend server to securely create orders using the Merchant API. Your secret API key must never be exposed on the client side.
  • A website checkout page where you'll mount the Revolut Pay button.

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

  1. Receiving the checkout details (e.g., amount, currency) from the frontend request.
  2. Securely calling the Merchant API: Create an order endpoint with the received details.
  3. Receiving the order details, including the public token, back from the Merchant API.
  4. Passing this token back to your frontend in the response.

In step 4, the createOrder callback calls this endpoint to fetch the token the widget needs to start the payment.

Send a POST request to the Create an order endpoint with the order details, including your Secret API key in the authorisation header:

Request example
POST /api/orders HTTP/1.1
Host: merchant.revolut.com
Authorization: Bearer sk_abcdef12347890_...
Content-Type: application/json
Revolut-Api-Version: 2026-08-17

{
  "amount": 1000,
  "currency": "GBP"
}
ParameterDescription
amountThe order amount in minor currency unit (e.g., cents). For £10.00, use 1000.
currency3-letter ISO 4217 currency code for the payment. See supported currencies.

Replace the Revolut-Api-Version value with your target API version. We highly recommend using the latest API version to access the latest features.

For complete details on all available parameters, see: Merchant API: Create an order.

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
yarn add @revolut/checkout
pnpm add @revolut/checkout
bun add @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: Installation.

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.

my-app.js
import RevolutCheckout from '@revolut/checkout'

const { revolutPay } = await RevolutCheckout.payments({
  publicToken: 'pk_abcdef12347890_...', // Merchant public API key
  mode: 'prod', // API environment, defaults to 'prod'
  locale: 'en', // Optional, defaults to 'auto'
})

// Configuration code will go here
ParameterDescription
publicTokenYour Merchant API public key
modeAPI environment. Default: 'prod'
localeWidget language (defaults to 'auto' for automatic detection). See Locale

For more information about the RevolutCheckout.payments() module, see: Payments module

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).

my-app.js
import RevolutCheckout from '@revolut/checkout'

const { revolutPay } = await RevolutCheckout.payments({
  publicToken: 'pk_abcdef12347890_...', // Merchant public API key
  mode: 'prod',
})

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/api/merchant#create-order
    const order = await yourServerSideCall()
    return { publicId: order.token }
  },

  // You can put other optional parameters here
}
ParameterDescription
currency3-letter ISO 4217 currency code of the payment. Must match the currency of the order created in step 1.
totalAmountOrder amount in the currency's smallest denomination (e.g., cents). Must match the order's amount.
createOrderFunction the widget calls when the customer clicks the button. It calls your backend endpoint from step 1, which creates the order via the Merchant API: Create an order endpoint and returns the order's token. 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: Revolut Pay 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.

Save payment method

Set savePaymentMethodForMerchant to true to 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. For automated recurring billing built on saved payment methods, see the Subscriptions API introduction.

To save a payment method, you must either create a customer object during order creation or provide a customer.id for an existing customer.

Collect shipping details with Fast checkout

Set requestShipping to true to enable Fast checkout. This allows Revolut Pay to collect the 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.

Other optional parameters
ParameterDescription
buttonStyleCustomise the look and feel of the button.
customerPre-fill the customer's details to speed up the checkout process.
validateProvide 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.

my-page.html
<!-- ... -->

<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.

my-app.js
import RevolutCheckout from '@revolut/checkout'

const { revolutPay } = await RevolutCheckout.payments({
  publicToken: 'pk_abcdef12347890_...', // Merchant public API key
  mode: 'prod',
})

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/api/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)
ParameterDescription
targetThe DOM element (or a CSS selector string) where the Revolut Pay button renders.
optionsThe paymentOptions object configured in step 4.

6. Handle payment results

Revolut Pay reports the payment result to your frontend through one of two integration paths: redirect URLs, or event listening with mobile redirects. Both paths confirm the result at the UX-level only.

The authoritative payment status for your business logic - fulfilment, shipping, inventory - always arrives on your server via webhooks, which you set up at the end of this step.

Before choosing your integration path, understand the roles of client-side results 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.
  • Redirect URLs and their query parameters: These tell your page which outcome the customer saw, but they are untrusted client-side values - a user can forge or replay them. Arrival at your success URL is never proof of payment.
  • 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.

Confirm payment result in UI

You have the following integration paths to choose from:

This is the simplest path. The widget automatically redirects the customer to a new page for all outcomes, on all devices. Simply add a redirectUrls object to your paymentOptions. No event listener is needed.

When to implement redirect URLs:

  • You have limited technical resources
  • You use an older tech stack
  • You have a multi-page application
  • You need to redirect customers to a new page upon payment
my-app.js
import RevolutCheckout from '@revolut/checkout'

const { revolutPay } = await RevolutCheckout.payments({
  publicToken: 'pk_abcdef12347890_...', // Merchant public API key
  mode: 'prod',
})

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/api/merchant#create-order
    const order = await yourServerSideCall()
    return { publicId: order.token }
  },

  redirectUrls: {
    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)
ParameterDescription
successURL the customer is redirected to after a successful payment.
failureURL the customer is redirected to after a failed payment.
cancelURL the customer is redirected to after they cancel the payment.

case 'cancel': // Handle payment cancellations if (event.dropOffState === 'payment_summary') { // The payment was abandoned on the payment summary screen } break } }) })

| Parameter | Description |
|-----------|-------------|
| `success` | URL the customer is redirected to on mobile after a successful payment. |
| `failure` | URL the customer is redirected to on mobile after a failed payment. |
| `cancel` | URL the customer is redirected to on mobile after they cancel the payment. |

| Event type | Description |
|------------|-------------|
| `success` | The payment completed successfully. |
| `error` | The payment failed. Error details are available in `event.error.message`. |
| `cancel` | The customer cancelled the payment. `event.dropOffState` shows where - see [Revolut Pay](/docs/sdks/merchant-web-sdk/payment-methods/revolut-pay) for all values. |

:::info
For more information about listening to `payment` events, see: [Revolut Pay](/docs/sdks/merchant-web-sdk/payment-methods/revolut-pay).
:::


#### Handle customer landing on redirect page

Every Revolut Pay integration uses redirects: with `redirectUrls`, the customer is redirected on all devices; with `mobileRedirectUrls`, on mobile only. In both paths, the customer lands on one of your redirect pages after the payment.

When a customer is redirected to your `success`, `failure`, or `cancel` page, Revolut Pay appends the order's public ID (`token`) as a query parameter named `_rp_oid`. For a `success` URL of `https://www.example.com/success`, the final URL is `https://www.example.com/success?_rp_oid=fe34dbd3-3fa9-4d4c-8987-3f7735ba3cdf`.

Retrieve this ID on your redirect page using one of the following methods:

1. **Using `URLSearchParams`:**

 ```js
 const searchParams = new URLSearchParams(window.location.search)
 const revolutPublicOrderId = searchParams.get('_rp_oid')
  1. Using the SDK's helper function (available from v1.1.3):

    import { getRevolutPayOrderIdURLParam } from '@revolut/checkout'
    
    const revolutPublicOrderId = getRevolutPayOrderIdURLParam()

Send this ID to your backend and resolve it to the order you created in step 1 - your backend looks up the order by its token and uses the stored order id to retrieve the current payment state:

Send a GET request to the Retrieve an order endpoint with the order id your backend stored at creation:

Request example
GET /api/orders/{order_id} HTTP/1.1
Host: merchant.revolut.com
Authorization: Bearer sk_abcdef12347890_...
Revolut-Api-Version: 2026-08-17
Redirect values are untrusted

The _rp_oid query parameter and the redirect URL are untrusted client-side values. A user can forge or replay them.

Do not treat arrival at the success URL - or a completed state read on your redirect page - as the trigger for business logic such as fulfilment, shipping, or inventory updates. Those decisions belong to your webhook handler, which receives the guaranteed ORDER_COMPLETED event.

For more information about redirect handling, see: Revolut Pay.

Set up webhooks

Your backend must rely on webhooks as the authoritative source of payment status. Widget events and redirect URLs confirm the result at the UX level only - webhooks are guaranteed, server-to-server notifications that drive your critical backend logic.

Send a POST request to the Create a webhook endpoint with your webhook URL and the events you want to subscribe to:

Request example
POST /api/webhooks HTTP/1.1
Host: merchant.revolut.com
Authorization: Bearer sk_abcdef12347890_...
Content-Type: application/json

{
  "url": "https://example.com/webhooks",
  "events": [
    "ORDER_COMPLETED",
    "ORDER_AUTHORISED",
    "ORDER_PAYMENT_DECLINED",
    "ORDER_PAYMENT_FAILED",
    "ORDER_CANCELLED",
    "ORDER_FAILED"
  ]
}
ParameterDescription
urlYour webhook URL. Must be a valid HTTP or HTTPS URL capable of receiving POST requests.
eventsArray of event types to subscribe to. See Webhook events for the full list.

For detailed information on setting up webhooks, see: Use webhooks to track order and payment lifecycle.

Payment outcomes

Handle the following scenarios in your webhook handler:

The payment has been authorised by the card network or the customer's Revolut account.

Webhooks expected:

Webhook eventDescription
ORDER_AUTHORISEDThe payment has been authorised but not yet captured
ORDER_COMPLETEDThe payment has been captured and completed

What to do:

  • Automatic capture (default): both ORDER_AUTHORISED and ORDER_COMPLETED fire in quick succession. Fulfil the order once ORDER_COMPLETED is received.
  • Manual capture: ORDER_AUTHORISED fires first. Capture the payment to complete the transaction, or cancel the order to decline. ORDER_COMPLETED fires after you capture.

Examples

Looking for more inspiration?

The examples below show a complete, self-contained implementation for each integration path: a checkout page, the client-side integration, and a minimal backend that creates orders. Both examples use async/await syntax - see steps 3-6 for the Promise-based equivalents.

Example with redirect URLs

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Checkout</title>
  </head>
  <body>
    <h1>Checkout</h1>

    <!-- The Revolut Pay button renders here -->
    <div id="revolut-pay"></div>

    <script type="module" src="/app.js"></script>
  </body>
</html>

Example with event listening and mobile redirect URLs

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Checkout</title>
  </head>
  <body>
    <h1>Checkout</h1>

    <!-- The Revolut Pay button renders here -->
    <div id="revolut-pay"></div>

    <script type="module" src="/app.js"></script>
  </body>
</html>

Implementation checklist

Before deploying your implementation to your production environment, complete the checklist below to see if everything works as expected, using the Merchant API's Sandbox environment. To test in Sandbox, change the address of the endpoints you want to test from https://merchant.revolut.com/ to https://sandbox-merchant.revolut.com/. For more information, see: Set up a sandbox account.

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 the user flows. Steps are the following:

  1. User clicks the Revolut Pay button on the merchant's site.
  2. User journey is completed in a popup.
  3. Payment completed or failed, the respective message is shown to the 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:

  1. User clicks the Revolut Pay button on the merchant's site using their mobile browser.
  2. Client SDK attempts to open the Revolut app.
  3. If it is not installed, the user is redirected to a standalone page where the payment flow continues.
  4. Payment completed or failed, the customer should be redirected to the merchant site and the respective message is shown to the customer.

You can test this flow with your browser's built-in mobile browser simulator, available in the browser's developer tools.

Mobile flow (with Revolut app installed)

This user flow represents the case where the Revolut app is installed on the customer's phone:

  1. User clicks the Revolut Pay button on the merchant's site using their mobile browser.
  2. Client SDK attempts to open the Revolut app.
  3. If it is installed, the Revolut app is opened where the payment flow continues.
  4. Payment completed or failed, the Revolut app attempts to redirect the 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.

  5. A self-closing page is opened, and the 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 token is successfully fetched.

  • Checkout is successful with test cards for successful payment.

  • Checkout errors are handled as expected by your application.

    • All cases of cancel events are handled, and every dropOffState is managed as intended. For more information about the cancellation events, see: Revolut Pay.

    • Failed payments are handled with test cards for error cases.

Webhook verification

  • Webhook endpoint is set up to receive order and payment updates.
  • Webhook subscription is created with ORDER_AUTHORISED, ORDER_COMPLETED, ORDER_PAYMENT_DECLINED, ORDER_PAYMENT_FAILED, ORDER_CANCELLED, and ORDER_FAILED events.
  • ORDER_COMPLETED webhook event is received when a payment completes successfully.
  • If using manual capture, ORDER_AUTHORISED is received after authorisation, and ORDER_COMPLETED is received after you capture the payment.
  • Your backend only fulfils orders after receiving the ORDER_COMPLETED webhook event - never on widget events or redirect URL arrivals.
  • Webhook signature verification is implemented for security.

Revolut Pay specific checks

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.

Redirect URLs

If you implemented Revolut Pay with redirect URLs:

  • Redirect pages are always opened, both on desktop and mobile.
  • Success or failure messaging works on your redirect pages, both on desktop and mobile.
  • Redirect pages display the payment result based on the order state retrieved through the Merchant API, not on the redirect URL alone.

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 the expected UI updates on desktop.
  • Redirect pages are always opened on mobile.
  • Success or failure messaging works on your redirect pages on 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.


What's next

Revolut Pay Web SDK reference
Full SDK reference for the web SDK
Revolut Pay integration example
Practical demonstration of this integration
Using webhooks
Track the payment lifecycle with server-to-server webhooks
Order and payment lifecycle
Understand how orders and payments are processed
Sandbox test flows
Test your Revolut Pay implementation in the Sandbox environment
Marketing guidelines
Review the Revolut Pay marketing assets
Revolut Pay button guidelines
Review the Revolut Pay button design guidelines
Rate this page