-
credit_ledger (dev-plan-4.md T23; schema M.2) joins users/sessions in the same SQLite db.js — architecture.md 1.4 named this as the other half of why SQLite got picked in T18 ("扣积分 + 创建生成任务 + 扣减片段 配额" needs real atomicity, T18 only used the credential-storage half). spend()/refund()/topup() each read-modify-write users.creditBalance inside a transaction; spend() refuses to take a balance negative (INSUFFICIENT_BALANCE) instead of allowing debt, and topup() dedupes by paymentRef so a Stripe webhook's guaranteed at-least-once delivery can't double-credit (T24 will rely on this). pricing_config (M.12) stays a JSON file via a new pricing.js — it's operator-edited config, not on the atomic spend path, so SQLite would be over-engineering here; keyed by `key` rather than an id, hence its own tiny module instead of store.js's createStore. Seeded with placeholder credit amounts (dev-plan-4.md §1.2 item 3 — real numbers are still TBD). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011DUiYs6UjxDMkfvnjtsKFo