# `clickToPay()` (Beta)

:::warning [Beta]
The Click to Pay SDK is currently in Beta. To enable it for your merchant account, contact your Revolut account executive.
:::

Enables customers to pay using Click to Pay, a card-network-based checkout experience backed by Visa and Mastercard. The SDK opens a pop-up widget where enrolled customers can complete their payment in just a few clicks - the card networks handle authentication and payment credential delivery behind the scenes. Customers can enter their card details during checkout through Click to Pay and opt in to save them to a new or existing Click to Pay profile for future use.

**Key features:**

- Card-network-backed checkout (Visa and Mastercard Click to Pay)
- Built-in 3DS authentication handled by the card networks
- No manual card entry for recognised or returning customers
- Optional billing address support
- Pop-up widget on your checkout page

:::info
For a complete implementation guide with examples, see: [Accept payments via Click to Pay - Web](/docs/guides/merchant/accept-payments/online-payments/click-to-pay/web)

Check out our [integration examples repository](https://github.com/revolut-engineering/revolut-checkout-example).
:::

## Prerequisites

This payment method requires payments module initialisation. See: [Payments module initialisation](/docs/sdks/merchant-web-sdk/initialisation/payments-module)

<!-- TODO: Confirm with PO whether Click to Pay requires any merchant-specific prerequisites. SDR (Confluence: SDR Click to Pay, page 5694324737) says merchant onboarding to Visa and Mastercard schemes is automatic by Revolut via Netcetera — no action needed from the merchant. -->

:::note [One-time payments only]
Click to Pay supports one-time card-not-present payments only. Merchant-initiated transactions (MIT) and recurring payments are not available. For subscription and recurring billing, consider [SEPA Direct Debit](/docs/sdks/merchant-web-sdk/payment-methods/sepa-direct-debit) or [Revolut Pay](/docs/sdks/merchant-web-sdk/payment-methods/revolut-pay).
:::

## Type signature

```typescript
PaymentsInstance.clickToPay(
  options: PaymentsModuleClickToPayOptions
): ClickToPayInstance

interface PaymentsModuleClickToPayOptions {
  createOrder: () => Promise<{ publicId: string }>
  billingAddress?: Address
  onSuccess?: (payload: { orderId: string }) => void
  onError?: (payload: {
    error: RevolutCheckoutError
    orderId: string
  }) => void
  onCancel?: (payload: { orderId: string | undefined }) => void
}

interface ClickToPayInstance {
  show: () => void
  destroy: () => void
}
```

## Parameters

| Parameter | Description | Type | Required |
| --------- | ----------- | ---- | -------- |
| `options` | Configuration object for Click to Pay | [`PaymentsModuleClickToPayOptions`](#paymentsmoduleclicktopayoptions-interface) | Yes |

### `PaymentsModuleClickToPayOptions` interface

| Parameter | Description | Type | Required |
| --------- | ----------- | ---- | -------- |
| `createOrder` | Async function that calls your backend to [create an order](/docs/api/merchant#create-order) and returns the order token | `() => Promise<{publicId: string}>` | Yes |
| `billingAddress` | Provides the customer's billing address for payment processing and regulatory requirements. The SDK does not collect billing address — you must collect it on your frontend and pass it via this option. Accepts an [`Address`](/docs/sdks/merchant-web-sdk/types/address) object | [`Address`](/docs/sdks/merchant-web-sdk/types/address) | No |
| `onSuccess` | Callback triggered when the payment has been authorised | `(payload: {orderId: string}) => void` | No |
| `onError` | Callback triggered when the payment fails. Receives [`RevolutCheckoutError`](/docs/sdks/merchant-web-sdk/types/revolut-checkout-error) | `(payload: {error: RevolutCheckoutError, orderId: string}) => void` | No |
| `onCancel` | Callback triggered when user cancels the payment. `orderId` may be `undefined` if order creation failed | `(payload: {orderId?: string}) => void` | No |

## Return value

```typescript
ClickToPayInstance

interface ClickToPayInstance {
  show: () => void
  destroy: () => void
}
```

The method returns a `ClickToPayInstance` object containing:

| Method    | Description                              | Type         |
| --------- | --------------------------------------- | ------------ |
| `show`    | Open the Click to Pay pop-up widget       | `() => void` |
| `destroy` | Close the widget and clean up resources | `() => void` |

## Callback events

The Click to Pay widget provides callback functions for handling payment lifecycle events.

:::warning
Widget callbacks are not guaranteed to fire due to network issues, browser closures, or ad-blockers. Always use [webhooks](/docs/guides/merchant/monitor-and-observe/webhooks/using-webhooks) for critical backend operations like order fulfilment.
:::

:::note
In all callbacks, `orderId` refers to the order's public token (`order.token` from the API response), not the internal `order.id`. This is the public identifier used in your frontend code.
:::

### `onSuccess`

```typescript
(payload: { orderId: string }) => void
```

Triggered when the payment has been authorised. With automatic capture (default), the payment is captured immediately after. With manual capture, you capture the payment separately.

:::note
With automatic capture (default), authorisation and capture happen in quick succession. If you use manual capture, `onSuccess` fires after authorisation — you must [capture the payment](/docs/api/merchant#capture-order) separately to complete the transaction.
:::

**Use cases:**

- Display success message to the customer
- Redirect to order confirmation page
- Update UI to reflect successful payment

**Example:**

```typescript
onSuccess: ({ orderId }) => {
  console.log('Payment successful!', orderId)
  window.location.href = `/confirmation?orderId=${orderId}`
}
```

### `onError`

```typescript
(payload: { error: RevolutCheckoutError; orderId: string }) => void
```

Triggered when the payment fails. The `error` parameter is a [`RevolutCheckoutError`](/docs/sdks/merchant-web-sdk/types/revolut-checkout-error) object containing error details.

**Use cases:**

- Display error message to the customer
- Log error for debugging
- Re-enable checkout form
- Offer alternative payment methods

**Example:**

```typescript
onError: ({ error, orderId }) => {
  console.error('Payment failed:', error.message, orderId)
  alert(`Payment failed: ${error.message}`)
}
```

### `onCancel`

```typescript
(payload: { orderId: string | undefined }) => void
```

Triggered when the user cancels the payment. The `orderId` may be `undefined` if order creation failed or the user closed the widget before completing the flow.

**Use cases:**

- Display cancellation message
- Re-enable checkout form
- Track abandonment analytics

**Example:**

```typescript
onCancel: ({ orderId }) => {
  console.log('Payment cancelled', orderId)
  alert('Payment was cancelled. You can try again.')
}
```

## Usage examples

- ![With async/await]

  ```typescript
  import RevolutCheckout from '@revolut/checkout'

  // Initialise payments module
  const { clickToPay } = await RevolutCheckout.payments({
    publicToken: process.env.REVOLUT_PUBLIC_KEY,
    mode: 'prod',
  })

  // Create Click to Pay instance
  const instance = clickToPay({
    createOrder: async () => {
      const response = await fetch('/api/create-order', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ amount: 1000, currency: 'GBP' }),
      })
      const order = await response.json()
      return { publicId: order.token }
    },

    onSuccess: ({ orderId }) => {
      console.log('Payment successful!', orderId)
      window.location.href = `/confirmation?orderId=${orderId}`
    },

    onError: ({ error, orderId }) => {
      console.error('Payment failed:', error.message, orderId)
      alert(`Payment failed: ${error.message}`)
    },

    onCancel: ({ orderId }) => {
      console.log('Payment cancelled', orderId)
      alert('Payment was cancelled.')
    },
  })

  // Show the widget when user clicks a button
  document
    .getElementById('click-to-pay-button')
    .addEventListener('click', () => {
      instance.show()
    })
  ```

- ![Without async/await]

  ```typescript
  import RevolutCheckout from '@revolut/checkout'

  // Initialise payments module
  RevolutCheckout.payments({
    publicToken: process.env.REVOLUT_PUBLIC_KEY,
    mode: 'prod',
  }).then(({ clickToPay }) => {
    // Create Click to Pay instance
    const instance = clickToPay({
      createOrder: () => {
        return fetch('/api/create-order', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ amount: 1000, currency: 'GBP' }),
        })
          .then((response) => response.json())
          .then((order) => ({ publicId: order.token }))
      },

      onSuccess: ({ orderId }) => {
        console.log('Payment successful!', orderId)
        window.location.href = `/confirmation?orderId=${orderId}`
      },

      onError: ({ error, orderId }) => {
        console.error('Payment failed:', error.message, orderId)
        alert(`Payment failed: ${error.message}`)
      },

      onCancel: ({ orderId }) => {
        console.log('Payment cancelled', orderId)
        alert('Payment was cancelled.')
      },
    })

    // Show the widget when user clicks a button
    document
      .getElementById('click-to-pay-button')
      .addEventListener('click', () => {
        instance.show()
      })
  })
  ```

### With billing address provided

Provide the customer's billing address for payment processing and regulatory requirements:

```typescript
const instance = clickToPay({
  createOrder: async () => {
    const response = await fetch('/api/create-order', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ amount: 1000, currency: 'GBP' }),
    })
    const order = await response.json()
    return { publicId: order.token }
  },

  billingAddress: {
    countryCode: 'GB',
    region: 'London',
    city: 'London',
    streetLine1: '1 Revolut Street',
    streetLine2: '',
    postcode: 'EC2V 7HN',
  },

  onSuccess: ({ orderId }) => {
    console.log('Payment successful!', orderId)
    window.location.href = `/confirmation?orderId=${orderId}`
  },

  onError: ({ error, orderId }) => {
    alert(`Payment failed: ${error.message}`)
  },
})

document.getElementById('click-to-pay-button').addEventListener('click', () => {
  instance.show()
})
```

## See also

- [Accept payments via Click to Pay - Web](/docs/guides/merchant/accept-payments/online-payments/click-to-pay/web)
- [Payments module initialisation](/docs/sdks/merchant-web-sdk/initialisation/payments-module)
- [`Address` type reference](/docs/sdks/merchant-web-sdk/types/address)
- [`RevolutCheckoutError` type reference](/docs/sdks/merchant-web-sdk/types/revolut-checkout-error)
- [Use webhooks to keep track of the payment lifecycle](/docs/guides/merchant/monitor-and-observe/webhooks/using-webhooks)