Guides • Accept Payments
Web
doc

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.

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

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

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:

  1. Set up an endpoint for 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
  7. 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 provide mobileRedirectUrls to handle redirection on mobile devices.
  • Redirects only: Use the redirectUrls option. 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.

note

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:

  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.

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.

caution

Make sure you're on the latest version of the @revolut/checkout library.

npm install @revolut/checkout
info

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.

my-app.js
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).

my-app.js
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 the revolutPay factory. You must pass your locale and publicToken (your merchant public API key) here.
  • revolutPay is 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.
  • createOrder is a function you define (e.g. yourServerSideCall()) that takes the order details from your frontend (amount, currency, etc.) and passes them to your backend.
    1. Your backend calls the Merchant API: Create an order endpoint with that payload.
    2. The Merchant API responds with a token.
    3. You return { publicId: order.token } to the widget, so it can start the checkout session.
info

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

    note

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

  • requestShipping: Set to true to 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.

    caution

    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.

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({
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

caution

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.

tip

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
my-app.js
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
}
})
info

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.

  1. 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()
  2. Verify with your backend: Send this ID to your server.

  3. Check order status: From your server, make a secure API call to the Merchant API: Retrieve an order endpoint using the order ID.

  4. Confirm fulfillment: Only fulfill the order (e.g., ship goods, grant access) if the state in the API response is completed. Display a corresponding success or failure message to the customer.

This server-side verification can be a crucial security step to prevent fraud.

Examples

tip

Looking for more inspiration?

The examples below assume you added the DOM container on your webpage where you want to render the Revolut Pay button:

my-page.html
<...>

<div id='revolut-pay'></div>

<...>

Example with event listening and mobile redirect URLs

my-app.js
// 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

my-app.js
// 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.

info

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:

  1. User clicks Revolut Pay button on merchant's site.
  2. User journey is completed in a popup.
  3. 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:

  1. User clicks Revolut Pay button on merchant's site using their mobile browser.
  2. Client SDK attempts to open the Revolut app.
  3. If it is not installed, 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 respective message is shown to customer.
tip

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:

  1. User clicks Revolut Pay button on merchant's site, on his mobile browser.
  2. Client SDK attempts to open the Revolut app.
  3. If it is installed, Revolut app is opened where the payment flow continues.
  4. Payment completed or failed, Revolut app attempts to redirect user to the same browser where the flow started.
    caution

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

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.

success

Congratulations! You've successfully implemented Revolut Pay and are ready to accept the first payment.

What's next

Was this page helpful?