Transaction splitting
On this page
Transaction splitting turns one incoming payment into several transactions, one for each offer_id of the checkout's products. You can then track, report, refund and pay out each product line separately. Typical uses:
- insurance companies (MTPL and CASCO products);
- marketplaces (different sellers);
- multi-brand retailers (different product lines);
- service providers (different service types).
How it works
- One payment, several transactions. A successful payment creates one transaction per distinct
offer_id. Its amount is the sum ofunit_price × quantityof the products with thatoffer_id. - Tagged transactions. Each transaction carries its
offer_id, and the payment webhook lists them. - One balance. All the money goes to your Payout account, whatever the
offer_id. - Optional routing. Split routing rules can pay out the transactions of an
offer_idto a separate IBAN, see Split routing rules.
Note
To have payouts routed by offer_id, send should_split: true even when the checkout has only one product or offer. Only then is the transaction tagged with the offer_id and routed by your rules.
Enabling transaction splitting
Both conditions must be met:
- Account feature. Ask support to enable transaction splitting for your account.
- Checkout flag. Send
"should_split": true(a JSON boolean, not the string"true") when you create the checkout.
Products
Give every product an offer_id. As everywhere in the Payment API, amount and unit_price are integers in cents:
{
"amount": 30000,
"currency": "EUR",
"should_split": true,
"products": [
{
"name": "Premium Service",
"offer_id": "PREMIUM",
"unit_price": 10000,
"quantity": 1
},
{
"name": "Basic Service",
"offer_id": "BASIC",
"unit_price": 20000,
"quantity": 1
}
]
}
This checkout pays 300.00 EUR: 100.00 EUR for PREMIUM and 200.00 EUR for BASIC. Products without an offer_id are grouped into one transaction with offer_id null, which cannot be refunded or routed by offer_id.
Important
With should_split: true, the sum of unit_price × quantity over all products must equal the checkout amount.
If a condition is not met, creating the checkout fails with HTTP 400:
| Problem | errors |
|---|---|
The sum of the products differs from amount |
[{"products": "Sum of products (250.00) must equal checkout amount (300.00)."}] |
| No products | [{"products": "Products must be provided when splitting is enabled."}] |
| Splitting is not enabled for your account | [{"shoud_split": "Transaction splitting not available."}] |
Examples of offer_id values by industry:
- Insurance:
MTPL,CASCO - Marketplace:
VENDOR_A,VENDOR_B - Retail:
ELECTRONICS,CLOTHING
Payment webhook
The checkout webhooks of a split payment, such as checkout.succeeded, have a split_transactions list in data, next to payment:
{
"external_id": "order-1001",
"object": "webhook",
"type": "checkout.succeeded",
"data": {
"object": "checkout",
"id": 141447,
"external_id": "order-1001",
"amount": 30000,
"currency": "EUR",
"status": "succeeded",
"is_status_final": true,
"payment": {
"object": "payment",
"status": "successful",
"payment_method": "card",
"fee": 250,
"net": 9750
},
"split_transactions": [
{
"transaction_id": 9001,
"amount": 10000,
"offer_id": "PREMIUM",
"bank_account": "SK3112000000198742637541"
},
{
"transaction_id": 9002,
"amount": 20000,
"offer_id": "BASIC",
"bank_account": null
}
]
},
"nonce": "UzhER2lFOFZCNkNQVmNuNQ",
"signature": "<signature>"
}
The example shows only the fields that matter here; the rest of the checkout object is the same as in other webhooks.
| Field | Description |
|---|---|
transaction_id |
ID of the split transaction |
amount |
Amount of the split transaction, in cents |
offer_id |
The offer_id of its products, or null for products without one |
bank_account |
IBAN of the active routing rule for this offer_id, or null when there is none |
split_transactions is present only when the checkout was created with should_split: true and has a successful payment. fee and net in data.payment refer to the first split transaction only.
Split routing rules
Split routing rules say where the payouts of an offer_id go. They are applied by your scheduled automatic payout, so they need an automatic payout to be set up on your account. They do not affect incoming payments.
The rules cannot be managed through the API or the portal; our team sets them up for you.
Setting up a rule
Contact support or your account manager with these details for each offer_id:
| Detail | Example | Notes |
|---|---|---|
offer_id |
PREMIUM |
Must match the offer_id of your products |
| IBAN | SK3112000000198742637541 |
Where the payouts of this offer_id go |
| Currency | EUR |
Currency of the automatic payout the rule applies to |
| Description | Premium Service Payouts |
Optional. Used as the beneficiary name; without it, your account name is used |
With each automatic payout, Payout sends the net amount of the available transactions of that offer_id, minus their refunds, to the rule's IBAN. The statement descriptor is Split payout: <offer_id>, and the usual withdrawal fee applies.
Transactions without a rule
Transactions whose offer_id has no active rule stay in your general balance. They are paid out by your regular automatic payout or by a manual withdrawal. Any change to the rules goes through our support team.
Refund handling
To refund a checkout created with should_split: true, you must send offer_id. It selects the split transaction to refund.
POST /api/v1/refunds
Content-Type: application/json
Authorization: Bearer <token>
{
"checkout_id": 141447,
"offer_id": "PREMIUM",
"amount": 5000,
"nonce": "<random string>",
"signature": "<signature>"
}
amountis optional, in cents. Without it, the whole remaining amount of the split transaction is refunded. Card payments processed by CardPay and pay-later payments can only be refunded in full.signatureis the SHA-256 ofamount|currency|external_id|iban|nonce|client_secret, encoded as lowercase hex.currencyandexternal_idare those of the checkout, andibanis empty unless you send one. If you leave outamount, sign the full checkout amount, not the remaining amount of the split transaction.
| Problem | HTTP status | Body |
|---|---|---|
Invalid signature |
403 |
{"errors": {"signature": "Is invalid"}} |
offer_id missing |
400 |
{"errors": {"offer_id": "is required for split refund"}} |
No split transaction with this offer_id |
422 |
{"errors": "split_transaction_not_found"} |
The response is the usual refund object; it does not repeat the offer_id:
{
"id": 5821,
"object": "refund",
"amount": 5000,
"currency": "EUR",
"external_id": "order-1001",
"status": "pending",
"metadata": {},
"created_at": 1759744800
}
Good to know
- Splitting works for payment methods that pay the checkout directly, such as cards, online banking and pay later. Payments by bank transfer with payment instructions, which are matched by their reference, are not split.
- Fees are calculated for each split transaction separately: each one is charged the fixed fee and the percentage fee of its own amount.
- Each split transaction stays linked to the original payment; the webhook gives you its
transaction_idandoffer_id. - Split routing rules only affect payouts, not incoming payments.
- Need help? Contact support.
- Questions? Contact sales.
- Service status? status.payout.one.
- LLM? Read llms.txt.