Skip to main content
In this guide you’ll save a customer’s card during a checkout, then charge that card later from your server with nobody on a payment page. By the end you’ll have a working flow for the payments a customer agrees to once and you collect many times: a renewal you bill yourself, a usage invoice at the end of the month, a top-up when a balance runs low. A payment taken with the customer away is called an off-session charge, as opposed to an on-session one where they are on a payment page and can answer their bank. The distinction matters: an off-session charge cannot ask the customer to authenticate, so a card whose issuer demands it will be refused rather than prompt anyone. There are two halves, and they happen at different times:
  • Save the card. A customer completes a checkout, and Bachs keeps their card against their customer record.
  • Charge it. Days or months later, you call POST /v1/charges with that customer and an amount.
Saving and charging cards this way is in beta. The behavior below might change, including field names and the shape of the response. Pin your integration to what you test, and check back before you rely on it in production.
If you want Bachs to run the billing cycle for you, use Subscriptions instead. This guide is for when you decide what to charge and when.

Your side of keeping a card

Storing a customer’s card and charging it later carries obligations that are yours, not ours.
  • Tell them, and let them agree. Card network rules require the cardholder’s consent to store their card for later use. Our checkout page states this on the card form, but your own terms, and how you present the choice, are yours to get right.
  • Say what you will charge and when. A customer who agreed to one amount has not agreed to any amount. Be specific about what you will bill and how often.
  • Let them stop it. Give them a way to remove a saved card and to cancel whatever it is paying for.
You are responsible for your own compliance with the laws, regulations and card network rules that apply to you.

Before you start

Everything below uses the sandbox base URL, so you can run the whole flow without moving real money.

Steps

1

Create the customer

A saved card belongs to a customer, and charging it later names that customer by id. Create them first, and keep the cust_ id: you will use it twice.
Already have the customer? Reuse their id and skip this step. See the customer object.
2

Save the card during a checkout

You have two ways to save a card, and they differ only in whether the customer pays at the same time.
Send save_payment_method with a customer and no price. The checkout collects a card, charges nothing, and saves it. Use this when a customer signs up before they owe you anything.
Send the customer to the checkout_url either way. See Accept a payment with Checkout for the full checkout flow.
3

Wait for the card to be saved

The card is saved when the customer finishes the checkout, not when you create it. Bachs sends payment_method.saved when the card is ready to charge.
payment_method.saved
Store the pm_ id against your own record of the customer. You can charge without it, but then you are charging whichever card is their default, and you cannot show them which card you are about to bill.is_default tells you whether this is the card a charge picks when you name none. The first card a customer saves becomes their default.
Do not call POST /v1/charges straight after creating the checkout. The customer has not entered a card yet, and the charge is refused with NO_SAVED_PAYMENT_METHOD.
4

Charge the saved card

Call POST /v1/charges with the customer and an amount. There is no checkout and no page for the customer to visit.
amount is what the card is charged. It is larger than the 29.00 you asked for because this account passes the processing fee to the customer. On an account that absorbs the fee, the card is charged 29.00 and you settle less. See Fees.Leave payment_method out and Bachs charges the customer’s default saved card.
Always send an Idempotency-Key. Without one, a retried request after a timeout charges the customer twice. Key it to the thing you are billing for, not to the attempt. See Idempotency.
5

Confirm the outcome with a webhook

The charge comes back processing, which means the card has been submitted and nobody has told us yet whether it worked. The answer arrives as a webhook.
collection.succeeded
If the card is refused you get collection.failed instead, and the charge ends at failed with amount_paid still "0.00". No money moved and nothing is owed.

Charging in a currency the card does not use

You do not have to bill in the currency the card was saved in. Ask for the amount you are owed and Bachs converts it. Say a customer saved a card that bills in USD, and you invoice in NGN:
Bachs converts 45000.00 NGN into USD at the prevailing rate and charges the card that amount. currency is the currency you are owed and settle in; the card is billed in its own. This is the same conversion a normal checkout does when a customer pays you in their currency.

A declined card is an answer, not an error

POST /v1/charges always answers with a charge. A refused card is an outcome you read from status, never an exception you catch. A refused card returns 201 with a charge like this:
Refused card
So branch on status, and do not rely on the request raising.
Do not treat a 201 as payment received. A charge is processing at that moment and can still fail. Only succeeded means you have the money, and it reaches you as collection.succeeded. Code that fulfils an order on the 201 will ship goods it was never paid for.
Most charges come back processing and settle a few seconds later. Some come back already failed, when the card is refused while your request is still open. Both are normal, and both are the same response shape, so read status rather than assuming which one you got.

Retrying a failed charge

A failed charge is final. To try again, create a new charge with a new Idempotency-Key. Before you retry, consider why it failed. A card refused for insufficient funds may work in three days; a card refused because it expired will never work, and the customer has to save a new one. Send them to a new checkout that saves a card to replace it.
Do not retry a failed charge in a tight loop. Repeated attempts against a refused card can get your account flagged by the card networks.

Test it in the sandbox

The sandbox takes test cards, so you can run the whole flow, save a card and charge it, without moving real money. Any future expiry date and any CVC work. The second one is worth spending time on. A card that saves and then fails is the case most integrations get wrong, because it only goes wrong long after the customer has gone.
Watch for your browser autofilling a card you used earlier. Check the field holds the card you meant before you submit, or you will test the wrong one.

Errors

These are refusals at the request itself, before any card is charged. They are the ones worth handling.
If the request fails with a network error or a 5xx, do not assume nothing happened. The charge may have gone through. Retry with the same Idempotency-Key, which returns the original charge instead of creating a second one.

Next steps