feat(credits): micropayment credit ledger & multi-party revenue distr… - #1582
Merged
yusuftomilola merged 1 commit intoAug 25, 2026
Conversation
…ibution Adds an internal double-entry credit ledger for high-frequency, low-value charges, a configurable revenue-split engine, and a batch settlement job that moves netted balances off-platform over the DistinctCodes#1574 Soroban rail. Per-minute usage, printing and room overage are too small to settle on-chain per event, so a charge is now a synchronous internal ledger movement with no rail or chain call in the hot path; value only crosses the platform boundary later, in one netted transfer per recipient. Ledger - ledger_accounts (members plus singleton system accounts) and append-only ledger_entries grouped by ledger_transactions. Every transaction is validated to balance before anything is written, so all balances in a currency always sum to exactly zero. - ledger_transactions.reference is unique: replayed charges, re-run settlement passes and resumed batch jobs return the original transaction instead of posting a duplicate. - Accounts are locked FOR UPDATE in ascending id order, so concurrent movements serialize rather than deadlock, and an overdraft check can never run against a stale balance. - balance is a materialized cache of the entries; an admin integrity report re-derives it and reports any drift. Overdraft policy - A charge is refused the moment it would take a member below -overdraftLimit (CREDITS_DEFAULT_OVERDRAFT_LIMIT, default 0 — no overdraft), raisable per account for graceful degradation. The check runs inside the account's row lock, so two charges that are each individually affordable but not affordable together can never both succeed. Tested with 25 concurrent charges against a fixed balance. Splits - RevenueSplitConfig basis points must sum to exactly 10000, rejected at configuration time rather than discovered mid-settlement. - Allocation uses the largest-remainder method with a documented, deterministic tie-break, so the shares always sum to exactly the input amount — no minor unit is ever dropped or duplicated. - Attachable to a DistinctCodes#1570 payment (posted as internal ledger entries) or computed over a settlement batch (internal entries plus on-chain payouts). Settlement - Hourly job: resumes open batches first, then distributes accumulated revenue across a split config and nets each payable account. - Safe to crash mid-run: amounts come from account balances rather than a running tally, one in-flight payout per account, and per-payout idempotency keys the rail dedupes on (reused on retry, never reminted). - A submission is never a settlement. The ledger drawdown and the per-entry settled marker are written only after the rail confirms the payout from fresh chain state; a failed leg leaves the balance shown as still owed. An unreachable rail is indeterminate, not a failure. - Admins can inspect a batch's full breakdown — entries in, recipients out, on-chain transaction references — and retry or abandon it. Payments integration - Credits reads payments, never the reverse: a CONFIRMED payment marked metadata.purpose=CREDIT_TOP_UP funds the payer's balance, and the payment/credit link table lives on the credits side. - SorobanPayoutAdapter implements the payout port over the existing escrow contract, deriving the escrow id from the payout's idempotency key so re-submitting can never transfer twice; the port resolves to null (and settlement says so) when SOROBAN_ENABLED is not true. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
@feyishola is attempting to deploy a commit to the naijabuz's projects Team on Vercel. A member of the Team first needs to authorize it. |
yusuftomilola
approved these changes
Aug 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…ibution
Adds an internal double-entry credit ledger for high-frequency, low-value charges, a configurable revenue-split engine, and a batch settlement job that moves netted balances off-platform over the Soroban rail.
Per-minute usage, printing and room overage are too small to settle on-chain per event, so a charge is now a synchronous internal ledger movement with no rail or chain call in the hot path; value only crosses the platform boundary later, in one netted transfer per recipient.
Ledger
Overdraft policy
Splits
Settlement
Payments integration
closes #1575