Marketplace Revenue Sharing and Payouts: Balanced Entries, Refund Clawbacks, and Settlement Reconciliation
A successful charge does not mean the seller's share is ready for payout, and a successful transfer does not prove bank settlement. This article freezes share policies and settlement batches, then uses a balanced multi-party ledger, reserves, stable payout identities, and UNKNOWN reconciliation to prevent overpayment and duplicate payouts.
Key Points
- Topic Focus: DevOps、独立开发
- Suitable for: indie hackers, full-stack engineers, AI application developers
- Core value: quickly understand background, practical paths, common pitfalls, and engineering advice
The platform collects $100, and the seller is entitled to $80. The transfer succeeds, but its response is lost. After the worker retries with a different ID, the seller receives $160. Charges, transfers, connected balances, and payouts each have their own failure domain.
Versioned Cost Attribution for AI Gross Margin handles revenue and COGS, while the Control Plane for Bulk Customer Operations governs bulk operations. This article covers only revenue sharing and payouts; it does not treat the provider balance as the general ledger.
Freeze sharePolicy.v1 and Multi-Currency Entries
marketplace.sharePolicy.v1 binds the parties, share basis and percentages, ownership of taxes and fees, currency, rounding owner, refund clawbacks, reserve and hold rules, minimum payout, cadence, negative-balance rules, provider API version, and effective period. Upgrades affect only new allocations.
schema: marketplace.sharePolicy.v1
policy: creator-market@8
basis: CAPTURED_ITEM_NET_OF_TAX
platformShare: {percentBps: 2000}
sellerShare: {percentBps: 8000}
providerFeeOwner: PLATFORM
reserve: {percentBps: 1000, hold: P14D}
payout: {cadence: WEEKLY, minimumMinor: 5000, currency: USD}
roundingResidualOwner: PLATFORM
For each currency, the order ledger records balanced legs for buyer cash, seller pending balances, platform revenue, taxes, fees, reserves, refunds, and disputes. Share calculations use integer minor currency units, with residuals allocated according to the policy. Balances in different currencies are never offset against one another. Multi-Currency Pricing and Settlement defines the amount boundaries, and this ledger references its charge receipt.
Stripe Connect can associate one charge with multiple transfers through a transfer group, but a refund does not automatically reverse those transfers. A connected-account payout then moves the balance toward a bank account. In automatic payout mode, the system imports only authoritative payout IDs and does not run a local dispatcher.
Separate Allocation, Transfer, and Payout Layers
A local allocation follows PAYMENT_PENDING -> CAPTURED -> ALLOCATION_READY -> ALLOCATED -> HOLDING -> TRANSFER_ELIGIBLE; refunds and disputes enter RESERVE_DEBITED/RECOVERY_DUE/NEGATIVE_BALANCE. After retrieving the authoritative charge receipt, the ledger uses CAS to match the order generation, policy, and amount/currency, then atomically writes a stable allocation ID, a balanced marketplaceSettlementReceipt.v1, and each party's pending and reserve legs. A webhook does not count as acceptance, and an allocation does not mean that funds have entered the connected account.
A transfer batch freezes its manifest by connected account, currency, cutoff, and policy generation. It selects only allocation legs whose hold has expired, that have no dispute, and that are not already reserved. Each transfer uses a distinct transferOperationId and follows TRANSFER_PREPARED -> TRANSFER_DISPATCHING -> TRANSFER_ACCEPTED/TRANSFER_FAILED. The dispatch CAS only reserves the legs and authorizes sending. On acceptance, the system matches an authoritative transfer receipt against the destination, amount/currency, transfer group, and idempotency identity, then atomically writes the connected-balance legs. A timeout enters TRANSFER_UNKNOWN; the system may only query that same transfer and may not use a payout query, release the legs, or resend under a different key.
Payout has a separate lineage. Automatic mode does not create a local dispatcher; it only imports provider state by authoritative payout ID. Manual mode consumes only accepted available legs from the connected balance, uses a distinct payoutOperationId, and follows PAYOUT_PREPARED -> PAYOUT_DISPATCHING -> PAYOUT_ACCEPTED -> PAYOUT_PENDING -> PAYOUT_IN_TRANSIT -> PAYOUT_PAID, while also mapping PAYOUT_CANCELED/PAYOUT_FAILED/PAYOUT_RETURNED. It enters PAYOUT_UNKNOWN only when no attributable payout object or receipt can be found after dispatch. A payout that has been accepted but has not arrived cannot masquerade as UNKNOWN. Transfer and payout IDs, idempotency keys, and receipts must never be reused.
Each payout state is accepted only from an authoritative receipt matching the account, payout ID, and amount/currency. UNKNOWN reconciliation queries the same ID. Even paid must still be reconciled against bank statements, while canceled, failed, or returned payouts atomically write reversal legs. Late refunds or disputes do not rewrite historical records: they consume the reserve first, create a seller negative balance if the reserve is insufficient, and deduct it from future revenue. The Prepaid Credits Ledger emphasizes that customer credits and seller payables must not share an account.
Make Charges, Transfers, and Bank Settlement Fail Independently
The failure matrix injects lost charge responses, duplicate webhooks, concurrent allocations, partial transfer failures and timeouts, disabled accounts, hold/dispute races, manual payouts that remain pending long after acceptance, in-transit delays, cancellations, UNKNOWN lookups, bank returns after paid, and simultaneous automatic and manual payout modes. Assert that the three lineages remain independent, known states are not misclassified as UNKNOWN, and UNKNOWN does not release balances.
Release gates require a 100% balanced-journal rate. Allocations without charge receipts, duplicate transfers, overpayments, unassociated operations, and UNKNOWN retries under different keys must all remain at zero. Unexplained discrepancies among balance transactions, allocations, transfers and connected balances, payouts, and bank statements must also remain at zero, with at least 99.9% reconciliation within 24 hours. After two shadow cycles, roll out to internal users and a 5% low-value cohort. Choose either automatic or manual payouts, never both. During an incident, freeze new dispatches; compensate for funds already paid only through reversals and reserves. Webhook Subscription Correctness ensures that notifications are verifiable, but provider and bank receipts remain authoritative for funds.
Related
Related Content
Article
What Is Vibe Coding? Will It Change How Programmers Work?
Technical trends, engineering practices, and an indie developer perspective on Vibe Coding, AI, and independent development.
Article
How Should a Technical Community Design Its Tag System and Recommendation Feed?
Centered on independent development, Next.js technology trends, engineering practices, and the indie developer perspective.
Article
Learning Technical Community Product Design from dev.to
Technical trends, engineering practices, and an independent developer's perspective on independent development and open source.
Article
How Can Indie Developers Use AI to Validate Product Ideas Quickly?
Centered on indie development and AI—technical trends, engineering practices, and the indie-developer perspective.