Charging Platform Fees
Charging Platform Fees
Many platforms sit between a customer and a merchant and earn a fee on each transaction — a taxi platform where riders pay drivers and the platform keeps a percentage, a food-delivery platform that charges restaurants a commission on every order, or a platform for sporting clubs that takes a cut of the subscriptions each club charges its players.
There are two ways to model this on Synapto.
Two approaches
- If you hold the regulatory approvals to operate a fund pool, or are a merchant of record with supplier or sub-contractor beneficiaries, you can use Synapto Flow. Your platform becomes the merchant of record (and so is subject to the risk of chargeback), collects the customer's payment, and a charge split allocates the merchant's share while your platform automatically retains the fee. The fee comes straight out of the payment flow — you never bill the merchant separately.
- If you don't hold those approvals, or your merchants are not suppliers or sub-contractors, use the approach in this guide. Each merchant is the merchant of record for its own customers' payments — so the merchant, not you, bears the chargeback risk — and the funds settle to the merchant; your platform never holds them. You orchestrate those charges with your platform API key, then bill each merchant separately for the fee you've earned.
Not sure which model fits you? Contact us — the right choice depends on your regulatory permissions and how your merchants relate to your platform.
The rest of this guide covers the second approach.
How it works
Every API call in this model is made with your platform API key — you act on behalf of your child accounts throughout. There are three kinds of party:
- Merchants are independent accounts under your platform — the drivers, restaurants or clubs you serve. Each is the merchant of record: its own MID collects what its customers pay, and it bears the chargeback risk. A merchant is also a customer on your platform account — linked with
backing_account— and that second hat is how you bill it your fee. - End customers are customers on your platform account, so the same person can be served by many merchants without being duplicated.
Two separate money movements result:
- A merchant charges an end customer. You raise the charge on the merchant's behalf — with your platform API key, creating the charge under the merchant's account. Funds collect via the merchant's MID, so it is the merchant of record and receives the money. Your platform never holds it.
- You charge the merchant a platform fee. Off the back of movement 1, you bill the merchant's customer record. This one settles to your platform account, via your MID — it's your revenue.
LEG 1 — a merchant charges an end customer (you orchestrate)
platform key ──create invoice under acct_MERCHANT──▶ End customer
(customer on your platform account)
End customer pays ──▶ funds settle via the MERCHANT's MID (merchant of record)
│
└── payment_intent.succeeded / invoice.paid ──▶ your platform
LEG 2 — you charge the merchant your fee (off the back of Leg 1)
Platform calculates its fee, then
platform ──charge / invoice──▶ Merchant (customer on your account, backing_account)
collected via YOUR platform MIDThe flow, end to end:
- Onboard each merchant — Synapto provisions its independent account offline; you create a customer record on your account to bill fees to.
- Grant servicing so merchants can bill customers that live on your platform account.
- Save a payment method for each merchant, so you can charge it your fees.
- Charge the end customer on the merchant's behalf.
- Charge the merchant your fee — work it out from the amount you charged the end customer, then collect per transaction or batched.
Setup — once per merchant
Do steps 1–3 once, when you bring a merchant onto your platform.
Step 1 — Onboard the merchant
Each merchant needs two Synapto resources: its independent account (provisioned offline by Synapto) and a customer record on your platform account (created by you) that you bill fees to.
The independent account — provisioned offline. You don't call createAccount for merchants. Because each is a merchant of record, it goes through full identity verification and underwriting, which Synapto runs directly with the merchant. You share your merchants with Synapto, which provisions an independent account (acct_...) for each and boards it for card payments.
Knowing when a merchant is ready to charge. Boarding completes asynchronously, so listen for account.updated rather than waiting on a hand-off. Its payments capability starts in PENDING and moves to ACTIVE once boarding finishes; the event carries the account, so you can watch for that transition and only start charging on the merchant's behalf once it's ACTIVE. One endpoint on your platform account covers every merchant — sub-account events are delivered to the parent too (see Parent account events).
Onboarding a batch of merchants to begin with? Synapto can also provide a CSV mapping each merchant to its account ID, so you can seed your records in one go instead of assembling them from events.
Either way, keep the merchant → acct_... mapping — you'll use these IDs throughout the rest of this guide.
The customer record — created by you. For each provisioned merchant, create a customer on your own platform account so you can bill it your fees:
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/customers \
-H "Authorization: Api-Key $API_KEY" \
-H "Idempotency-Key: YOUR_UNIQUE_KEY" \
-H "Content-Type: application/json" \
-d '{
"customer": {
"backing_account": "accounts/acct_MERCHANT",
"full_name": "Northside Services Ltd",
"email": "[email protected]",
"phone": "+447700900000",
"address": {
"address_lines": ["1 High Street"],
"locality": "London",
"postal_code": "SW1A 1AA",
"country_code": "GB"
}
}
}'Link the customer to the account with
backing_account. Set it to the merchant's independent account (accounts/acct_MERCHANT) so the customer record formally represents that account — this is the canonical link between the merchant's two resources, so you always know which account a fee invoice belongs to. Synapto then derives the customer'stypeasINTERNAL(a customer backed by a child account) rather thanEXTERNAL(an ordinary end customer). A few rules apply: the referenced account must be a direct child of your platform account, each child account can back only one customer, andbacking_accountis immutable after creation — so set it when you create the customer.
The full postal address and contact fields shown above are required if you plan to bill by Direct Debit; for card billing, name and email are enough. See Customers.
Step 2 — Let merchants bill your end customers
Because your end customers live on your platform account but the merchant is the one billing them, each merchant needs permission to invoice your customers. Grant it by listing the merchant accounts in your platform account's capabilities.billing.invoice.servicing_accounts — this is the Invoicing on Behalf of Another Account grant, made by the account that owns the customers (your platform):
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT \
-H "Authorization: Api-Key $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"account": {
"capabilities": {
"billing": {
"invoice": {
"servicing_accounts": ["accounts/acct_MERCHANT_A", "accounts/acct_MERCHANT_B"]
}
}
}
}
}'The grant replaces the whole list each time, so include every merchant that should be able to bill your customers. Listed accounts must belong to your account tree (your merchants do, as your children) and can't include the platform account itself. See Invoicing on Behalf of Another Account for how to revoke a grant and the full set of RPCs it unlocks.
Step 3 — Save the merchant's payment method
You prompt the merchant to create a saved payment method that you then use when charging them fees.
The simplest way is a checkout session in SETUP mode — Synapto hosts the whole page, so there's no frontend work. Pass mode: SETUP and the merchant's customer, and omit amount (nothing is charged — the session saves a payment method for future use):
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/checkout-sessions \
-H "Authorization: Api-Key $API_KEY" \
-H "Idempotency-Key: YOUR_UNIQUE_KEY" \
-H "Content-Type: application/json" \
-d '{
"mode": "SETUP",
"customer": "accounts/acct_YOUR_ACCOUNT/customers/cus_MERCHANT",
"success_url": "https://example.com/fees/setup-complete",
"cancel_url": "https://example.com/fees"
}'Redirect the merchant to the url in the response. On the hosted page they save whichever method your account has enabled — a card or a UK Bacs Direct Debit mandate. To offer only one, restrict the rails with payment_method_types.
Both instruments work identically at billing time: a Direct Debit mandate is a saved payment method, used just like a saved card. Direct Debit is well suited to lower-cost, recurring collection like weekly platform fees, but is beta and UK-only.
In
SETUPmodecustomeris required, and the saved payment method is bound to that customer — it can't later be charged for any other customer. That's exactly what you want here: the method belongs to the merchant you'll bill.
Prefer to embed the form in your own UI instead of redirecting? Create a setup intent and collect the method with the JS SDK — see Saving Card Details for cards or Direct Debit for mandates.
On each transaction
Repeat steps 4–5 for every transaction.
Step 4 — Charge the end customer
When the service is delivered, charge the end customer on the merchant's behalf, using your platform API key. The charge is always created under the merchant's account (acct_MERCHANT in the path) — that's what puts the merchant on record for the payment, so funds collect via its MID and settle to it, never to your platform.
The usual way is an invoice, which is what the rest of this step walks through.
First, create the end customer — on your platform account, not the merchant's:
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/customers \
-H "Authorization: Api-Key $API_KEY" \
-H "Idempotency-Key: YOUR_UNIQUE_KEY" \
-H "Content-Type: application/json" \
-d '{
"customer": {
"full_name": "Sam Taylor",
"email": "[email protected]"
}
}'No backing_account this time — that field is only for the merchant customer records in Step 1, so this customer's type comes back as EXTERNAL.
Do this once per end customer, not once per transaction. Because the record lives on your platform account rather than a merchant's, one person is one customer however many merchants charge them: merchant A can invoice Sam this week and merchant B next week, both pointing at the same cus_..., with no duplicate records and a single view of that customer across your platform.
Then create the invoice under the merchant's account, pointing customer at it:
curl -X POST https://api.synaptopay.com/v1/accounts/acct_MERCHANT/invoices \
-H "Authorization: Api-Key $API_KEY" \
-H "Idempotency-Key: YOUR_UNIQUE_KEY" \
-H "Content-Type: application/json" \
-d '{
"customer": "accounts/acct_YOUR_ACCOUNT/customers/cus_END_CUSTOMER",
"currency": "GBP"
}'Creating the invoice under the merchant's account makes it owned by the merchant, so its funds collect via the merchant's MID — it is the merchant of record and receives the money, exactly as if it had billed the customer itself. The customer still lives under your platform account, which is why the merchant needs the servicing grant from Step 2; without it, the call is rejected.
Then add line items, finalize, and collect it — send the end customer the hosted_invoice_url from the finalize response, where they pay on Synapto's hosted page (the payment method is created under the merchant's rail at that point). See Invoices. The invoice belongs to the merchant (accounts/acct_MERCHANT/invoices/in_...), so address follow-up calls there.
You don't need an invoice. You can also charge the end customer with a checkout session or a payment intent created under
acct_MERCHANT— useful for in-app or web checkout. With the JS SDK, initialise it with the merchant's publishable key, since the intent is owned byacct_MERCHANT.
Autopay caveat. You can't auto-charge a card the end customer saved with your platform against this invoice: the invoice is owned by the merchant, but a saved method lives under your platform account (its own MID / Bacs SUN), and a credential can't be charged across accounts.
Step 5 — Charge the merchant your fee
Work out your fee from the amount you charged — you raised the end-customer charge, so you already know it. Synapto doesn't track pending fees for you — you record what each merchant owes and charge it.
When to charge it. Take your fee either:
- at the same time as you charge the end customer; or
- when the end customer actually pays — your platform receives the merchant's
payment_intent.succeededon a single endpoint on your own account (sub-account events are delivered to the parent — see Parent account events).
Invoice the merchant. Bill the fee as an invoice on your own account, against the merchant's customer record, and pay it with their saved payment method. Create it, add the fee as a line item — or multiple line items if there are several fees or platform charges — then finalize and pay:
# 1. Create the fee invoice. Every call below carries an Idempotency-Key keyed to
# the transaction, so a retry after an ambiguous response never duplicates the fee.
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/invoices \
-H "Authorization: Api-Key $API_KEY" \
-H "Idempotency-Key: fee_txn_12345_create" \
-H "Content-Type: application/json" \
-d '{
"customer": "accounts/acct_YOUR_ACCOUNT/customers/cus_MERCHANT",
"currency": "GBP"
}'
# 2. Add the fee as a line item
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/customers/cus_MERCHANT/invoice-items \
-H "Authorization: Api-Key $API_KEY" \
-H "Idempotency-Key: fee_txn_12345_item" \
-H "Content-Type: application/json" \
-d '{
"item": {
"invoice": "accounts/acct_YOUR_ACCOUNT/invoices/in_INVOICE_ID",
"display_name": "Platform fee — transaction #12345",
"currency": "GBP",
"unit_amount": "1000",
"quantity": "1"
}
}'
# 3. Finalize, then pay against the merchant's saved payment method
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/invoices/in_INVOICE_ID:finalize \
-H "Authorization: Api-Key $API_KEY" \
-H "Idempotency-Key: fee_txn_12345_finalize"
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/invoices/in_INVOICE_ID:pay \
-H "Authorization: Api-Key $API_KEY" \
-H "Idempotency-Key: fee_txn_12345_pay" \
-H "Content-Type: application/json" \
-d '{ "payment_method": "accounts/acct_YOUR_ACCOUNT/payment-methods/pm_MERCHANT_METHOD" }'The invoice is on your own account, so it's paid via your platform's MID — the fee is your revenue. A card is charged synchronously and the invoice comes back PAID; a Direct Debit is submitted to Bacs and settles over a few working days, so treat it as collected only on invoice.paid / payment_intent.succeeded, and watch for charge.failed.
Batch it instead, if you prefer. Rather than finalizing an invoice per transaction, keep one open invoice per merchant and add a line item per transaction as it completes, then finalize and pay that invoice on a schedule — say weekly.
Refunds
There is no automatic link between a merchant's charge to an end customer and the fee you charge the merchant — you reconcile the two yourself. If a merchant refunds a customer, decide whether to waive the fee for that payment. If you haven't collected it yet (batching, not yet invoiced), drop or reduce it before you bill. If you've already charged it, refund the fee.
Applying tax
Platform fees are usually taxable. You can attach a tax rate to the fee invoice or to individual line items so it shows VAT correctly.
Next steps
- Independent Accounts — onboarding your merchants as merchants of record
- Invoicing on Behalf of Another Account — the servicing grant that lets merchants bill your customers
- Saving Card Details / Direct Debit — saving the merchant's payment method
- Invoices — the full invoice lifecycle, tax, and voiding
- Collect Webhooks — tracking merchant boarding with
account.updated, calculating fees frompayment_intent.succeeded, and reconcilinginvoice.paid - Synapto Flow Overview — the fund-pool alternative, if you hold the regulatory approvals
Updated 5 days ago