<div id="integration-guide">

<div class="ig-section">
<div class="ig-section-header">
<div class="ig-section-icon embedded-checkout"><i class="fas fa-paint-brush"></i></div>
<h2>Customize Embedded Checkout</h2>
</div>
<p class="ig-section-desc">Customise the Checkout Element's appearance using theme presets, CSS variables, custom rules, fonts, and locale settings.</p>

<div class="ig-subsection">
<h3>Checkout Options</h3>
<p>Pass these options when creating the checkout element:</p>

<table class="ig-comparison">
<thead>
<tr>
<th>Option</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr><td><code>appearance</code></td><td><code>object</code></td><td>Theme, variables, and CSS rules for styling</td></tr>
<tr><td><code>disabled</code></td><td><code>boolean</code></td><td>Disable all inputs in the checkout element</td></tr>
<tr><td><code>hideSubmitButton</code></td><td><code>boolean</code></td><td>Hide the built-in submit button (use your own)</td></tr>
<tr><td><code>submitButtonProps</code></td><td><code>object</code></td><td>Customise the built-in submit button (text, amount, style)</td></tr>
<tr><td><code>cardOptions</code></td><td><code>object</code></td><td>Options specific to the card input</td></tr>
<tr><td><code>applePayOptions</code></td><td><code>object</code></td><td>Options specific to the Apple Pay button, including the Apple Pay <code>paymentRequest</code></td></tr>
<tr><td><code>cardContainerStyle</code></td><td><code>object</code></td><td>Inline styles for the card input container</td></tr>
<tr><td><code>applePayContainerStyle</code></td><td><code>object</code></td><td>Inline styles for the Apple Pay button container</td></tr>
<tr><td><code>onTokenRefresh</code></td><td><code>function</code></td><td>Called when the session token needs refreshing - return a new token</td></tr>
</tbody>
</table>
</div>

<div class="ig-subsection">
<h3>Callbacks</h3>
<p>Register callbacks when creating the checkout element:</p>

<table class="ig-comparison">
<thead>
<tr>
<th>Callback</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr><td><code>onReady</code></td><td>Fired when the element is mounted and ready for interaction</td></tr>
<tr><td><code>onChange</code></td><td>Fired when any input value changes</td></tr>
<tr><td><code>onFocus</code></td><td>Fired when an input receives focus</td></tr>
<tr><td><code>onBlur</code></td><td>Fired when an input loses focus</td></tr>
<tr><td><code>onSuccess</code></td><td>Fired on successful payment - receives <code>(response, paymentMethod)</code></td></tr>
<tr><td><code>onError</code></td><td>Fired when a payment error occurs</td></tr>
<tr><td><code>onTokenRefresh</code></td><td>Called when session token needs refresh - <code>() =&gt; Promise&lt;string&gt;</code></td></tr>
</tbody>
</table>
</div>

<div class="ig-subsection">
<h3>Saving a payment method</h3>

```admonition
icon: fas fa-exclamation-triangle
title: Consent comes first
content: 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.
```

<p>Set <code>save_payment_method</code> to <code>true</code> 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 &mdash; nothing changes in how you mount the element.</p>

<p>Two fields work together on the session. <code>save_payment_method</code> is what mints the token; <code>type</code> decides what the customer sees:</p>

<table class="ig-comparison">
<thead>
<tr><th><code>type</code></th><th><code>amount.value</code></th><th>What the element renders</th></tr>
</thead>
<tbody>
<tr><td><code>ACCOUNT_VERIFICATION</code></td><td><code>0</code></td><td>Saves the card without charging it. The submit button shows no price.</td></tr>
<tr><td><code>SALE</code></td><td>The real amount</td><td>Saves the card alongside a real payment, and adds a consent checkbox that gates submission.</td></tr>
</tbody>
</table>

<p>If you send <code>type: ACCOUNT_VERIFICATION</code> with <code>save_payment_method: false</code>, Teya verifies the card but saves nothing.</p>

```javascript
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');
```

<p>The token arrives on the <code>payment.succeeded.v1</code> webhook as <code>data.token_response.token_id</code>, once the payment and the token have both been created successfully. Store it alongside <code>data.payment_method.brand</code> and <code>last4</code> so you can render &ldquo;Mastercard &bull;&bull;&bull;&bull;4065&rdquo; in your UI.</p>

<h4>Saving an Apple Pay card</h4>

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

```javascript
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 */ },
});
```

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

<p>See <a href="/partners/online-payments/saved-payments/overview">Card on File</a> for the consent rules and the wider flow, and <a href="/partners/online-payments/saved-payments/charge">Charge a Saved Payment Method</a> for charging the token.</p>
</div>

<div class="ig-subsection">
<h3>Theme Presets</h3>
<p>Six built-in theme presets are available. Set the <code>theme</code> property in the <code>appearance</code> option:</p>

