---
id: docs.save-a-card
title: "Save a card"
canonical: https://docs.secureprocessing.app/save-a-card
last_verified: 2026-11-06
---

# Save a card

Store a card with the customer’s consent and charge it again later.

> **Warning:** **In beta.** Saving a card and charging it later both work, and are tested end to end. What is not yet settled is a permission the bank grants per merchant, and whether the bank’s live system accepts a merchant-initiated charge as we send it: it works in the bank’s sandbox, and has not yet been proved in production. Expect it to work; tell us if a charge is refused, because that is a question for the bank rather than for your code.

## Ask to save it

Cards belong to a customer, so create one first. Then set `setup_future_usage` on the payment the customer is making now.

```ts
const customer = await sp.customers.create({
  email: "diver@example.com",
  name: "Reuben Arnold",
});

const intent = await sp.paymentIntents.create({
  amount: 6000,
  currency: "usd",
  customer: customer.id,
  setup_future_usage: "off_session",
});
```

When the payment succeeds you get a `payment_method.attached` event, and the card appears on the customer.

## Charge it later

```ts
const methods = await sp.customers.listPaymentMethods(customer.id);
const card = methods.data[0];

await sp.paymentIntents.create({
  amount: 4500,
  currency: "usd",
  customer: customer.id,
  payment_method: card.id,
  off_session: true,
  confirm: true,
});
```

A charge to a saved card is always taken in full. Combining `confirm: true` with `capture_method: "manual"` is refused with a `400` before anything is created: the bank does not allow a hold on a charge the cardholder is not present for. To hold money, send the customer to a [checkout session](https://docs.secureprocessing.app/preauthorize-and-capture.md).

`confirm: true` needs both `customer` and `payment_method`. Without them it is also refused with a `400` (`param: customer`, or `param: payment_method` if no card was given) before anything is created — no payment intent and no `payment_intent.created` event is left behind.

## It can still be refused

A card issuer can decline a charge the cardholder is not present for. A decline comes back as a `402` with `card_declined` and a `decline_code`, and the payment intent stays at `requires_payment_method`. The issuer may ask for authentication instead. When that happens the payment reaches `requires_action` with a URL in `next_action` — you will need the customer back to complete it. Build for that rather than assuming a saved card always works.

And it can come back `processing`. That means the charge was sent and the bank’s answer has not reached us yet — the card may already have been charged. Do not charge it again: the payment settles on its own within minutes, to `succeeded` or `requires_payment_method`, and you get the webhook for whichever it is.

## What we hold

Never a card number. What we store is a token from the bank, the masked number, the brand and the expiry — enough to show the customer which card it is, and nothing that could be used anywhere else.
