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.
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Demo wallet recharge
|
||||
An authenticated user SHALL recharge their wallet through a simulated demo flow that records a wallet recharge row and credits the available monetary account with one signed ledger entry in one transaction, without invoking any external payment channel. The API payload and the wallet UI SHALL clearly label the recharge as simulated/demo. Amounts SHALL be positive integer minor units in the account's currency, and monetary arithmetic SHALL NOT use floating point.
|
||||
|
||||
#### Scenario: demo recharge credits balance
|
||||
- **WHEN** a signed-in buyer submits a demo recharge of 5000 minor units
|
||||
- **THEN** the available balance increases by exactly 5000 minor units and exactly one ledger entry with reason `wallet_recharge` records the resulting balance
|
||||
|
||||
#### Scenario: flow is labeled as demo
|
||||
- **WHEN** a buyer opens the recharge flow
|
||||
- **THEN** it is visibly marked as a simulated recharge and no external payment provider is contacted
|
||||
|
||||
### Requirement: Guarded withdrawal freeze
|
||||
An authenticated user SHALL apply to withdraw a positive amount in their account currency. The application SHALL atomically move the amount from available to frozen balance through guarded conditional updates that succeed only when available balance covers the amount, and each balance change SHALL be paired with a ledger entry in the same transaction. When available balance is insufficient the request SHALL fail without changing any balance, and concurrent applications SHALL never overdraw or produce a negative balance.
|
||||
|
||||
#### Scenario: withdrawal freezes funds
|
||||
- **WHEN** a buyer applies to withdraw 1000 minor units
|
||||
- **THEN** available balance decreases and frozen balance increases by exactly 1000 minor units and two ledger entries record the move
|
||||
|
||||
#### Scenario: concurrent applications cannot overdraw
|
||||
- **WHEN** two withdrawal applications together exceed the available balance
|
||||
- **THEN** at most one application succeeds and all balances remain non-negative
|
||||
|
||||
### Requirement: One-time withdrawal review
|
||||
Platform admins SHALL list pending withdrawal applications and approve or reject each exactly once through a guarded status transition that requires the `pending` status and returns 409 on a repeated review. Approve SHALL deduct the frozen amount with a ledger entry recording that the funds left the platform. Reject SHALL return the frozen amount to available balance with a ledger entry. Every review SHALL record the reviewing admin, timestamp, and optional note.
|
||||
|
||||
#### Scenario: reject returns funds
|
||||
- **WHEN** an admin rejects a pending withdrawal application
|
||||
- **THEN** the frozen amount returns to available balance and the application status becomes rejected
|
||||
|
||||
#### Scenario: approve consumes frozen funds
|
||||
- **WHEN** an admin approves a pending withdrawal application
|
||||
- **THEN** frozen balance decreases by the requested amount and the application status becomes approved
|
||||
|
||||
#### Scenario: repeated review conflicts
|
||||
- **WHEN** an admin reviews an application that was already reviewed
|
||||
- **THEN** the API returns 409 and no balance or ledger row changes
|
||||
|
||||
### Requirement: Paginated fund entries
|
||||
An authenticated user SHALL page through their own monetary account entries newest first. Each entry SHALL carry the signed delta minor, resulting balance minor, reason, optional business reference, and timestamp, mapped from the existing append-only ledger rows. Entries SHALL never expose or mutate another user's ledger.
|
||||
|
||||
#### Scenario: entries page maps the ledger
|
||||
- **WHEN** the buyer requests a page of fund entries
|
||||
- **THEN** only their own available/frozen ledger rows appear with signed deltas and resulting balances
|
||||
|
||||
#### Scenario: entries are user-isolated
|
||||
- **WHEN** one user requests fund entries
|
||||
- **THEN** no entry belonging to another user is ever returned
|
||||
Reference in New Issue
Block a user