Skip to main content

Saving a Card and Charging It Later

Subscriptions, renewals, installments, and "charge my card next time" all rest on the same four steps. Each one is required. Skipping any of them produces an integration that looks correct — the card saves, the first payment succeeds — and then fails weeks later at the first renewal, when the buyer is gone and the card can no longer be re-collected.

This guide is the order to do them in. The detailed field references live on other pages and are linked at each step.

StepWhat you doWhere
1Confirm the account can charge a saved cardGET /v1/capabilities
2Declare what the stored card is forPOST /v1/sessions
3Collect the buyer's consent as you save the cardyour checkout surface
4Charge the card laterPOST /v1/payment_intents

Step 1 — Confirm the account supports it

Read GET /v1/capabilities and require supported_operations.mit before you build. Branch on the response rather than assuming — what an account supports depends on how it was onboarded.

Step 2 — Declare what the stored card is for

Card networks require the payment that stores a card to say what the card will be used for. Send storedCredentialUse on POST /v1/sessions — the server-to-server call you make when the buyer signals they're ready to pay.

This is a factual statement to the card networks about the agreement you have with the cardholder, so make it from your server, alongside the rest of your session setup — not from code a shopper's browser can reach.

ValueUse it forYour later charge sends
recurringa fixed-interval subscriptionmit.reason: "recurring"
installmenta known number of scheduled payments for one purchasemit.reason: "installment"
unscheduleda card kept on file and charged later with no fixed schedulemit.reason: "unscheduled"

Use the same word in both places. The two vocabularies are identical on purpose, so agreement is a plain string match with no mapping table to consult.

{
"amount": 2999,
"currency": "USD",
"buyerId": "user_8f3a2b",
"buyerEmail": "buyer@example.com",
"storedCredentialUse": "recurring"
}
Omitting the field declares nothing

There is no inferred default. If you omit storedCredentialUse, no declaration is made to the card networks and the payment goes out exactly as it would have before the field existed — even when the buyer consents to saving their card.

Consent looks identical for a subscription and for a card kept on file, so nothing is guessed on your behalf. An undeclared card saves and charges normally, but its later merchant-initiated charges carry no stored-credential lineage and may be declined.

A subscription must declare recurring explicitly.

To confirm your declaration landed, retrieve the session and read storedCredentialUse back. A null you did not expect is a bug in your create-session call, not a cosmetic detail.

Vaulting server-to-server instead?

If you vault through POST /v1/tokens rather than a checkout session, the same declaration is named stored_credential_use — snake_case on that surface, camelCase on session create. Reaching the card networks from there also needs currency and buyer_id on the request plus account-level enablement; when it doesn't, the card still vaults and the response carries a warnings entry instead of an error.

The call that vaults the card must carry setup_for_future_use: "off_session". Without it, the resulting token can never be charged on a merchant-initiated basis.

That gate is about merchant-initiated charges only. A saved card charged while the buyer is present — a one-click repeat purchase — goes through without off-session consent, because the buyer is consenting at that moment. A working one-click flow is therefore not evidence that consent was captured; the first rebill is where you find out.

On the embedded-fields surface, mount the consent element Von Payments ships. You do not have to build the checkbox yourself:

const save = elements.create('save-for-future-use', {});
save.mount('#save-card');
// submit() forwards the buyer's tick as setup_for_future_use: 'off_session'

It starts unchecked and cannot be pre-ticked: under the card networks' stored-credential rules a pre-ticked box is not consent, so the element gives you no way to set one.

submit() sends whatever the mounted element reports. Its options are for the payment intent and the billing address — a consent value passed to submit() is not read. If you render your own consent control instead of mounting the element, use the single-field tokenize({ setupForFutureUse }) call, which does accept it. Pick one route per checkout; do not do both.

Collecting the consent is yours to get right — the element makes it easy, it does not police it. setup_for_future_use is an ordinary optional field on a route that authenticates with the publishable key already shipped in your page. Whatever you send is recorded as the consent, and nothing checks that a buyer ticked anything. Forwarding the result of a control you built yourself is a supported route. Asserting consent you never collected is not: it does not fail here, it fails later at the cardholder's bank, where a stored credential the buyer never agreed to is refused on rebill — and the liability for that is yours.

The consent control needs a session that already has a buyer

There must be someone to vault the card against. If the buyer ticks the box on a session created without a buyer, nothing is saved — and the browser SDK reports frame_consent_requires_buyer rather than failing the payment.

