Event API Reference

The SDK exposes events through the typed .on(name, handler) API at two levels:

  1. Instance eventspayrails.on(name, handler). Session-level and
    in-flight payment-attempt events (success, failed, pending,
    buttonClicked, requestStart, actionRequired, sessionExpired,
    deliveryAddressChanged).
  2. Element eventselement.on(name, handler), where element is the
    object returned by payrails.cardForm(), payrails.paymentButton(),
    payrails.googlePayButton(), and so on. Element events describe one rendered
    element (change, focus, blur, ready, saveInstrumentCheckboxChanged,
    preferredSchemeChanged, billingAddressChanged, validate,
    stateChanged, paymentOptionSelected).

.on() returns an unsubscribe function. Handlers may be async; the SDK awaits
each handler before proceeding. A handler that throws is logged and isolated
(other handlers still run). Payrails.init resets all subscriptions.

const off = payrails.on('success', (event) => {
  console.log(event.paymentMethodCode, event.action);
});
// later:
off(); // or payrails.off('success', handler)

Init-time callback (the one exception)

The only bag callback that still exists is onClientInitialized, passed inside
PayrailsClientOptions.events on Payrails.init. It has no .on() form
because it fires during construction, before a .on(...) listener could
register.

const payrails = await Payrails.init(initResponse, {
  events: {
    onClientInitialized: (execution) => useExecution(execution),
  },
});

Fires once when the Payrails instance is constructed, and again after a
successful session refresh (see sessionExpired). Its argument is the execution
response object (WorkflowExecutionResponse, e.g. execution.id).

Instance events — payrails.on(name, handler)

Every payment-attempt payload carries PaymentAttemptContext:

FieldTypeDescription
executionIdstringThe workflow execution id of the attempt.
paymentMethodCodestringThe payment method of the attempt.
action'AUTHORIZE' | 'TOKENIZE'Whether it's a payment or an instrument save.

One listener fires for any element's event in the session — filter with
event.paymentMethodCode / event.action if you need per-method behaviour.

success

Fires when authorization (or tokenization) completes successfully.

Payload: { …ctx, data? }. data carries the tokenization result on a
TOKENIZE success (notably for Google Pay / Apple Pay) and is absent for flows
that return nothing (e.g. AUTHORIZE).

failed

Fires when the attempt fails or is canceled.

Payload: { …ctx, data?: { code?, message? } } where code is an
AuthorizationFailureReasons value (VALIDATION_FAILED, AUTHORIZATION_ERROR,
AUTHENTICATION_ERROR, UNKNOWN_ERROR, USER_CANCELLED).

pending

Fires when the payment ends in a pending state (e.g. the result is not final yet
after a redirect or challenge).

Payload: { …ctx, data? }.

requestStart (cancelable)

Fires immediately before the authorize/tokenize request is sent.

Payload: { …ctx, preventDefault() }. Call preventDefault() to abort — the
SDK then fires failed with code: 'VALIDATION_FAILED'.

buttonClicked (cancelable)

Fires when the shopper clicks the payment button, before anything is submitted.

Payload: { …ctx, bin?, preventDefault() }. For card payments bin carries the
current BIN. Call preventDefault() to abort — the SDK then fires failed with
code: 'VALIDATION_FAILED'.

sessionExpired

Fires when the SDK detects the client session has expired, or when a payment
attempt fails because the session/vault token is no longer valid.

Payload: void. Handlers may return a fresh InitOptions
({ version, data }); the SDK awaits all handlers, re-inits from the first
non-null result, and then re-runs the init onClientInitialized callback.

payrails.on('sessionExpired', async () => fetchNewInitResponse());

deliveryAddressChanged (cancelable)

Fires when the shopper changes the shipping address inside an express-checkout
sheet (PayPal, Apple Pay).

