# Speed up customer checkout by using saved card details

You can offer a 1-click checkout experience by saving a customer's card details and displaying them during the checkout process. To do this, you first need to clarify and update your service's Terms and Conditions about how you handle personal and financial data to comply with local regulations.

:::info
It's your responsibility as a merchant to meet local legislation requirements and be PCI DSS compliant.
:::

## Overview

Check the following high-level procedure to implement a 1-click checkout process using the Revolut Checkout Widget and the Merchant API:

1. [Save card details on Revolut](#step-1-save-card-details-on-revolut)
1. [(Optional) Get card details from Revolut to be stored on your server](#optional-step-2-get-card-details-from-revolut-to-be-stored-on-your-server)
1. [Display saved cards on the checkout page](#step-3-display-saved-cards-on-the-checkout-page)
1. [Use the `/payments` endpoint to initiate payment](#step-4-use-payments-endpoint-to-initiate-payment)
1. [Handle 3DS challenge and track payment state](#step-5-handle-3ds-challenge-and-track-payment-state)
1. [Handle payment completion](#step-6-handle-payment-completion)

## Step 1. Save card details on Revolut

To save a customer's card details by completing an order, see: [Save the card details of a customer](/docs/guides/merchant/optimise-checkout/save-payment-methods/charge-saved-payment-method#1-save-a-customers-card-details).

There are two ways of saving a payment method, you can save it either for the customer or merchant by initialising the Revolut Checkout Widget with the `savePaymentMethodFor` parameter set to `merchant` or `customer`.

In case of a 1-click checkout process, the customer will initiate the payment, so you have to save the payment method for the customer (`savePaymentMethodFor: "customer"`). If you also plan to manage subscriptions or take merchant initiated transactions, you can use `savePaymentMethodFor: "merchant"` as it will serve both cases. For more information about subscription management, see: [Manage subscriptions](/docs/guides/merchant/optimise-checkout/save-payment-methods/subscription-management).

See an example with the [Card field](/docs/sdks/merchant-web-sdk/payment-methods/card-field):

- ![Production]

  ```js {16}
  RevolutCheckout('<token>').then(function (instance) {
    var card = instance.createCardField({
      target: document.getElementById('card-field'),
      onSuccess() {
        window.alert('Thank you!')
      },
      onError(message) {
        window.alert('Oh no :(')
      },
    })

    document
      .getElementById('button-submit')
      .addEventListener('click', function () {
        card.submit({
          savePaymentMethodFor: 'customer',
        })
      })
  })
  ```

- ![Sandbox]

  ```js {16}
  RevolutCheckout('<token>', 'sandbox').then(function (instance) {
    var card = instance.createCardField({
      target: document.getElementById('card-field'),
      onSuccess() {
        window.alert('Thank you!')
      },
      onError(message) {
        window.alert('Oh no :(')
      },
    })

    document
      .getElementById('button-submit')
      .addEventListener('click', function () {
        card.submit({
          savePaymentMethodFor: 'customer',
        })
      })
  })
  ```

:::info
You can also use the [Card pop-up](/docs/sdks/merchant-web-sdk/payment-methods/pop-up) of the Revolut Checkout Widget to save a customer's payment method.
:::

## (Optional) Step 2. Get card details from Revolut to be stored on your server

Storing card details can be helpful when you have more websites, and you want to provide a faster checkout experience across all your sites. However, if you choose to store card details, you must ensure that these are in sync with the ones on the Merchant API.

Use the last paid order of the customer to retrieve a customer's card details stored on Revolut's server. Fetch and save the information from the response of the [Retrieve payment list of an order](/docs/api/merchant#retrieve-payment-list) endpoint.

See an example of a saved `payment_method` JSON object, returned by the endpoint:

```json
[
  {
    "id": "64a28499-aa14-a504-ab1b-fb64dd38b342",
    "order_id": "64a28331-7d61-a4b0-b5f8-df0446ce4ca1",
    "state": "captured",
    "payment_method": {
      "type": "card",
      "id": "64a28499-05c5-af30-bbf9-9c1b028e00b8",
      "brand": "mastercard_credit",
      "last_four": "1234"
    }
  }
]
```

You can use this JSON object to retrieve and save a customer's payment details on your server.

## Step 3. Display saved cards on the checkout page

Based on whether you store customer's payment methods on your server ([Step 2.](#optional-step-2-get-card-details-from-revolut-to-be-stored-on-your-server)), you have two main approaches to display saved cards during checkout:

- Display payment methods stored on your server
- Display payment methods by fetching the [Retrieve payment method list of a customer](/docs/api/merchant#retrieve-payment-method-list) endpoint

## Step 4. Use /payments endpoint to initiate payment

To initiate a payment send a `POST` request to the [Pay for an order](/docs/api/merchant#pay-order) endpoint, with the following payload:

```json
{
  "saved_payment_method": {
      "type": "card",
      "id": "<ID of the saved payment method>",
      "initiator": "customer"
}
```

:::info
For this checkout process, only use requests with `initiator: customer`. Revolut will ensure that the payment is PDS2 compliant by initiating a 3DS challenge. For more information about Revolut's 3DS solution, see: [3D Secure](/docs/guides/merchant/reference/3d-secure).
:::

## Step 5. Handle 3DS challenge and track payment state

:::info
For more information about Revolut's 3DS solution, see: [3D Secure](/docs/guides/merchant/reference/3d-secure).
:::

After your backend sends the request to the `/payments` endpoint, the response will have `authentication_started` or `authorisation_started` payment state. Set up an automated request, using the `order_id` from the response, that repeatedly checks the payment status to track the payment's `state` transitions.

For more information about this endpoint, see: [Merchant API: Retrieve payment details](/docs/api/merchant#retrieve-payment-details).

When the payment state is `authentication_challenge`, the response includes an `authentication_challenge` object. Check the `type` field and handle the challenge accordingly:

- ![three_ds]

  The payment requires the customer to complete a full 3DS challenge. Extract the `acs_url` from the response:

  ```json [Example payment with three_ds challenge] {5,24}
  {
    "id": "5e96b328-4054-41ed-b089-595ccc4d0870",
    "order_id": "a16718e0-077a-4942-89bc-23ac17a2e14c",
    "token": "294358bf-3818-49b2-aacc-7ca971f52695",
    "state": "authentication_challenge",
    "created_at": "2024-05-02T12:32:49.033945Z",
    "updated_at": "2024-05-02T12:32:57.003519Z",
    "amount": 100,
    "currency": "GBP",
    "settled_amount": 100,
    "settled_currency": "GBP",
    "billing_address": {
      "street_line_1": "123 Example Street",
      "street_line_2": "Building A, 3rd Floor",
      "city": "Example City",
      "region": "Example Region",
      "country_code": "GB",
      "postcode": "E14 4HD"
    },
    "risk_level": "low",
    "fees": [],
    "authentication_challenge": {
      "type": "three_ds",
      "acs_url": "https://acs.example.com/3ds-challenge/3c90cb4e-31f9-4a24-ab6b-68b3326d34ed"
    },
    "payment_method": {
      "type": "card",
      "card_brand": "visa",
      "funding": "credit",
      "card_country_code": "GB",
      "card_bin": "411111",
      "card_last_four": "1111",
      "card_expiry": "12/28",
      "cardholder_name": "Example Customer"
    }
  }
  ```

  Use a redirect page or a pop-up window to proceed with the challenge. Send a `GET` request to the `acs_url` to display it to your customer and continue polling the payment state.

- ![three_ds_fingerprint]

  The payment requires a silent 3DS fingerprint step before a potential full challenge. Extract the `fingerprint_html` from the response:

  ```json [Example payment with three_ds_fingerprint challenge] {5,16}
  {
    "id": "5e96b328-4054-41ed-b089-595ccc4d0870",
    "order_id": "a16718e0-077a-4942-89bc-23ac17a2e14c",
    "token": "294358bf-3818-49b2-aacc-7ca971f52695",
    "state": "authentication_challenge",
    "created_at": "2024-05-02T12:32:49.033945Z",
    "updated_at": "2024-05-02T12:32:57.003519Z",
    "amount": 100,
    "currency": "GBP",
    "settled_amount": 100,
    "settled_currency": "GBP",
    "risk_level": "low",
    "fees": [],
    "authentication_challenge": {
      "type": "three_ds_fingerprint",
      "fingerprint_html": "PCFET0NUWVBFIGh0bWw+CjxodG1sPgo8Ym9keT4KCjwvYm9keT4KPC9odG1sPg=="
    },
    "payment_method": {
      "type": "card",
      "card_brand": "visa",
      "funding": "credit",
      "card_country_code": "GB",
      "card_bin": "411111",
      "card_last_four": "1111",
      "card_expiry": "12/28",
      "cardholder_name": "Example Customer"
    }
  }
  ```

  Decode the `fingerprint_html` value from `base64` and load the resulting HTML into a sandboxed `<iframe>` in your front-end:

  ```html
  <iframe
    sandbox="allow-scripts allow-forms"
    srcdoc="DECODED_HTML"
    style="display: none"
  ></iframe>
  ```

  :::warning
  Do not use `innerHTML` or other direct DOM injection to render the decoded content — this can expose your page to XSS attacks.
  :::

  No further action is required — the script handles the 3DS Method flow automatically.

  Continue polling the payment state until the next transition.

  :::info
  If the fingerprint is not resolved within 15 seconds, Revolut escalates the payment to a full 3DS challenge.
  :::

Your backend should continue to check the payment state until the response contains a final payment state. Depending on the received final payment state, you need to do the following:

| Payment state | Description                                                                                                                                                                                                                                                                                                                     |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `captured`    | You can handle captured payments as completed and continue with [Step 6.](#step-6-handle-payment-completion) It usually takes 24 hours to settle payments on your Merchant Account (except in case of instant settlement).                                                                                                      |
| `completed`   | In case of instant settlements, captured payments instantly get settled to your Merchant Account, so the payment transitions to completed state. Continue with [Step 6.](#step-6-handle-payment-completion)                                                                                                                     |
| `authorised`  | In case you capture payments manually, payments stay in authorised state until you capture them, to learn more about the process, see: [Authorise an amount to capture later](/docs/guides/merchant/operations/capture-and-settlement/capture-later). After you captured the payment, continue with [Step 6.](#step-6-handle-payment-completion) |
| `declined`    | The payment was declined for some reason, check the [Troubleshooting](#troubleshooting) section to learn more about how to resolve the issue. For more information about specific decline reasons, see: [Decline reasons](/docs/guides/merchant/reference/error-codes/decline-reasons).                                 |
| `failed`      | The payment failed for some reason, check the [Troubleshooting](#troubleshooting) section to learn more about how to resolve the issue. For more information about specific decline reasons, see: [Decline reasons](/docs/guides/merchant/reference/error-codes/decline-reasons).                                       |
| `cancelled`   | If you capture payments manually, a payment in `authorised` state can be cancelled via the [Cancel an order](/docs/api/merchant#cancel-order) endpoint. If you want to reinitiate the payment, a new order has to be created. Restart the checkout process from creating the order.                                             |

## Step 6. Handle payment completion

If the payment transitioned to one of the successful final states (`captured`, `completed`), the corresponding order transitions to `completed` state. You can stop polling the payment status, and redirect the customer to your confirmation page to inform your customers about the status of their purchase.

:::info
Optionally, you can also set up webhooks to track order completion. However, Revolut only sends webhook events when a final order state is reached (`ORDER_COMPLETED`, `ORDER_PAYMENT_DECLINED`, `ORDER_PAYMENT_FAILED`).

If you only rely on webhook events, your system cannot react to intermediate payment state transitions. For more information about using webhooks, see: [Using webhooks to keep track of the payment lifecycle](/docs/guides/merchant/monitor-and-observe/webhooks/using-webhooks).
:::

### Send the receipt of the payment

You have two options to check for order completion:

- Retrieve order information by using the [Retrieve an order](/docs/api/merchant#retrieve-order) endpoint
- Use a webhook to track `ORDER_COMPLETED` events

When you confirmed that the order is completed, your system should send a notification (e.g. email) to the customer containing the following:

- Detailed receipt of the payment (including tax rates applied)
- Information about the payment method used and how to change it
- Date when the payment was collected
- Information about the refunding process

:::info
Local regulations may differ in what is required for such notification. It is your responsibility to consider the requirements of local regulators and comply with the relevant laws.
:::

:::tip
You've successfully implemented a 1-click checkout process using the Revolut Checkout Widget and the Merchant API.
:::

## Troubleshooting

The above-described process assumes that everything is working as expected. However, your system should be prepared to handle error cases. To test different payment errors, [use the test cards for error cases](/docs/guides/merchant/test-and-go-live/testing/test-cards#test-for-error-cases) in the Sandbox environment.

You have two options to check for error events:

- Use payment status polling to catch unsuccessful final states (`declined`, `failure`).
- Use a webhook to receive `ORDER_PAYMENT_DECLINED`, `ORDER_PAYMENT_FAILED` events.

When you catch an error, your system should send a notification (e.g. email) to the customer containing the following:

- Detailed information about the order (including tax rates applied).
- Information about the unsuccessfully charged payment method.
- Information about why the payment failed. You can retrieve this information from the `payments.decline_reason` field from the response of the [Retrieve an order](/docs/api/merchant#retrieve-order) endpoint, using the unsuccessful order's `id`. To learn more, check our [decline reasons](/docs/guides/merchant/reference/error-codes/decline-reasons).
- Information about how to resolve the issue:
  - Ask the customer to top up their saved payment method.
  - Ask the customer to provide another payment method. Redirect them to a page where you initialise the Revolut Checkout Widget, using the unsuccessful order's `token` and saving the new payment method.