Developers › Payments
Save a card
Verified 6 November 2026Explain this to your team →
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.
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
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.
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.