chore(openspec): archive add-customer-accounts
Sync the customer-accounts capability and the frontend-mall stats requirement into the main specs, and write the capability Purpose the archive leaves as a placeholder so strict validation stays green.
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-18
|
||||
@@ -0,0 +1,36 @@
|
||||
## Context
|
||||
|
||||
The buyer center shows fixed balance, frozen balance, and points. Later points redemption requires protected debits and audit history; mutable profile columns would not provide either. VMall supports multiple currencies, while points are integer units rather than money.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Provide customer account summaries and append-only balance history.
|
||||
- Make every later debit, credit, freeze, and release transactionally safe.
|
||||
- Replace fixture account statistics in the mall.
|
||||
|
||||
**Non-Goals:**
|
||||
- Funding, payment, withdrawal, refunds, settlement, point-earning campaigns, or a public ledger listing.
|
||||
|
||||
## Decisions
|
||||
|
||||
### Accounts are keyed by kind and currency
|
||||
`customer_accounts` has one row per `(user_id, kind, currency)`, where monetary kinds (`available`, `frozen`) carry an ISO currency and `points` has no currency. `balance_minor BIGINT` holds money and integer points; constraints reject negative stored balances and invalid kind/currency pairs. Seed/new users receive the defined account rows with zero balances.
|
||||
|
||||
### Entries are immutable audit facts
|
||||
`customer_account_entries` records account ID, signed delta, resulting balance, reason code, optional reference type/ID, and timestamp. Entries are inserted in the same transaction after their guarded account update. No public API mutates an account in this change; a module service provides the internal mutation API for later capabilities.
|
||||
|
||||
### Guarded mutation is the only write path
|
||||
Debit/freeze uses a conditional update whose predicate proves sufficient balance, returning Conflict on zero rows. Credit/release uses atomic addition. The service locks all affected accounts in stable `(kind, currency)` order, applies the transfers, then appends entries. This avoids races and keeps available/frozen transfers balanced.
|
||||
|
||||
### Mall reads a defined summary
|
||||
`GET /api/me/stats` returns the customer’s configured display-currency `balance_minor`, `frozen_minor`, `points`, and monetary currency. The shared client and mall user center consume it; localization remains UI-owned. `frozen_minor` is usually zero in this change because no public flow freezes funds. There is no customer ledger page or `GET` entries API.
|
||||
|
||||
### Demo credits belong to later spend flows
|
||||
New and seeded customers receive zero balances. Granting spendable points is not a campaign in this change; `add-points-mall` SHALL credit demo points through this module’s guarded credit path (append-only entry) after this change is archived. Direct `SET balance` is forbidden.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- Multi-currency balances introduce selection rules; the initial public summary exposes the buyer’s configured currency, while APIs remain extensible for future wallets.
|
||||
- An internal service, rather than public mutation endpoints, prevents granting balances without a business event but means points mall must depend on this archived change.
|
||||
- Exposing frozen balance before any freeze flow exists can look like a missing feature; keep the kind so later withdrawals do not migrate schema, and keep the mall figure honest (zero until a freeze exists).
|
||||
@@ -0,0 +1,26 @@
|
||||
## Why
|
||||
|
||||
Account statistics are fixed mall fixtures, and points redemption needs an auditable balance rather than mutable user fields. A customer account boundary establishes safe balances and append-only history before any points-spend flow is introduced.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add one customer account per supported balance: cash balance, frozen cash, and integer loyalty points.
|
||||
- Add append-only account entries that record the resulting balance, signed delta, reason, reference type, and reference ID.
|
||||
- Add an authenticated customer account summary endpoint and replace the account-stat fixture on the mall user center.
|
||||
- Provide transactional service operations for later flows to credit, debit, freeze, and release funds or points with non-negative-balance checks.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `customer-accounts`: Customer balance and points accounts, immutable entries, account summary, and guarded balance mutations.
|
||||
|
||||
### Modified Capabilities
|
||||
- `frontend-mall`: User-center account statistics load through the shared API client.
|
||||
|
||||
## Non-goals
|
||||
|
||||
No top-up, payment-from-balance, withdrawal, refund-to-balance, points earning campaign, merchant settlement, or public account-entry listing is included. The frozen monetary kind is reserved for later freeze/release flows.
|
||||
|
||||
## Impact
|
||||
|
||||
Adds Rust account module and migrations, shared account types/API/locales, a mall live-data migration, and a reusable transactional dependency for the points mall.
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Customer account summary
|
||||
Each customer SHALL have available monetary balance, frozen monetary balance, and loyalty-points accounts. An authenticated customer SHALL retrieve a summary containing balance minor, frozen balance minor, their monetary currency, and integer points. Public APIs SHALL NOT list or mutate account entries.
|
||||
|
||||
#### Scenario: new customer summary
|
||||
- **WHEN** a newly registered customer requests account stats
|
||||
- **THEN** the API returns zero balances and zero points without relying on browser fixtures
|
||||
|
||||
#### Scenario: frozen kind exists without a freeze flow
|
||||
- **WHEN** a customer with no freeze events requests account stats
|
||||
- **THEN** `frozen_minor` is zero and available balance is unchanged
|
||||
|
||||
### Requirement: Append-only account entries
|
||||
Every account balance change SHALL create an immutable entry with signed delta, resulting balance, reason, optional business reference, and timestamp in the same transaction. Account entries SHALL NOT be edited or deleted through public APIs.
|
||||
|
||||
#### Scenario: debit records result
|
||||
- **WHEN** an internal business flow debits points successfully
|
||||
- **THEN** the points balance and one entry with the resulting balance commit together
|
||||
|
||||
### Requirement: Guarded account mutation
|
||||
A debit or freeze SHALL succeed only when the affected account has sufficient available balance. Concurrent changes SHALL use conditional atomic updates and return 409 rather than create a negative balance.
|
||||
|
||||
#### Scenario: competing debits
|
||||
- **WHEN** two debits together exceed a points balance
|
||||
- **THEN** at most one debit succeeds and the final stored balance is non-negative
|
||||
@@ -0,0 +1,8 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Live customer account statistics
|
||||
The buyer-center dashboard and points surfaces SHALL load the signed-in customer's balance, frozen balance, currency, and points from the shared account API rather than `USER_STATS`.
|
||||
|
||||
#### Scenario: account summary reload
|
||||
- **WHEN** a signed-in shopper reloads the buyer center
|
||||
- **THEN** displayed account statistics come from the selected API adapter and survive browser state loss
|
||||
@@ -0,0 +1,18 @@
|
||||
## 1. Account persistence and mutation boundary
|
||||
|
||||
- [x] 1.1 Add additive migrations for customer accounts, account kinds/currencies, append-only entries, and required indexes/checks.
|
||||
- [x] 1.2 Initialize zero account rows for existing seeded users and every newly registered customer. Do not credit demo points here.
|
||||
- [x] 1.3 Implement the Rust account module with stable-order account locking, conditional non-negative debits/freezes, atomic credits/releases, and entry insertion.
|
||||
|
||||
## 2. Summary contract and proof
|
||||
|
||||
- [x] 2.1 Add shared account-summary types, client method, localized UI strings, and fixed-data adapter compatibility.
|
||||
- [x] 2.2 Expose authenticated `GET /api/me/stats` through the account service without public balance mutation endpoints.
|
||||
- [x] 2.3 Add API integration tests for zero initialization, summary ownership, immutable entries, available-to-frozen transfer, and competing insufficient debits.
|
||||
|
||||
## 3. Mall migration and verification
|
||||
|
||||
- [x] 3.1 Replace buyer-center and points-page `USER_STATS` reads with the shared account summary API.
|
||||
- [x] 3.2 Remove affected stats fixture imports while keeping the fixed-data adapter rollback path intact.
|
||||
- [x] 3.3 Seed and browser-smoke the live buyer account summary.
|
||||
- [x] 3.4 Run cargo test for vmall-api, the mall build, and strict validation for this OpenSpec change.
|
||||
Reference in New Issue
Block a user