Skip to main content

What the fee is

There is no single rate. The processing fee on a charge depends on the payment method and the currency the customer paid in, and it can be set per account, so two accounts on the same platform taking the same payment can be charged differently. Some corridors are a percentage, some add a fixed amount on top, and some cap the percentage. Because of that, the fee is something you read rather than something you compute. Every payment reports the fee that was actually applied to it: If you need the rates that apply to your own account before you take a payment, see Fees for the standard rates or ask your Bachs contact about account-specific pricing. An NGN virtual account deposit uses its own rate of 1.5%, capped at NGN 300. Sizing a platform fee against a guess is the mistake this section exists to prevent: on a destination charge, your cut is what our fee comes out of first.

Two separate questions

Every charge carries a processing fee, and two independent questions decide what happens to it:
  • Who bears it. Whether the account absorbs the fee or it is added on top for the customer.
  • Who collects it. Whether Bachs takes the fee out of the charge, or your platform pays it out of its own balance.
These do not interact. Bearing the fee decides whose money it is. Collecting the fee decides who takes it out of the charge. An account can absorb the fee while Bachs collects it, or absorb the fee while your platform collects it, or any other combination. Keep them separate when you reason about a payout, because the two settings answer different questions.

Who bears the fee

An account has a fee_handling setting with two values: account_pays_fee, where the fee comes out of the account’s own proceeds, and customer_pays_fee, where the fee is added on top and the customer pays it. Every checkout the account creates inherits this setting by default. You set it on the account, either at creation or later, with POST /v1/accounts/{account_id}. The write field is fee_preference, with values org_pays (the account bears the fee, stored as account_pays_fee) and customer_pays (stored as customer_pays_fee). The read exposes it as fee_handling; the write takes fee_preference. Send X-Account-Id to set it on a connected account, or call it as the account itself. To override it for a single checkout, send customer_bears_fee on POST /v1/checkout-sessions: true charges the customer the fee on top for that checkout, false has the account absorb it, whatever the account’s default is. Omit it to inherit fee_handling.
On a direct charge, it is the account’s own fee_handling that applies, since the charge is the account’s. On a destination charge, it is your platform’s fee_handling that applies instead, since your platform is the merchant of record for that charge. So to make the platform absorb the fee on your destination charges, set fee_preference: org_pays on your platform account. What follows is a different setting entirely.

Who collects the fee

Separately from who bears the fee, each account has a fees_collector setting with two values: bachs, the default, and platform. It is per account, not a setting on your platform itself: an account with no parent has no platform to absorb anything for it, whatever this setting would otherwise say. You set it with responsibilities.fees.collector when you create that account, sent to POST /v1/accounts. It is returned on every read of the account, nested the same way: responsibilities.fees.collector. responsibilities.fees.collector can only be set at creation. There is no update path for it: once an account exists, this setting is fixed for its lifetime. Changing fee posture mid-relationship would retroactively disagree with ledger history that has already settled under the old one. With fees_collector set to bachs, the processing fee is taken out of the charge, the same way it is for any account with no platform. Whoever bears the fee, per the setting above, pays it there. With fees_collector set to platform, your platform pays the fee on that account’s charges out of its own balance instead of taking it from the charge. The account’s payout is correspondingly larger: it keeps the full amount it would otherwise have had the fee deducted from, because your platform absorbed that cost on its behalf. Your platform is subsidising that account’s processing costs. Your platform paying the fee applies to direct charges only. On a destination charge, the fee comes from the charge regardless of fees_collector, because the account being paid is the counterparty to that movement.

The fallback

fees_collector: platform asks your platform’s balance to cover the fee on every charge it applies to. That debit is guarded: if the balance cannot cover it, the debit fails and the fee is taken from the charge instead, the same as if fees_collector were bachs. A charge with fees_collector set to bachs takes the fee from the charge. Set to platform with enough balance, the fee comes from the platform's balance. Set to platform without enough balance, the guarded debit fails and falls through to taking the fee from the charge. A charge with fees_collector set to bachs takes the fee from the charge. Set to platform with enough balance, the fee comes from the platform's balance. Set to platform without enough balance, the guarded debit fails and falls through to taking the fee from the charge.
Settlement does not fail when the platform’s balance can’t cover the fee. The fee is recognized either way, from the platform’s balance when there’s enough, from the charge when there isn’t, and settlement completes the same in both cases. This fallback happens silently: nothing on the charge or the checkout flags that it occurred. fee_paid_by, described next, is the only way to detect it after the fact.

Knowing which happened

The payment carries fee_paid_by, with two values: merchant, meaning the fee came out of the charge, and platform, meaning your platform’s balance covered it. It reports what actually happened when the charge settled, rather than what was configured beforehand, because the fallback means the outcome isn’t known until then. That timing is visible in the field. fee_paid_by is null until the charge settles, which on most rails is days after the customer paid. A charge reading succeeded has been paid; it has not necessarily settled, and until it does there is no answer to report. Poll it after the charge’s settlement date, or read it when the payment’s settlement details are populated, rather than treating null as a third outcome. It is available on both the payment and the payments list. fee_paid_by is the only way an account can tell why two identical sales paid out differently. Two charges with the same fees_collector setting can still resolve to different values of fee_paid_by if the platform’s balance covered one and not the other. On a destination charge, fee_paid_by never reads platform: the fee always comes from the charge there, for the same reason fees_collector: platform does not apply to that shape.

The drain

A platform that creates accounts with fees_collector set to platform and takes no platform fee on their charges has configured a pure cost centre for those accounts. Every one of their charges debits the platform’s own balance to cover the processing fee, and nothing about that arrangement puts money back in. The balance only goes down. Once it empties, the fallback described above starts firing: charges continue to settle, but the fee shifts back to being taken from the charge, and fee_paid_by starts reading merchant where it previously read platform. Nothing announces the transition. A platform running this configuration has to watch its own balance to know when the subsidy stops.

Next steps