Split 2D Payment
A split payment is a normal payment plus sellers[] — you declare which sub-dealer gets how much of the charge, and Lunixi enforces seller eligibility, computes commission, hold and net per seller leg, and records the full breakdown on the transaction. Use the 2D route when the transaction will not run a 3DS challenge; for the challenge flow see Split 3D Secure Payment.
All amounts are minor units (kuruş): "amount": 25000 = ₺250.00. Note the asymmetry: the top-level amount is an integer, while each seller leg's amountMinor is a string ("15000").
How it works
Eligibility gate
Before any money moves, every seller in sellers[] is checked: is the seller ACTIVE, KYC-complete and payout-configured? Ineligible sellers are rejected according to your multiSellerPolicy.
Split & commission
The gross is split across seller legs; the commission rules you defined resolve each leg's commission, hold and net.
Charge
The card is charged through the core payment pipeline — same routing, failover and idempotency as a regular payment.
Record
Commission, hold and net per seller are written to the transaction record — inspect them any time via GET /marketplace/transactions.
Two ways to take a split payment
| Hosted checkout form | Direct API (2D) | |
|---|---|---|
| Card data | Collected on Lunixi's form — never touches your server | Sent server-to-server (you are in PCI DSS scope) |
| You call | POST /payments/marketplace/intents → form flow | POST /payments/marketplace/2d |
| Split declared | At intent creation | On the charge request |
Hosted form route
POST /payments/marketplace/intents accepts everything a core checkout form intent accepts, plus sellers[] and multiSellerPolicy. The seller legs must add up to the intent's amount (see below). Eligibility is gated and the split computed at intent creation; the buyer then completes the standard hosted form flow, and the charge applies the split. Everything after intent creation — tokenize, BIN/installments, 2D/3D submit — is identical to the core flow, so follow the Checkout Form guide from step 2 onward.
Direct API route
POST /payments/marketplace/2d is a Step-Up endpoint: Bearer token plus Ed25519 request signature, like every money-movement call.
The direct route carries raw PAN. Only use it if your integration is PCI DSS certified — otherwise use the hosted form route above.
Request
curl -X POST https://api-gateway.lunixi.com/api/v1/payments/marketplace/2d \
-H "Authorization: Bearer <access-token>" \
-H "X-Key-Id: <key-id>" \
-H "X-Date: <iso-timestamp>" \
-H "X-Nonce: <nonce>" \
-H "X-Signature: <ed25519-signature>" \
-H "Idempotency-Key: 5c1e6a3e-90bd-4b2f-a1c7-1f0d1c9e2ab4" \
-H "Content-Type: application/json" \
-d '{
"amount": 25000,
"currency": "TRY",
"orderId": "ORD-2026-1042",
"card": {
"cardNumber": "5400000000000005",
"expireMonth": "12",
"expireYear": "27",
"cvcNumber": "123"
},
"sellers": [
{ "subDealerId": "d_8f2a...", "amountMinor": "15000" },
{ "subDealerId": "d_c41b...", "amountMinor": "10000" }
],
"multiSellerPolicy": "ALL_OR_NOTHING"
}'The sellers[] array
| Field | Type | Notes |
|---|---|---|
subDealerId | string | The seller's dealer id — must be ACTIVE and payout-configured |
amountMinor | string | This seller's share, minor units as a string |
scenario | object, optional | Per-request commission scenario override for this leg |
Multiple entries for the same subDealerId (e.g. several basket items from one seller) are aggregated into a single leg.
The sellers' amountMinor values must add up to exactly the request's amount. A lower or higher total is rejected with 400 VALIDATION_ERROR before the card is charged. amountMinor contains digits only ("15000").
multiSellerPolicy
| Policy | Behaviour |
|---|---|
ALL_OR_NOTHING (default) | If any seller is ineligible, the whole payment is rejected with 422 before the card is charged — no partial fulfilment risk |
PARTIAL_ALLOWED | Ineligible legs are marked REJECTED; the payment proceeds with the eligible sellers |
Response
A successful charge returns the standard payment result:
{
"success": true,
"intentId": "pay_01HV3K9X7QWE...",
"status": "CAPTURED",
"attemptCount": 1,
"createdAt": 1782115200000
}The per-seller breakdown — each leg's gross, commission, hold and net — is written to the transaction record rather than inlined here. Read it with GET /marketplace/transactions, where each split payment lists its seller legs.
Errors
| Status | Meaning | What to do |
|---|---|---|
422 | One or more sellers ineligible (under ALL_OR_NOTHING) | Inspect details.ineligible[] — see below |
400 | Validation failed (leg amounts, legs not adding up to amount, missing fields). If a removed amount field was sent: payment.amount.field_removed | Fix the request body |
401 / 403 | Step-Up auth failed (bad signature, stale X-Date, reused nonce) | Re-sign the request — see Authentication |
The 422 ineligibility response
{
"code": "marketplace.payment.seller_not_eligible",
"message": "One or more sellers are not eligible for marketplace payment.",
"details": {
"policy": "ALL_OR_NOTHING",
"ineligible": [
{ "subDealerId": "d_c41b...", "reasonCode": "SELLER_KYC_REQUIRED" }
]
}
}reasonCode | Meaning | Fix |
|---|---|---|
SELLER_SUSPENDED | Seller is suspended, on hold or closed | Reactivate via PUT /marketplace/dealers/{id}/status if appropriate |
SELLER_KYC_REQUIRED | Onboarding/KYC not complete | Have the seller finish onboarding |
SELLER_PAYOUT_NOT_CONFIGURED | No payout configuration for the seller yet | Complete payout terms during onboarding |
Webhooks
Split payments emit the same events as core payments — payment.captured, payment.failed, payment.refunded, payment.voided and the rest of the payment.* family. There are no marketplace-specific event types; correlate by orderId / payment id and read the seller breakdown from the transactions endpoint.
Next steps
- Split 3D Secure Payment — the same request through a 3DS challenge
- Refunds & Cancellations — reversing a split payment
- Transactions & Payouts — reading seller legs and settling payouts