Mintcash
Guides

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

Rendering diagram…

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: false on POST /payments to hide the option for a specific payment, or saveCardEnabled: true to 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.succeeded directly
  • 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's card.saved is false