Files
vmall/openspec/specs/settlement/spec.md
T
james 9904696e76 feat: wave 2 migration (P3, P5, P7 openspec changes)
Implements, verifies, and archives the three remaining Wave 2 changes from
openspec/MIGRATION-PLAN.md.

- add-wallet-settlement (P3): demo recharge, guarded withdrawal freeze and
  one-time admin review, paginated own fund entries, idempotent per-shop
  weekly/monthly settlement statements with commission rate and one-time
  payout confirmation.
- add-merchant-onboarding (P5): personal/enterprise applications with one live
  application per user, guarded review with mandatory rejection reason, and
  transactional shop + owner provisioning returning one-time credentials;
  mall onboarding/status pages and an admin review console.
- add-membership-messaging (P7): platform member levels, append-only growth
  accrual on order completion with guarded one-way leveling, order/shipment/
  refund system messages with unread/read state and soft deletion, plus the
  mall header unread badge.

Backend: migrations 0019-0023, new wallet, settlement, merchant_onboarding,
membership and messaging modules, event hooks in order/fulfillment/aftersale,
and integration suites for each. Shared contract extended and all three
frontends updated; code indexes, domain docs, backend guidelines and the
migration tracker synced.

Verification: cargo test -p vmall-api green twice consecutively; mall, admin
and shop-admin builds pass; browser smoke on every new surface; openspec
validate --all --strict green (33 passed).

The three changes share the @vmall/shared contract, the mall mock adapter and
per-app locale/nav files, so they are committed together to keep every commit
buildable.
2026-09-25 15:25:29 +00:00

4.8 KiB
Raw Blame History

settlement Specification

Purpose

Platform-mediated merchant settlement: per-shop weekly or monthly statements generated manually for a closed period, snapshotted from confirmed-received orders minus completed refunds minus a platform commission rate configured in integer basis points. Amounts are integer minor units in the platform base currency; generation is idempotent per shop and period; and a platform admin confirms the payout exactly once, crediting the shop owner's available account with a single ledger entry.

Requirements

Requirement: Idempotent periodic statement generation

A settlement statement SHALL be generated manually for one shop, one period kind (week or month), and one closed period. For the same shop, period kind, and period start at most one statement SHALL exist, enforced by the database. A repeated generation request SHALL return the existing statement unchanged instead of recomputing or duplicating it. No scheduled batch job SHALL generate statements.

Scenario: repeat generation is idempotent

  • WHEN a statement already exists for a shop and period and generation is requested again
  • THEN the existing statement is returned unchanged and no second row is created

Scenario: generation is manual only

  • WHEN no generation request is made for a closed period
  • THEN no statement exists for that period

Requirement: Immutable amount snapshot

A generated statement SHALL snapshot the contributing order count, the gross total of confirmed-received orders in the period, the deduction total of completed refunds against those orders (aftersales rows with status refunded, per-order totals from add-aftersale-refunds), the platform commission rate at generation time, the commission amount, and the payable amount where payable = gross − refunds − commission. All amounts SHALL be integer minor units computed with integer arithmetic, and the commission rate SHALL be an integer basis-point value. The snapshot SHALL NOT change after generation even when later orders, refunds, or commission-rate changes occur.

Scenario: refunds reduce the payable amount

  • WHEN a confirmed-received order in the period has a completed refund of 2000 minor units
  • THEN the statement's refund deduction includes those 2000 minor units and the payable amount is reduced accordingly

Scenario: commission arithmetic is integral

  • WHEN the commission rate is 500 basis points and gross minus refunds is 10000 minor units
  • THEN the commission snapshot is 500 minor units and the payable snapshot is 9500 minor units with no floating-point arithmetic

Scenario: snapshot survives rate changes

  • WHEN the platform commission rate changes after a statement was generated
  • THEN the generated statement keeps its snapshotted rate and amounts

Requirement: Platform commission rate configuration

The commission rate SHALL be a single platform-level setting stored as integer basis points and readable and editable by platform admins. A rate change SHALL affect only statements generated after the change.

Scenario: new rate applies to new statements

  • WHEN an admin sets the commission rate and a statement is generated afterwards
  • THEN that statement snapshots the new rate

Requirement: One-time payout confirmation

A statement SHALL transition exactly once from pending to confirmed through a guarded status transition requiring the pending status, triggered by a platform admin confirming the payout; a repeated confirmation SHALL return 409. Confirmation SHALL record the confirming admin and timestamp and SHALL credit the payable amount to the shop owner's available account with exactly one ledger entry referencing the statement.

Scenario: confirm pays out once

  • WHEN an admin confirms a pending statement
  • THEN the statement becomes confirmed and the shop owner's available balance increases by the payable amount with one ledger entry referencing the statement

Scenario: double confirmation conflicts

  • WHEN an admin confirms an already confirmed statement
  • THEN the API returns 409 and no second ledger entry is written

Requirement: Shop-scoped statement visibility

Statements and their breakdown SHALL be scoped to one shop. Through the shop's own-shop scope, shop users SHALL read and generate statements only for their own shop and SHALL never observe another shop's statements, contributing orders, or refund lines; platform admins SHALL observe every shop.

Scenario: merchant sees own shop only

  • WHEN a shop user lists settlement statements
  • THEN only statements of their own shop appear

Scenario: detail breakdown is shop-scoped

  • WHEN a shop user opens a statement detail
  • THEN the contributing orders and refunded amounts are visible for that shop only