# Saved Cards

Inline Checkout supports two saved-card flows:

- Save a card during a card payment with `paymentForm()` or `paymentCard()`.
- Charge a previously saved card with the headless `savedCardPayment()` SDK method.


Saved cards are represented by a `cardId`. Store that token against your own customer record after you receive it from xMoney, then use it for later payments.

## Requirements

To save and reuse cards, your order payload should include:

1. A stable `customer.identifier` that maps to the shopper in your system.
2. `saveCard: true` when you want xMoney to create a reusable card token.
3. A backend process that stores the returned `cardId` only after server-side confirmation.


Confirm tokens server-side
Do not rely only on browser callbacks to store or activate a saved card. Use your webhook or another trusted server-side confirmation path before making the card available for future orders.

## Save a card during checkout

Enable saved cards in the card configuration.

```javascript
const paymentForm = await window.XMoney.paymentForm({
  container: 'xmoney-payment-form',
  publicKey,
  orderPayload,
  orderChecksum,
  card: {
    savedCards: {
      enabled: true,
      optInVisible: true,
    },
  },
  onPaymentComplete(transaction) {
    handlePaymentResult(transaction)
  },
})
```

`savedCards.enabled` controls whether returning customers can see available saved cards in the form. `savedCards.optInVisible` controls whether the shopper can see the save-card choice when entering a new card.

If your business flow requires saving a card, set `saveCard: true` in the server-generated order payload and make sure the customer consent text is clear in your checkout UI.

```json
{
  "publicKey": "pk_test_your_key",
  "customer": {
    "identifier": "customer-123",
    "email": "customer@example.com"
  },
  "order": {
    "orderId": "order-123",
    "type": "purchase",
    "amount": 4999,
    "currency": "EUR",
    "description": "Online order"
  },
  "cardTransactionMode": "authAndCapture",
  "saveCard": true
}
```

## Read the saved `cardId`

When tokenization succeeds, the final transaction data can include a `cardId`.

```json
{
  "externalOrderId": "order-123",
  "transactionId": 987654,
  "transactionStatus": "complete-ok",
  "cardId": 12345,
  "customerId": 67890
}
```

Store the `cardId` with the matching shopper only after you validate the final transaction status on your backend.

## Pay with a saved card from the form

When `card.savedCards.enabled` is true and saved cards are available for the customer, the embedded form can render them. The shopper can select a saved card and submit from the same secure component.

```javascript
await window.XMoney.paymentCard({
  container: 'payment-card',
  publicKey,
  orderPayload,
  orderChecksum,
  card: {
    savedCards: {
      enabled: true,
      optInVisible: false,
    },
  },
  onPaymentComplete(transaction) {
    handlePaymentResult(transaction)
  },
})
```

Use this when you still want xMoney to own the saved-card selection UI.

## Headless saved-card payment

Use `XMoney.savedCardPayment()` when your application renders its own saved-card list and you only need the SDK to trigger the secure payment. This method does not render a visible UI.

```javascript
const savedCardPayment = await window.XMoney.savedCardPayment({
  publicKey,
  orderPayload,
  orderChecksum,
  onReady() {
    enableSavedCardButtons()
  },
  onPaymentProcessing(isProcessing) {
    setSavedCardLoading(isProcessing)
  },
  onPaymentComplete(transaction) {
    handlePaymentResult(transaction)
  },
  onError(error) {
    showPaymentError(error?.message || 'Saved card payment failed')
  },
})

document.querySelectorAll('[data-card-id]').forEach((button) => {
  button.addEventListener('click', () => {
    savedCardPayment.pay({
      cardId: Number(button.dataset.cardId),
    })
  })
})
```

The SDK creates a hidden iframe automatically when no `container` is provided. Call `destroy()` when the saved-card checkout UI is no longer active.

```javascript
savedCardPayment.destroy()
```

## Updating orders

For visible widgets, use `updateOrder()` when cart totals change.

```javascript
await paymentForm.updateOrder({
  orderPayload: nextPayload,
  orderChecksum: nextChecksum,
})
```

For `savedCardPayment()`, create a new instance for each new order payload before calling `pay({ cardId })`.

## 3DS for saved cards

Saved-card payments can still require 3D Secure authentication. The SDK handles the 3DS modal and final callback in the same way as new-card payments. See [3DS and payment results](/guides/checkout/inline-checkout/3ds-results) for the complete flow.

## Related guides

- [Save card on payment](/guides/payments/save-card-on-payment)
- [Website integration](/guides/checkout/inline-checkout/web-integration)
- [Custom layouts](/guides/checkout/inline-checkout/custom-layouts)