The charge still succeeds, so your page shows success and you only discover the missing card at the first rebill. Create the session with buyerId (or a buyer email) before you render the control, or hide the control on guest checkouts.

Consent is recorded once and cannot be added later

Consent is written at the moment the card is vaulted. No endpoint updates it — there is no PATCH, no PUT, no "promote consent" call. A retry loop built on re-stamping an existing token fails every time.

If a merchant-initiated charge returns payment_method_consent_missing (422), the card must be collected again with consent captured on that same call. See Fixing a missing consent for which endpoint to use for your flow.

There is a second permission, and it is also one-way

setup_for_future_use is permission to charge the card again. allow_redisplay is permission to show it back to the buyer in a later checkout. They are different agreements: a buyer who signs up for a subscription has not agreed to see that card offered as a one-click option on an unrelated purchase months later.

ValueMeaning
alwaysThe card may be shown back to the buyer
limitedSaved for one arrangement — never offer it back as a general saved card
unspecifiedYou did not ask

On the embedded-fields surface you do not send this yourself — the mounted consent element resolves it from the buyer's tick. An unticked box grants nothing (unspecified). A tick grants both permissions, and the display half defaults to always.

Set displayScope: "limited" on the element if the card is being saved for one arrangement and must never be offered back on an unrelated checkout:

const save = elements.create('save-for-future-use', {
label: 'Save my card for my Pro subscription',
displayScope: 'limited',
});

If you reword the label to name a specific arrangement, set limited. Leaving the default claims a broader permission than that buyer actually gave.

If you vault server-to-server instead, send allow_redisplay yourself on the vaulting call — and send unspecified when you did not ask, because that is a real answer and omitting the field is not.

Either way, this permission can only be recorded when the card is first saved. There is no path that adds it later: the buyer isn't present, and permission captured afterwards isn't permission given at save time. A card saved without it can never be shown back to its owner. See allow_redisplay.

Step 4 — Charge the card later

Send the mit block on POST /v1/payment_intents, anchored to the original payment where consent was captured. That page carries the field reference, worked renewal examples in both SDKs, and the chain-validity rules.

You own the rebill loop — the scheduler, the dunning on failure, and the subscription state machine are yours. Von Payments vaults the card and relays the charge.


Constraints worth knowing before you build

Attach a customer to every payment

On an account configured for network tokens, a payment that is not shaped to store a card is refused outright — not downgraded to a normal charge. The payment is shaped to store a card when the session identifies the buyer, through either buyerId or buyerEmail.

Send buyer identity on every payment, not only the ones where you intend to save the card. Observed on a live network-token connection in August 2026: an otherwise identical charge with no buyer attached was rejected with 400, while the same charge with a buyer succeeded and vaulted.

If you declare storedCredentialUse but the charge that stores the card carries no setup_for_future_use, the declaration is discarded and no error is returned. We will not assert an arrangement to the card networks on a signal the buyer never gave.

This is invisible from your side: the card saves, the payment succeeds, and nothing in the response indicates the declaration was dropped. It is the most common cause of a rebill that fails months after an integration was signed off. The read-back in step 2 is what catches it.

Not every connection accepts every arrangement

Matching your declaration to your later mit.reason is necessary but not always sufficient — what a connection will accept is set by your payment provider and the card issuer, not by us. On some connections only a fixed-interval subscription is accepted, and a correctly matched card-on-file pair is still refused.

The measured results and what they mean for your integration are on the payments page: Matching the values is necessary, not always sufficient.

A refusal may not tell you what went wrong

Some request-level faults arrive as an ordinary decline with the issuer's generic "revalidate payment information" advice. A charge you are debugging as a decline may in fact be a malformed request. If declines appear suddenly on a flow that previously worked, re-check the request before assuming the issuer refused it.


Troubleshooting

What you seeMost likely causeGo to
payment_method_consent_missing (422) on a rebillThe card was vaulted without off-session consent, and it cannot be repairedStep 3
Rebills decline; the first payment was fineNothing was declared when the card was storedStep 2
storedCredentialUse reads back nullYour create-session call omitted it, or it was dropped for want of consentStep 2
400 on a charge that works elsewhereNo buyer identity on the sessionAttach a customer
Matched declaration still declinesThe connection does not accept that arrangementConnection support
The consent box is ticked but consent never landsConsent was passed to submit() instead of a mounted elementStep 3

What's next