Sandbox
Help

sepaDirectDebit()

Enables customers to authorise direct debits from their EUR-denominated bank accounts via a SEPA Direct Debit mandate, collected through a popup widget on your checkout page.

Key features:

  • SEPA Direct Debit Core scheme support for personal and business accounts
  • In-widget mandate collection - customers enter IBAN and consent in the widget
  • Merchant-initiated transactions (MIT) enabled by default - payment methods are saved for the merchant
  • Optional billing address pre-fill
  • Asynchronous settlement (up to 5 business days)

For a complete implementation guide with examples, see: Accept payments via SEPA Direct Debit - Web

Prerequisites

This payment method requires payments module initialisation. See: Payments module initialisation

SEPA Direct Debit is not enabled by default. Contact your account executive to have it enabled on your merchant account. You will also need a valid SEPA Creditor ID.

Type signature

PaymentsInstance.sepaDirectDebit(
  options: PaymentsModuleSepaDirectDebitOptions
): SepaDirectDebitInstance

interface PaymentsModuleSepaDirectDebitOptions {
  createOrder: () => Promise<{ publicId: string }>
  billingAddress?: Address
  onSuccess?: (payload: { orderId: string }) => void
  onError?: (payload: {
    error: RevolutCheckoutError
    orderId: string
  }) => void
  onCancel?: (payload: { orderId: string | undefined }) => void
  isRecurringPayment?: boolean // defaults to true - saves payment method for merchant-initiated transactions
}

interface SepaDirectDebitInstance {
  show: () => void
  destroy: () => void
}

Parameters

ParameterDescriptionTypeRequired
optionsConfiguration object for SEPA Direct DebitPaymentsModuleSepaDirectDebitOptionsYes

PaymentsModuleSepaDirectDebitOptions interface

ParameterDescriptionTypeRequired
createOrderAsync function that calls your backend to create an order and returns the order token() => Promise<{publicId: string}>Yes
billingAddressPre-fills the customer's billing address in the widget. If provided and valid (countryCode, postcode, streetLine1, city), the billing form is hidden. If omitted or incomplete, the customer enters it manuallyAddressNo
onSuccessCallback triggered when the payment is submitted to the SEPA scheme. This does not indicate settlement - see Callback events(payload: {orderId: string}) => voidNo
onErrorCallback triggered when the payment fails. Receives RevolutCheckoutError(payload: {error: RevolutCheckoutError, orderId: string}) => voidNo
onCancelCallback triggered when user cancels the payment. orderId may be undefined if order creation failed(payload: {orderId?: string}) => voidNo
isRecurringPaymentWhen true (default), the payment method is saved for the merchant, enabling retries and recurring charges via the Merchant API (POST /api/payments). Set to false for one-time payments where the method should not be retained.boolean (default: true)No

Return value

SepaDirectDebitInstance

interface SepaDirectDebitInstance {
  show: () => void
  destroy: () => void
}

The method returns a SepaDirectDebitInstance object containing:

MethodDescriptionType
showOpen the SEPA Direct Debit popup widget() => void
destroyClose the widget and clean up resources() => void

Callback events

The SEPA Direct Debit widget provides callback functions for handling payment lifecycle events.

Widget callbacks are not guaranteed to fire due to network issues, browser closures, or ad-blockers. Always use webhooks for critical backend operations like order fulfilment.

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.

SEPA Direct Debit is a slow-rail payment method

The onSuccess callback fires when the payment is submitted to the SEPA scheme, not when it settles. Settlement can take up to 5 business days.

Authorisation started does not guarantee payment completion - the debtor's bank can still return the payment within the refusal window. Fulfil orders only after receiving the ORDER_COMPLETED webhook event.

onSuccess

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

Triggered when the payment is submitted to the SEPA scheme. This signals that the customer has provided their IBAN and mandate consent, and the debit instruction has been queued - not that the payment has settled.

Use cases:

  • Redirect to a "payment processing" page (not a confirmation page)
  • Display a message informing the customer that the debit is being processed
  • Update UI to reflect the pending payment state

Example:

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

onError

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

Triggered when the payment fails. The error parameter is a RevolutCheckoutError 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:

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

onCancel

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

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

Usage examples

import RevolutCheckout from '@revolut/checkout'

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

// Create SEPA Direct Debit instance
const instance = sepaDirectDebit({
  createOrder: async () => {
    const order = await yourServerSideCall()
    return { publicId: order.token }
  },

  billingAddress: {
    countryCode: 'DE',
    region: 'Berlin',
    city: 'Berlin',
    streetLine1: 'Musterstraße 1',
    streetLine2: '',
    postcode: '10115',
  },

  onSuccess: ({ orderId }) => {
    console.log('Payment submitted!', orderId)
    window.location.href = `/processing?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('sepa-direct-debit-button')
  .addEventListener('click', () => {
    instance.show()
  })

Merchant-initiated transactions

isRecurringPayment defaults to true, meaning the payment method is saved for the merchant after a successful payment. This enables merchant-initiated transactions (MIT) - retries and recurring charges - via POST /api/payments without requiring the customer to re-enter their IBAN.

The example below shows the default behaviour. The isRecurringPayment: true is redundant but shown for clarity:

const instance = sepaDirectDebit({
  createOrder: async () => {
    const order = await yourServerSideCall()
    return { publicId: order.token }
  },

  isRecurringPayment: true, // default - saved for MIT

  onSuccess: ({ orderId }) => {
    console.log('Recurring payment submitted!', orderId)
    window.location.href = `/processing?orderId=${orderId}`
  },

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

document.getElementById('sepa-dd-recurring-button').addEventListener('click', () => {
  instance.show()
})

To disable MIT for one-time payments, set isRecurringPayment: false. The payment method will not be saved after the payment completes.

See also

Rate this page