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.

Why a checkout cannot save a card

Only a card can be charged again with the customer away. So when you ask a checkout to save a card, Bachs checks that it can offer one. If it cannot, the checkout is refused when you create it, with CHECKOUT_CANNOT_SAVE_PAYMENT_METHOD:
This is refused early on purpose. If we accepted the checkout, your customer could pay by bank transfer, and you would only find out there was no card when your first charge failed.
The sandbox offers every payment method, so a flow that works there can fail the first time you use a live key. Your live account only offers what has been approved for it.
Work through these checks in order. 1. Check your live account can take card payments. Call GET /v1/accounts/checkout/settings with your live key. USD_CARD or NGN_CARD in enabled_payment_methods needs "enabled": true. If it is enabled there and you still get this error, cards have not been approved on your account yet. Send your account ID to support@bachs.io and we will check. 2. Check the currency. Cards collect in USD and NGN only. When adaptive_pricing is false, a checkout can only be paid in the currency it is priced in. So a checkout priced in any other currency has no card to offer. Price it in USD or NGN, or turn adaptive pricing on. 3. To save without charging, you need a USD card. An NGN card can be saved only while the customer pays. If your account takes NGN cards and not USD cards, use Save while they pay in step 2, not Save without charging. 4. Check your payment_method_types. If you restrict the checkout, the list must include USD_CARD or NGN_CARD. A checkout restricted to NGN_BANK_TRANSFER cannot save anything. See Restrict payment methods. 5. Do not send a free price. A checkout with price_type: "free" takes no payment, so no card comes out of it. To collect a card without charging, leave the price out, as in Save without charging. If all five check out and you still get this error, send your account ID and the full error response to support@bachs.io.

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