Split-Money Settlement
How a single Total Fare fans out to every party β partners, Points, the hub and the platform β directly through the gateway's split rails, while RideChain itself never holds a single rupee.
1. Overview β one fare, many payees
A RideChain delivery is paid for by one booker as a single Total Fare, but that money belongs to several parties the moment it lands: the partner(s) who carried the parcel, the RideChain Points that handled drop-off and collection, the Block Hub that consolidated a relay, and the platform itself. The job of split settlement is to fan that one amount out to all of them β without RideChain ever holding the money in between.
The split is executed by the gateway's native split rails, abstracted behind the same Go PaymentGateway interface used for pay-in:
Razorpay Route (primary)
The default split rail. Each payee is a Route linked account; a captured order is transferred to many linked accounts in one instruction, settling directly to each payee.
Cashfree Easy Split (fallback)
The failover rail. Each payee is a Cashfree vendor; Easy Split distributes the captured amount across vendors. Selected by the same live success-rate router as pay-in.
Platform holds no money
No RBI nodal / PA licence. Funds rest in gateway escrow and move directly to each payee on a release instruction; the platform's only "balance" is its commission slice plus the immutable ledger.
Ledger mirrors, never holds
The append-only escrow ledger records every slice in paise. It is a faithful mirror of what the gateway did β a projection, never a pot of cash.
Every booking obeys one balance identity. The Total Fare equals the sum of partner payouts, plus the sum of Point/Hub handling fees, plus the platform commission β and the platform commission is the slice that absorbs the gateway's processing fee and the tax reserve:
T = Ξ£ partner_payout(s) + Ξ£ Point/Hub handling fees + Platform commissionwhere
Platform commission = PG fee (~2%) + tax reserve (GST / TCS / TDS) + net platform margin.Crucially, the gateway processing fee and the tax reserve come out of the platform's slice β partners and Points receive their stated payout in full, with no hidden deduction. See Commission & Pricing for how the slices are sized.
flowchart LR BK["π± Booker pays Total Fare T
(one payment, in paise)"] --> ESC["Gateway escrow
(RideChain holds NO money)"] ESC --> SPL{"Split rail
Razorpay Route β Cashfree Easy Split"} SPL --> PA["π΅ Partner(s)
linked account / vendor"] SPL --> DP["πͺ Drop Point
handling fee"] SPL --> CP["πͺ Collect Point
handling fee"] SPL --> HB["π¬ Block Hub
(relay only)"] SPL --> PL["ποΈ Platform commission"] PL --> PG["PG fee ~2%"] PL --> TR["Tax reserve
(GST / TCS / TDS)"] PL --> NET["Net platform margin"] classDef store fill:#fff3e0,stroke:#f4920b,color:#8a5200; classDef plat fill:#e8f0fb,stroke:#1f5fae,color:#143d6e; class ESC store; class PL,PG,TR,NET plat;
2. The vendor / linked-account model
For money to reach a partner or Point directly, that party must first exist at the gateway as a payable entity: a Razorpay Route linked account or a Cashfree Easy Split vendor. Creating that entity requires a verified bank account / UPI handle and completed KYC β until then, no slice can be transferred to them.
Onboarding a payee is part of the partner/Point onboarding flow (see Onboarding & KYC) and standardises on Cashfree Easy KYC regardless of which rail later moves the money:
- Collect identity & payout details β Aadhaar OKYC, PAN, and the payee's bank account or UPI handle, plus name/face match.
- Penny-drop verification β a tiny credit confirms the bank/UPI actually belongs to the verified name before it is ever used for a payout.
- Create the gateway entity β on success, a Route linked account (Razorpay) and/or an Easy Split vendor (Cashfree) is created and bound to the partner/Point record.
- Mark payable β only now can the party appear as a split target. A split that names an un-onboarded payee holds that slice in escrow.
flowchart TB
START["Partner / Point earns a slice"] --> Q{"Payee onboarded?
(linked account / vendor live)"}
Q -->|"yes"| PAY["Transfer slice directly
(Route / Easy Split)"]
Q -->|"no"| KYC{"KYC + penny-drop
complete?"}
KYC -->|"not yet"| HOLD["HOLD slice in gateway escrow
(rest of split still releases)"]
KYC -->|"now complete"| MAKE["Create linked account / vendor
mark payable"]
MAKE --> PAY
HOLD -. "KYC clears later" .-> MAKE
classDef bad fill:#fdecea,stroke:#c0392b,color:#7b241c;
classDef good fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20;
class HOLD bad; class PAY good;
3. The split computation β worked example #1
Take the canonical single point-to-point booking with Total Fare βΉ120 (12000 paise). One partner carries the parcel end to end; a drop Point and a collect Point each handle a physical handover; the platform takes the remainder, out of which it absorbs the gateway fee and the tax reserve.
| Payee | Share (βΉ) | Paise | Note |
|---|---|---|---|
| π΅ Partner | βΉ80 | 8000 | Full carry, door-to-door / Point-to-door |
| πͺ Drop Point | βΉ6 | 600 | Origin-side handover fee |
| πͺ Collect Point | βΉ6 | 600 | Destination-side handover fee |
| ποΈ Platform commission | βΉ28 | 2800 | Remainder β absorbs PG fee + tax reserve |
| Total Fare | βΉ120 | 12000 | Ξ£ of the above (80 + 6 + 6 + 28) |
The platform's βΉ28 (2800 paise) is not all margin. It is the slice that quietly carries the cost of moving the money and the cost of tax:
| Inside the platform's βΉ28 | Approx βΉ | Paise |
|---|---|---|
| PG fee (~2% of fare) | ~βΉ2.4 | ~240 |
| Tax reserve (GST / TCS / TDS) | ~βΉ3 | ~300 |
| Net platform margin | ~βΉ22.6 | ~2260 |
| Platform commission | βΉ28 | 2800 |
pie showData title "βΉ120 booking split (paise)"
"Partner 8000" : 8000
"Drop Point 600" : 600
"Collect Point 600" : 600
"Platform 2800" : 2800
flowchart LR T["Total Fare βΉ120
(12000 paise)"] --> S{"Split"} S -->|"8000"| P["π΅ Partner βΉ80"] S -->|"600"| D["πͺ Drop Point βΉ6"] S -->|"600"| C["πͺ Collect Point βΉ6"] S -->|"2800"| PL["ποΈ Platform βΉ28"] PL -->|"~240"| PG["PG fee ~βΉ2.4"] PL -->|"~300"| TX["Tax reserve ~βΉ3"] PL -->|"~2260"| NET["Net margin ~βΉ22.6"] classDef plat fill:#e8f0fb,stroke:#1f5fae,color:#143d6e; class PL,PG,TX,NET plat;
4. Per-leg split for relay / milk-run β worked example #2
A relay (bundled milk-run) booking is multiple legs through a Block Hub, each leg a different partner. Money splits per leg, and each leg's slice releases when that leg's handover completes (dual-OTP + POD) β so the money chain advances in lockstep with the custody chain. Take the canonical relay with Total Fare βΉ220 (22000 paise):
| Payee | Leg | Share (βΉ) | Paise | Releases on |
|---|---|---|---|---|
| π΅ Partner A | Leg 1 (origin β hub) | βΉ55 | 5500 | Leg-1 handover at hub (OTP + POD) |
| π¬ Block Hub | Consolidation | βΉ8 | 800 | Hub intake confirmed |
| π΅ Partner B | Leg 2 (hub β destination) | βΉ95 | 9500 | Leg-2 drop (OTP + POD) |
| πͺ Drop Point | Origin side | βΉ6 | 600 | Parcel staged at drop Point |
| πͺ Collect Point | Destination side | βΉ6 | 600 | Final collection handover |
| ποΈ Platform commission | β | βΉ50 | 5000 | On final SETTLED (absorbs PG + tax) |
| Total Fare | βΉ220 | 22000 | Ξ£ (55 + 95 + 6 + 8 + 6 + 50) | |
The key idea is lockstep: the partner who completes leg 1 is paid for leg 1 the moment the hub handover verifies β even though a second partner still has to finish the journey. The custody chain and the money chain advance together, link by link.
sequenceDiagram
autonumber
participant SB as "Sender / Drop Point"
participant PA as "Partner A (leg 1)"
participant HUB as "Block Hub"
participant PB as "Partner B (leg 2)"
participant CP as "Collect Point / Receiver"
participant LED as "Escrow ledger"
Note over SB,LED: "Escrow HOLD of 22000 paise at pay-in"
SB->>PA: "Pickup OTP β leg 1 begins"
PA->>HUB: "Handover at hub (dual-OTP + POD)"
HUB-->>LED: "RELEASE_PARTNER A 5500 + RELEASE_HUB 800"
HUB->>PB: "Leg 2 assigned"
PB->>CP: "Drop (dual-OTP + POD)"
CP-->>LED: "RELEASE_PARTNER B 9500 + RELEASE_POINT 600 + 600"
Note over LED: "Delivery confirmed β COMMISSION 5000 (PG + tax reserve absorbed)"
5. The append-only escrow ledger
RideChain's money tables are immutable event logs. You never update a balance; you append a new, signed entry describing a movement. A party's balance is a projection β a fold over the entries β never a mutable counter. All amounts are stored as paise (integers); the βΉ figures are display only.
| Entry type | Meaning |
|---|---|
HOLD | Fare captured into gateway escrow at pay-in (one credit of the full Total Fare). |
RELEASE_PARTNER | A partner's leg slice released to their linked account / vendor on that leg's handover. |
RELEASE_POINT | A drop/collect Point's handling fee released. |
RELEASE_HUB | The Block Hub's consolidation fee released (relay only). |
COMMISSION | The platform's commission slice booked. |
PG_FEE | Gateway processing fee, recorded against the platform slice. |
TAX_RESERVE | Amount withheld from the platform slice for GST / TCS / TDS. |
REFUND | An un-released portion returned to the booker. |
ADJUSTMENT | A correcting entry (e.g. COD reconciliation, dispute resolution) β never an edit of a prior row. |
Here is the full ledger for the βΉ120 single point-to-point booking once delivered and settled. Read top to bottom: one HOLD credit, then the slices released, then the platform's commission decomposed into PG fee + tax reserve + net. The entries sum to zero against the held amount β that is the reconciliation invariant.
| # | Entry type | Payee / account | Paise | βΉ |
|---|---|---|---|---|
| 1 | HOLD | Gateway escrow (booking) | +12000 | βΉ120.00 |
| 2 | RELEASE_PARTNER | Partner (full carry) | β8000 | βΉ80.00 |
| 3 | RELEASE_POINT | Drop Point | β600 | βΉ6.00 |
| 4 | RELEASE_POINT | Collect Point | β600 | βΉ6.00 |
| 5 | COMMISSION | Platform | β2800 | βΉ28.00 |
| 5a | PG_FEE | Gateway (from platform slice) | ~240 | ~βΉ2.40 |
| 5b | TAX_RESERVE | Tax holding (from platform slice) | ~300 | ~βΉ3.00 |
| 5c | (net margin) | Platform net | ~2260 | ~βΉ22.60 |
| Escrow balance after settle | 0 | βΉ0.00 | ||
Entries 5aβ5c are sub-postings within the platform's 2800-paise commission, not new money out of the fare; they explain where the platform's slice goes (240 + 300 + 2260 = 2800). The relationship between bookings, legs and immutable ledger entries is a strict append-only event log:
erDiagram
BOOKING ||--o{ LEG : "decomposes into"
BOOKING ||--|| ESCROW_ACCOUNT : "holds fare"
ESCROW_ACCOUNT ||--o{ LEDGER_ENTRY : "append-only log"
LEG ||--o{ LEDGER_ENTRY : "triggers releases"
PAYEE ||--o{ LEDGER_ENTRY : "credited by"
BOOKING {
string booking_id
int total_fare_paise
string status
}
LEG {
string leg_id
int seq
string handover_state
}
LEDGER_ENTRY {
string entry_id
string type
int amount_paise
string idempotency_key
datetime created_at
}
PAYEE {
string payee_id
string kind
string linked_account_id
}
6. Release timing & entry lifecycle
Money does not move on a clock β it moves on proof. Escrow is HOLD at pay-in; each leg's slice is RELEASEd on that leg's handover (dual-OTP + POD); the booking reaches SETTLED on final delivery + POD, at which point the platform commission books. Payout to the partner's UPI / bank is then instant or daily depending on rail and partner tier.
- HOLD at pay-in β the full Total Fare is captured into gateway escrow; the ledger appends one
HOLDcredit. - RELEASE per leg β each completed handover (dual-OTP + geo/time POD) appends the
RELEASE_*entries that leg has earned and instructs the split rail to transfer them. - SETTLED on final delivery β the last drop's OTP + POD closes the booking;
COMMISSION(withPG_FEE+TAX_RESERVE) books and the escrow balance returns to zero. - Payout β the released slices land in each payee's account via Route / Easy Split / Payouts: instant for trusted partners, daily-batched otherwise.
stateDiagram-v2
[*] --> HELD
HELD --> RELEASED: "leg handover verified (dual-OTP + POD)"
HELD --> REFUND_DUE: "leg fails / cancel before release"
RELEASED --> PAID_OUT: "Route / Easy Split transfer (instant or daily)"
RELEASED --> REVERSED: "dispute resolved for booker"
REFUND_DUE --> REFUNDED: "gateway refund webhook"
PAID_OUT --> [*]
REFUNDED --> [*]
REVERSED --> REFUNDED
HELD in escrow, becomes RELEASED only on its leg's verified handover, then is PAID_OUT to the payee. Before release a failure routes to REFUND_DUE; after release, only a dispute can reverse it.HELD β RELEASED only on a verified drop-OTP + POD for that specific leg; everything else holds or refunds.7. Reconciliation & payout failures
Because the ledger is a mirror of gateway activity, the two must agree to the paise. A daily reconciliation job pulls each gateway's settlement / payout report and diffs it against the internal append-only ledger. Matches are marked reconciled; missing webhooks are recovered by replaying the idempotent transition; genuinely unexplained gaps are flagged and the affected booking's further releases are frozen until ops clears it.
flowchart TB CRON["Daily reconciliation job"] --> PULL["Pull gateway settlement + payout report
(Razorpay Route / Cashfree)"] PULL --> DIFF{"Diff vs append-only ledger
(paise-exact)"} DIFF -->|"match"| OK["Mark reconciled"] DIFF -->|"missing webhook"| REPLAY["Replay idempotent entry
(no double-apply)"] DIFF -->|"payout failed"| RETRY{"Retry payout"} DIFF -->|"unexplained mismatch"| FREEZE["Flag + freeze further releases
(ops review)"] RETRY -->|"ok"| OK RETRY -->|"still failing"| FALLBACK["Fallback rail / wallet
+ notify payee"] REPLAY --> OK classDef bad fill:#fdecea,stroke:#c0392b,color:#7b241c; class FREEZE,FALLBACK bad;
Payout failures (a closed UPI handle, a bounced bank credit) are first-class: the failed transfer is retried, then routed to the fallback rail or held to the payee's RideChain Wallet, and the payee is notified β the released slice is never lost, only delayed. A persistent failure surfaces in the ops queue with the exact ledger entry it relates to.
8. Tax & statements
Settlement produces the paper trail that keeps everyone honest and compliant. Tax is computed against the captured fare; the tax reserve is held out of the platform's commission slice (the ~βΉ3 / 300 paise in the βΉ120 example), so partners and Points always receive their stated payout in full.
GST e-invoice to booker
A GST-compliant e-invoice is generated for the booker against the Total Fare, with correct tax components on the platform fee line. Generated automatically at settlement.
TDS on partner payouts
TDS is deducted where applicable on partner / vendor payouts, withheld from the tax reserve and reported per the payout records the gateway executes on RideChain's instruction.
Partner weekly statement
Each partner gets a transparent weekly statement: every leg, its slice, payout date and any TDS β no hidden cuts. The PG fee and tax reserve are visibly the platform's, not theirs.
Point commission statement
Each RideChain Point gets a handling-fee statement of every drop/collect it serviced and the fee earned, reconciled to the ledger entries.
RELEASE_PARTNER entries. There is no place for a silent deduction to hide. See Commission & Pricing for how the slices are set.9. Edge cases & failure modes
Split settlement assumes payees will be half-onboarded, UPIs will be closed, relays will fail mid-way and gateways will flake. Every money-touching failure has a defined, idempotent mitigation; the full catalogue lives in the Edge-Case Catalog.
| Risk / scenario | Mitigation |
|---|---|
| Payee KYC incomplete at release | Hold that slice in gateway escrow; the rest of the split releases normally; the held slice settles the instant Cashfree Easy KYC + penny-drop clears. |
| Payout to closed / invalid UPI handle | Retry the transfer, then fall back to the alternate rail or the payee's RideChain Wallet; notify the payee; persistent failure goes to manual ops with the exact ledger entry. |
| Partial relay failure (leg 2 never completes) | Release only the completed legs (leg-1 partner + hub keep their earned slices); refund only the un-delivered remainder β per-leg ledger makes this exact. |
| Split rounding when slices don't divide evenly | All math is in paise integers; any remainder paise are deterministically allocated to the platform slice, so Ξ£ slices always equals the captured fare exactly. |
| Gateway split API down | Queue the split instruction and retry with backoff; the success-rate router fails over from Razorpay Route to Cashfree Easy Split; nothing settles until a real transfer confirms. |
| Double-release of the same slice | Idempotency-key on every ledger entry; a replayed release maps to the same entry and transfers nothing twice. |
| Refund requested after a partial release | Reverse only the un-released portion still in escrow; already-released, fairly-earned slices stand (defended with POD + OTP if disputed). |
| COD β cash collected offline by partner | Reconcile the cash into the ledger as a wallet ADJUSTMENT: the COD pay-in is treated as already-captured, then split to partner/Point/platform (see Payments β COD). |
| Commission dispute (party contests their slice) | The append-only ledger is the evidence; resolution posts an ADJUSTMENT entry rather than editing history; statement re-issued. |
| Negative partner balance (COD shortfall) | Debit the partner's wallet for the gap via an ADJUSTMENT; apply a trust-tier hit; future payouts offset the negative balance. |
| Ledger vs gateway mismatch at reconciliation | Flag the booking and freeze further releases until ops reconciles; because the ledger is immutable, the discrepancy is always traceable to a specific entry. |
| Tax reserve shortfall (reserve under-withheld) | Top up the reserve from the platform's net margin via an ADJUSTMENT; partners/Points are never clawed back β the platform slice absorbs the gap. |