Customize Embedded Checkout

Customise the Checkout Element's appearance using theme presets, CSS variables, custom rules, fonts, and locale settings.

Checkout Options

Pass these options when creating the checkout element:

Option Type Description
appearanceobjectTheme, variables, and CSS rules for styling
disabledbooleanDisable all inputs in the checkout element
hideSubmitButtonbooleanHide the built-in submit button (use your own)
submitButtonPropsobjectCustomise the built-in submit button (text, amount, style)
cardOptionsobjectOptions specific to the card input
applePayOptionsobjectOptions specific to the Apple Pay button, including the Apple Pay paymentRequest
cardContainerStyleobjectInline styles for the card input container
applePayContainerStyleobjectInline styles for the Apple Pay button container
onTokenRefreshfunctionCalled when the session token needs refreshing - return a new token

Callbacks

Register callbacks when creating the checkout element:

Callback Description
onReadyFired when the element is mounted and ready for interaction
onChangeFired when any input value changes
onFocusFired when an input receives focus
onBlurFired when an input loses focus
onSuccessFired on successful payment - receives (response, paymentMethod)
onErrorFired when a payment error occurs
onTokenRefreshCalled when session token needs refresh - () => Promise<string>

Saving a payment method

Consent comes first
Before you store a card, you must obtain and record the customer's agreement to save it and charge it again. That agreement — often called a mandate — defines when, how often, and for how much you can charge the card. It is what makes the later merchant-initiated payments valid.

Set save_payment_method to true when you create the checkout session on your server. The card is entered inside Teya's secure iframe, so 3D Secure runs there and the card number never touches your systems — nothing changes in how you mount the element.

Two fields work together on the session. save_payment_method is what mints the token; type decides what the customer sees:

typeamount.valueWhat the element renders
ACCOUNT_VERIFICATION0Saves the card without charging it. The submit button shows no price.
SALEThe real amountSaves the card alongside a real payment, and adds a consent checkbox that gates submission.

If you send type: ACCOUNT_VERIFICATION with save_payment_method: false, Teya verifies the card but saves nothing.

const { session_token } = await (await fetch('/api/save-card-session')).json();

const teya = await initTeyaBlocks(session_token);

const checkout = teya.elements.create('checkout', {
  submitButtonProps: { buttonText: 'Save card' },
  onSuccess: async (response) => {
    const authenticationId =
      response?.checkout_payment_response?.online_transaction?.authentication_id;

    await fetch('/account/cards/confirm', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ authenticationId }),
    });
  },
  onError: (error) => console.error('Save failed', error),
});

checkout.mount('#checkout-container');

The token arrives on the payment.succeeded.v1 webhook as data.token_response.token_id, once the payment and the token have both been created successfully. Store it alongside data.payment_method.brand and last4 so you can render “Mastercard ••••4065” in your UI.

Saving an Apple Pay card

Apple only issues a reusable token when the customer agrees to a recurring payment on the Apple Pay sheet. Pass Apple's recurringPaymentRequest through applePayOptions.paymentRequest; the SDK forwards it to Apple and requests the Apple Pay version that returns the merchant token. Without it, an Apple Pay payment with save_payment_method: true saves nothing.

const checkout = teya.elements.create('checkout', {
  applePayOptions: {
    paymentRequest: {
      recurringPaymentRequest: {
        paymentDescription: 'Monthly plan',
        regularBilling: {
          label: 'Monthly plan',
          amount: '9.99',
          paymentTiming: 'recurring',
          recurringPaymentIntervalUnit: 'month',
        },
        managementURL: 'https://example.com/account/subscriptions',
      },
    },
  },
  onSuccess: async (response) => { /* same as above */ },
});

The token arrives on the same webhook, with token_response.token_type: "WALLET_TOKEN". Store that value next to the token_id: a wallet token is charged with token_type: "WALLET_TOKEN" and initiator: MERCHANT only.

See Card on File for the consent rules and the wider flow, and Charge a Saved Payment Method for charging the token.

Theme Presets

Six built-in theme presets are available. Set the theme property in the appearance option:

const teya = await initTeyaBlocks('session-token', {
  appearance: {
    theme: 'default',
  },
});
Preset Description
defaultStandard light theme
darkDark background with light text
minimalReduced visual chrome
roundedLarger border radius on inputs
compactTighter spacing for dense layouts
boldHeavier font weights and borders

Theme Variables

Override individual CSS variables using appearance.variables:

const teya = await initTeyaBlocks('session-token', {
  appearance: {
    theme: 'default',
    variables: {
      colorPrimary: '#0055ff',
      colorBackground: '#ffffff',
      colorText: '#333333',
      borderRadius: '8px',
    },
  },
});
Variable Type Description
colorPrimarystringPrimary accent colour (focus rings, active states)
colorBackgroundstringInput background colour
colorTextstringInput text colour
colorDangerstringError and invalid state colour
colorSuccessstringSuccess state colour
colorWarningstringWarning state colour
borderRadiusstringInput border radius
borderWidthstringInput border width
borderColorstringInput border colour
fontFamilystringFont family for all text
fontSizeBasestringBase font size
fontWeightNormalstringNormal font weight (default: 400)
fontWeightMediumstringMedium font weight (default: 500)
fontWeightBoldstringBold font weight (default: 700)
spacingUnitstringBase spacing unit used for padding and margins
focusRingColorstringColour of the focus ring around inputs
focusRingWidthstringWidth of the focus ring

Custom CSS Rules

For more granular control, use appearance.rules to target specific selectors:

const teya = await initTeyaBlocks('session-token', {
  appearance: {
    rules: {
      '.Input': {
        border: '1px solid #ccc',
        padding: '12px',
      },
      '.Input:focus': {
        borderColor: '#0055ff',
        boxShadow: '0 0 0 2px rgba(0, 85, 255, 0.2)',
      },
      '.Input--invalid': {
        borderColor: '#dc3545',
      },
      '.Label': {
        fontWeight: '600',
        marginBottom: '4px',
      },
    },
  },
});

Submit Button

Customise the built-in submit button or provide your own:

const checkout = teya.elements.create('checkout', {
  hideSubmitButton: false,
  submitButtonProps: {
    buttonText: 'Pay now',
    buttonAmount: '€10.00',
    style: { backgroundColor: '#0055ff' },
  },
});

Fonts

Set appearance.fontFamily to one of the built-in options loaded from Google Fonts:

Value Font
systemSystem font stack (default)
interInter
robotoRoboto
open-sansOpen Sans
latoLato
poppinsPoppins
montserratMontserrat
source-sans-proSource Sans Pro
nunitoNunito
ralewayRaleway

Locales

Set the locale option to change the language of labels, placeholders, and error messages:

const teya = await initTeyaBlocks('session-token', {
  locale: 'de-DE',
});

Supported locales: en-GB (default), en-US, de-DE, es-ES, fr-FR, it-IT, pt-PT, da-DK, fi-FI, pl-PL, hr-HR, hu-HU, cs-CZ, is-IS, sk-SK.