Payload: { …ctx, deliveryAddress?, preventDefault() }. Call preventDefault()
to reject the address (the Apple Pay sheet shows an error and prompts for
another address; PayPal cancels the change).

  • Apple Pay exposes only redacted fields (city, postalCode, state,
    country.code) before the shopper authorizes; the full address is available
    on success. Apple Pay requires a response within ~30 seconds — keep this
    handler fast.
  • On PayPal, the payload shape is
    { orderID?, paymentID?, paymentToken?, shippingAddress?: { city, countryCode, postalCode, state } }.

actionRequired (cancelable)

Fires when a payment attempt needs a shopper-facing action and the SDK is about
to perform its default behavior — navigating to a redirect URL or presenting the
3DS challenge (popup or redirect).

Payload (ActionRequiredEvent):

FieldTypeDescription
urlstringThe URL the shopper must be taken to.
kind'genericRedirect' | '3ds'What the action is (ACTION_REQUIRED_KIND values).
executionIdstringThe workflow execution id of the payment attempt.
paymentMethodCodestringThe payment method of the attempt.
preventDefault()() => voidCall to take over: the SDK skips its default redirect/popup and you handle url yourself.
payrails.on('actionRequired', (event) => {
  if (event.kind === 'genericRedirect') {
    event.preventDefault();
    myRouter.openInModal(event.url);
  }
});

Element events — element.on(name, handler)

Element events are declared on the object returned by an element factory
(cardForm, paymentButton, googlePayButton, applePayButton,
paypalButton, dropin, dynamicElement, …). A name lives on exactly one
element type; using it on the wrong element is a compile error.

change

Fires on every card-form input change.

Payload: { isValid, cardNetwork, bin?, billingAddress?, cardSchemes? }. On a
standalone secure field (collect element) the payload is
{ isValid, isEmpty, network, bin?, cardSchemes? } — note network, not
cardNetwork.

focus / blur

Fires when a card field gains / loses focus. Focusing the card form also clears
a cardList selection.

Payload: void.

ready

Fires when all secure fields have mounted and the form is interactive.

Payload: void.

saveInstrumentCheckboxChanged

Fires when the shopper toggles the "save instrument" checkbox.

Payload: { checked: boolean }.

preferredSchemeChanged

Fires when the detected/selected card scheme changes for a co-branded card.

Payload: { preferredScheme: string, cardSchemes: CardScheme[] }.

billingAddressChanged

Fires when the shopper edits the billing address form.

Payload: { isValid: boolean, billingAddress? }.

validate

Fires after the card form is validated on click, before the payment request.

Payload: { isValid, error?, fieldErrors? }. fieldErrors maps fields to their
validation errors.

stateChanged

Fires when a payment button switches between enabled and disabled (e.g. as
card-form validity changes).

Payload: { state: 'enabled' | 'disabled' }.

Wallet availability (not an event)

Wallet availability is exposed as readonly isAvailable: Promise<boolean> on
googlePayButton, applePayButton, and paypalButton — not as an event,
because it is a capability check that settles once. The promise never rejects;
it resolves to false on any check-side failure. See the
payrails reference.

paymentOptionSelected

Fires when the shopper selects a payment option in the drop-in.

Payload: { paymentMethod?, instrument? }. paymentMethod is the selected
method's configuration; instrument is set when a stored instrument was
selected.

Usage

import { Payrails } from '@payrails/web-sdk';

const payrails = await Payrails.init(initResponse, {
  events: { onClientInitialized: (execution) => useExecution(execution) },
});

const cardForm = payrails.cardForm();
cardForm.on('change', (e) => render(e.isValid));
cardForm.on('ready', () => show());

payrails.on('buttonClicked', async (e) => {
  if (!(await ok())) e.preventDefault();
});
payrails.on('success', (e) => console.log(e.paymentMethodCode));
payrails.on('failed', (e) => console.log(e.data?.code));
payrails.on('sessionExpired', async () => fetchNewInitResponse());


Did this page help you?