> ## Documentation Index
> Fetch the complete documentation index at: https://docs.overflow.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Checkout best practices

> Recommendations for shipping a robust Checkout integration.

## Show a loading state until `onReady`

Checkout renders a skeleton while it fetches your merchant configuration. Keep any external UI (a page-level spinner, disabled buttons) in a loading state until `onReady` fires.

```javascript theme={null}
checkout.on('onReady', () => {
  document.querySelector('#page-spinner').hidden = true;
});
```

## Gate external CTAs on `onChange.complete`

If you render your own Pay button outside Checkout, disable it until the form validates:

```javascript theme={null}
const payButton = document.getElementById('pay');

checkout.on('onChange', ({ complete }) => {
  payButton.disabled = !complete;
});
```

`complete` flips `true` when every visible field passes validation and a payment method is selected and ready.

## Authorize on the server

The `onSubmit` payload is tokenized and safe to POST from the browser to your server. Do not call the Overflow authorize endpoint directly from the browser: your secret key stays server-side.

```javascript theme={null}
checkout.on('onSubmit', async ({ value }) => {
  const res = await fetch('/api/authorize', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(value),
  });
  const result = await res.json();
  // Show success or failure UI.
});
```

## Handle `onError`

`onError` fires for validation failures and runtime errors. Surface a message to the shopper and let them retry.

```javascript theme={null}
checkout.on('onError', ({ code, message, fieldErrors }) => {
  showBanner(message);
});
```

Per-field errors are also emitted inline; you only need to render your own copy when you want a page-level summary.

## Retry on `UNKNOWN_KID`

If your authorize endpoint returns `400 { code: 'UNKNOWN_KID' }` after a bank submission, call `overflow.invalidateBankFieldEncryptionCache()` and prompt the shopper to submit again. This clears the cached bank field encryption key so the next submit uses the current one.

```javascript theme={null}
if (result.code === 'UNKNOWN_KID') {
  overflow.invalidateBankFieldEncryptionCache();
  showBanner('Please try again.');
}
```

Checkout's own retry path invokes this for you when it recognizes the error, so this pattern is mainly for standalone bank integrations.

## Match your brand

Pass tokens at construction so every section shares your palette, radii, and typography:

```javascript theme={null}
new Overflow('live_pub_...', {
  appearance: {
    size: 'normal',
    variables: {
      colorPrimary: '#0a3783',
      colorBorder: '#ededed',
      borderRadius: '6px',
    },
  },
});
```

Do not set `appearance` on nested Checkout slots. Checkout is themed as a single cohesive unit. See [Theming](/payment-elements/theming/overview).

## Order matters for wallets

If you want wallets prominent, list them first:

```javascript theme={null}
overflow.checkout({
  paymentMethods: ['applePay', 'googlePay', 'card', 'bank'],
});
```

Wallets that are unavailable on the shopper's device are hidden without shifting the order of the remaining methods.

## Accessibility

* Provide an accessible label on your surrounding page container.
* Do not add `tabindex` overrides inside the mount host; Checkout manages focus order.
* The submit button inside Checkout is a real `<button>` with a keyboard-navigable focus ring.

## Test with `test_pub_…`

Use a `test_pub_…` publishable key while you build. Swap to `live_pub_…` for production. The same code paths run on both; only the API environment changes.

## Next steps

<CardGroup cols={2}>
  <Card title="Accept a payment" icon="credit-card" href="/payment-elements/guides/accept-a-payment">
    End-to-end walkthrough with server code.
  </Card>

  <Card title="Handle validation errors" icon="triangle-exclamation" href="/payment-elements/guides/handle-validation-errors">
    Patterns for surfacing validation errors.
  </Card>
</CardGroup>
