Recurring Payments and Card Tokenization: How Auto-Billing Works
Basics4 minInnovialabs editorial team
In short
On the first payment the provider stores the card and gives the store a token; the store then charges that token on schedule, while the provider and bank check that the customer has consented to such charges.
How it works in brief
A recurring payment is a charge to a customer's card without the customer taking part at the moment of payment: monthly for a subscription, after each box ships, or when a balance runs low. To do that the card has to be "remembered", but storing card numbers yourself is off-limits for most stores because of strict PCI DSS requirements.
So stores use tokenization: card data stays with the payment provider, and the store receives a token, a random string that means nothing outside the "this store plus this provider" pairing. A stolen token is useless without the store's API credentials.
Lifecycle: from first payment to auto-billing
- First payment with consent. The customer enters the card in the provider's form, passes 3-D Secure or an SMS check, and agrees to future charges.
- Card saved. The provider returns a token and usually a card mask (last digits, expiry date) to display in the customer's account.
- Storage. The store saves the token next to the subscription: plan, amount, next billing date.
- Merchant-initiated charge. On the due date the store's server calls the provider's API with the token and amount. These are MIT (merchant-initiated transactions): the customer is not involved and repeat 3-D Secure is usually not required.
- Result. The provider returns success or an error code, and the store updates the subscription status and notifies the customer.
A simplified charge request looks like this:
{
"token": "tok_7f3a9c...",
"amount": 99000,
"currency": "UZS",
"order_id": "sub-1042-2026-11",
"description": "Subscription, November"
}
order_id must be unique per attempt. It protects against double charges when a request is resent.
When a charge fails
Declines are a normal part of recurring billing. What matters is telling the reasons apart:
| Reason | What to do |
|---|---|
| Insufficient funds | Retry later on a schedule, warn the customer |
| Card blocked or token revoked | Do not retry, ask for a new card |
| Card expired | Ask the customer to update the card; some providers offer card updater services |
| Technical error | Retry shortly, but check the transaction status first |
Good practice is dunning: a series of retries with pauses plus emails or messages to the customer. The subscription is not cut off immediately but moves to a "payment pending" state for a limited time. Retrying too often on the same decline can lead to penalties from the bank or provider, so cap the number of attempts.
Customer consent: what is required
Auto-billing without clear consent leads straight to chargebacks and account blocks. Before the first payment the customer should see:
- the amount and frequency of charges;
- the date of the first charge, especially after a free trial;
- how to cancel, and cancelling should be no harder than signing up;
- what happens if the price changes.
It is also worth:
- sending a reminder before charging, especially when a trial ends;
- offering a "remove card" button in the account;
- keeping a consent record: date, terms shown, IP or session ID.
Examples: boxes and services
Subscription box (coffee, pet food, cosmetics). Billing follows the packing cycle: the charge runs a few days before shipping so that a decline can pause packing in time. Letting customers skip a month helps.
Service or SaaS. A fixed amount monthly or yearly. Mid-cycle upgrades and downgrades and correct proration matter here.
Balance top-up. The charge fires on an event, for example when a balance drops below a threshold. A limit is essential here so a logic bug cannot overcharge.
Common mistakes
- Storing card numbers in your own database instead of tokens.
- Not making requests idempotent and getting double charges.
- Retrying endlessly after a "card blocked" decline.
- Hiding the cancel button.
- Ignoring the provider's webhooks and relying only on the request response.