```javascript
const teya = await initTeyaBlocks('session-token', {
  appearance: {
    theme: 'default',
  },
});
```

<table class="ig-comparison">
<thead>
<tr>
<th>Preset</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr><td><code>default</code></td><td>Standard light theme</td></tr>
<tr><td><code>dark</code></td><td>Dark background with light text</td></tr>
<tr><td><code>minimal</code></td><td>Reduced visual chrome</td></tr>
<tr><td><code>rounded</code></td><td>Larger border radius on inputs</td></tr>
<tr><td><code>compact</code></td><td>Tighter spacing for dense layouts</td></tr>
<tr><td><code>bold</code></td><td>Heavier font weights and borders</td></tr>
</tbody>
</table>
</div>

<div class="ig-subsection">
<h3>Theme Variables</h3>
<p>Override individual CSS variables using <code>appearance.variables</code>:</p>

```javascript
const teya = await initTeyaBlocks('session-token', {
  appearance: {
    theme: 'default',
    variables: {
      colorPrimary: '#0055ff',
      colorBackground: '#ffffff',
      colorText: '#333333',
      borderRadius: '8px',
    },
  },
});
```

<table class="ig-comparison">
<thead>
<tr>
<th>Variable</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr><td><code>colorPrimary</code></td><td><code>string</code></td><td>Primary accent colour (focus rings, active states)</td></tr>
<tr><td><code>colorBackground</code></td><td><code>string</code></td><td>Input background colour</td></tr>
<tr><td><code>colorText</code></td><td><code>string</code></td><td>Input text colour</td></tr>
<tr><td><code>colorDanger</code></td><td><code>string</code></td><td>Error and invalid state colour</td></tr>
<tr><td><code>colorSuccess</code></td><td><code>string</code></td><td>Success state colour</td></tr>
<tr><td><code>colorWarning</code></td><td><code>string</code></td><td>Warning state colour</td></tr>
<tr><td><code>borderRadius</code></td><td><code>string</code></td><td>Input border radius</td></tr>
<tr><td><code>borderWidth</code></td><td><code>string</code></td><td>Input border width</td></tr>
<tr><td><code>borderColor</code></td><td><code>string</code></td><td>Input border colour</td></tr>
<tr><td><code>fontFamily</code></td><td><code>string</code></td><td>Font family for all text</td></tr>
<tr><td><code>fontSizeBase</code></td><td><code>string</code></td><td>Base font size</td></tr>
<tr><td><code>fontWeightNormal</code></td><td><code>string</code></td><td>Normal font weight (default: 400)</td></tr>
<tr><td><code>fontWeightMedium</code></td><td><code>string</code></td><td>Medium font weight (default: 500)</td></tr>
<tr><td><code>fontWeightBold</code></td><td><code>string</code></td><td>Bold font weight (default: 700)</td></tr>
<tr><td><code>spacingUnit</code></td><td><code>string</code></td><td>Base spacing unit used for padding and margins</td></tr>
<tr><td><code>focusRingColor</code></td><td><code>string</code></td><td>Colour of the focus ring around inputs</td></tr>
<tr><td><code>focusRingWidth</code></td><td><code>string</code></td><td>Width of the focus ring</td></tr>
</tbody>
</table>
</div>

<div class="ig-subsection">
<h3>Custom CSS Rules</h3>
<p>For more granular control, use <code>appearance.rules</code> to target specific selectors:</p>

```javascript
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',
      },
    },
  },
});
```
</div>

<div class="ig-subsection">
<h3>Submit Button</h3>
<p>Customise the built-in submit button or provide your own:</p>

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

<div class="ig-subsection">
<h3>Fonts</h3>
<p>Set <code>appearance.fontFamily</code> to one of the built-in options loaded from Google Fonts:</p>

<table class="ig-comparison">
<thead>
<tr>
<th>Value</th>
<th>Font</th>
</tr>
</thead>
<tbody>
<tr><td><code>system</code></td><td>System font stack (default)</td></tr>
<tr><td><code>inter</code></td><td>Inter</td></tr>
<tr><td><code>roboto</code></td><td>Roboto</td></tr>
<tr><td><code>open-sans</code></td><td>Open Sans</td></tr>
<tr><td><code>lato</code></td><td>Lato</td></tr>
<tr><td><code>poppins</code></td><td>Poppins</td></tr>
<tr><td><code>montserrat</code></td><td>Montserrat</td></tr>
<tr><td><code>source-sans-pro</code></td><td>Source Sans Pro</td></tr>
<tr><td><code>nunito</code></td><td>Nunito</td></tr>
<tr><td><code>raleway</code></td><td>Raleway</td></tr>
</tbody>
</table>
</div>

<div class="ig-subsection">
<h3>Locales</h3>
<p>Set the <code>locale</code> option to change the language of labels, placeholders, and error messages:</p>

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

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

</div>
</div>
