In DevelopmentCodeSale — Digital Goods Marketplace
A production-grade marketplace for buying and selling digital codes, built with financial-system-level rigor from the ground up.
Full StackSolo ProjectNode.jsNext.jsMongoDBStripeStripe Connect
OverviewCodeSale is a peer-to-peer marketplace where sellers list digital goods (game keys, gift cards, software licenses) and buyers purchase them with guaranteed delivery. Every transaction is escrow-protected — funds are held until the buyer confirms delivery, with a full dispute resolution system if something goes wrong. The platform is built to handle real money, real fraud vectors, and real edge cases.
The ProblemExisting digital goods marketplaces share a recurring pattern of structural failures:
- Sellers get chargebacks they cannot fight — because payment is direct, not escrow-protected.
- Buyers get scammed with invalid codes — because delivery is unverified.
- Platforms absorb fraud they cannot detect — because there is no risk scoring or behavior tracking.
- Disputes are resolved subjectively — because there is no structured evidence model.
CodeSale is designed to make all four of these economically contained by architecture, not by policy.
Technical ArchitectureEvery financial surface in the system has a single authority layer. Nothing moves money implicitly.
Payment Model
Direct card checkout is permanently disabled. All purchases go through a wallet, and the wallet is funded by Stripe top-ups only. Chargebacks can only ever hit a wallet top-up — never an order directly. The chargeback surface is reduced to a single, containable layer.
Escrow System
Every order creates an escrow lock via an append-only ledger. Funds do not move — ledger entries do. Release and refund are symmetric operations. The ledger is the single source of truth — escrowStatus on the order is a cached mirror, not the authority.
Dispute Lifecycle
Disputes follow a strict state machine: OPEN → UNDER_REVIEW → NEEDS_RESPONSE → WON / LOST → CLOSED. Every transition is validated against the state matrix, wrapped in a MongoDB transaction, and appended to an immutable DisputeEvent collection protected by optimistic concurrency.
Risk Scoring Engine
Every dispute generates an immutable evidence snapshot at creation time. A deterministic scoring engine produces buyer and seller risk tiers: NORMAL → WARNING → AT_RISK → RESTRICTED → BANNED. Tiers drive enforcement automatically — listing visibility, payout eligibility, dispute limits — without manual admin intervention.
Payout Architecture
Seller payouts flow through a multi-layer system: PayoutEligibilityService gates all payouts, WithdrawalDomainService handles seller-initiated requests, WithdrawalExecutionService bridges to per-order payouts via FIFO, and PayoutService (3-phase commit) is the only entity that calls Stripe. Automated payouts and seller withdrawals converge at one execution path.
Inventory Atomicity
Code delivery is atomic. The checkout transaction does this in a single MongoDB session: lock code with findOneAndUpdate, re-validate wallet risk flag, deduct wallet, mark order complete. If any step fails, the transaction rolls back. Double-sale under concurrency is structurally impossible.
Key Engineering Decisions01Wallet-only checkout before any other financial feature
Disabling direct card checkout early shaped the entire chargeback model. Instead of building fraud detection for per-order Stripe payments, the entire chargeback surface was contained to wallet top-ups. Every downstream financial system became simpler and more auditable.
02Ledger as the only source of truth
Two parallel financial systems existed early — a status-based legacy model and a ledger-type-based model. Formally removing the legacy system locked the invariant: seller balances derive strictly from immutable ledger entry types. No status fields, no derived calculations on the frontend. This prevented an entire class of financial drift bugs.
03Dispute lifecycle separated from financial outcomes
Dispute state transitions are fully decoupled from money movement. Refund and release are separate service calls that transition dispute state as a side effect. This means Support manages the lifecycle, Admin executes the financial action, and neither can accidentally trigger the other.
04Payout decoupled from dispute resolution
An earlier version called PayoutService directly after releasing escrow. After refactoring, dispute resolution only writes ledger entries and sets eligibilityStatus to ELIGIBLE. The payout worker picks up eligible orders independently. This eliminated an entire class of stuck states where dispute resolved but payout failed.
05Roadmap-driven development with explicit invariants
Each step in the development roadmap locks invariants that future steps cannot violate: Orders are ledger-only. Stripe ≠ escrow. Wallet ≠ card. Architectural decisions made in step 26 are still enforced in step 75 — not by memory, but by documented constraint.
ChallengesConcurrency in financial transactions
Two buyers attempting to purchase the last available code simultaneously. Solved with MongoDB transactions using findOneAndUpdate with a strict filter — only one session can atomically lock a code. Combined with optimistic concurrency on dispute transitions filtering on current status, the system prevents both double-sale and double-finalization.
Chargeback isolation
A buyer funds their wallet, buys a code, then files a Stripe chargeback on the top-up. Without isolation this creates seller debt. The solution: chargebacks flag the buyer wallet as WALLET_AT_RISK and block further transactions, but never touch seller earnings. Seller earnings derive from ledger entries, not wallet state — structurally insulated.
Dispute evidence integrity
Using the AuditLog for dispute history had a fatal flaw: a 90-day TTL. Long-running disputes would lose their history. A separate DisputeEvent collection was introduced — append-only, no TTL, indexed by disputeId and createdAt — written atomically inside the same transaction as every status transition.
Race condition in token refresh
The frontend auth migration to httpOnly cookies introduced a classic race condition: two simultaneous 401 responses both trigger a refresh, the second fails because the token was already rotated, and the user gets logged out. Solved with a shared in-flight refresh promise — all concurrent 401s await the same promise, only one refresh call reaches the backend.
Tech StackBackendNode.js — Fastify, CommonJS
DatabaseMongoDB with multi-document transactions
Frontend — BuyerNext.js
Frontend — Seller / Admin / SupportReact
PaymentsStripe Connect, Webhooks, Wallet Top-ups
Image StorageImageKit
Document StorageCloudflare R2
AuthJWT with httpOnly cookies, CSRF protection
Scope76development steps planned and documented before implementation
4user roles — Buyer, Seller, Support, Admin — each with distinct authority surfaces
1financial invariant every step must respect: the ledger is the only source of truth
0direct card payments — the entire payment model flows through the wallet layer
What's Still Being BuiltCodeSale is currently in active development. The core financial engine — wallet, escrow, ledger, disputes, and payouts — is implemented and hardened. The remaining work covers the risk scoring engine, seller withdrawal UX, automated testing enforcement, and production hardening before launch.