Accept with a saved card
Once a customer has paid once, you have a card token you can reuse — no hosted page, no redirect, just a server-to-server charge.
When a customer completes a payment and chooses to save their card, MintCash delivers a card token on the payment.succeeded webhook. The token represents that saved card. Use it to charge the same customer again without redirecting them anywhere.
When to use it
- One-tap re-orders for returning customers
- Upgrades or upsells from inside your product
- Programmatic charges (no human in the loop)
If the customer needs to see and confirm a card, use the HPP flow instead.
The flow
No redirect. The customer doesn't see anything until you tell them about it.
Implementation
The request shape is almost identical to the HPP flow — but you include cardToken and omit returnUrl:
const r = await fetch("https://sandbox.mintcash.me/payments", {
method: "POST",
headers: {
Authorization: `Basic ${btoa(`${publicKey}:${secretKey}`)}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
externalId: orderId,
amount: 14.99,
currency: "USD",
cardToken: savedCardToken, // from a previous payment's webhook
customer: {
externalId: customerId,
email: customer.email,
name: customer.name,
},
}),
});
const payment = await r.json();No redirectUrl is returned on the token flow. The payment proceeds directly to either succeeded or failed (sometimes via pending if the provider needs reconciliation).
Where the token comes from
When the customer ticks "save my card" on the payment page and the charge succeeds, the payment.succeeded webhook's data.card object carries the token:
{
"card": {
"brand": "mastercard",
"first6": "555544",
"last4": "0000",
"mask": "555544******0000",
"expMonth": "12",
"expYear": "2030",
"saved": true,
"token": "a1b2c3d4-..."
}
}card.token is non-null only when card.saved is true — that is, when the customer actually consented to saving the card. Store the token alongside your customer record. Card metadata (brand, last4, expiry) is non-sensitive — safe to display in your dashboard so the customer recognises which card you'll charge.
Whether the save-card option is offered is controlled at two levels:
- Account level — card saving is enabled per merchant (on by default). Your account manager can turn it off.
- Per payment — pass
saveCardEnabled: falseonPOST /paymentsto hide the option for a specific payment, orsaveCardEnabled: trueto show it even if your account default is off. Omit the field to inherit the account setting.
Apple Pay and Google Pay payments are never saved — tokens only come from card payments.
Tokens are merchant-scoped
A cardToken belongs to the merchant who created it. Using another merchant's
token — or an unknown, revoked, or expired one — returns 400 invalid_argument ("Card token is invalid, expired, or not found"). The raw
card details are never exposed — that's by design.
What to test
- Charge with a valid token — should hit
payment.succeededdirectly - Charge with an unknown token —
400 invalid_argument("Card token is invalid, expired, or not found") - Charge with an inactive/expired token — same error
- Provider network failure — should hit
503 unavailable - Idempotent retry on the same
externalId— second call returns the original payment - A payment with
saveCardEnabled: false— the save-card option doesn't appear and the webhook'scard.savedisfalse