Files
2026-05-27 23:29:53 +01:00

73 lines
5.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Tasks: enterprise-payment
**Change ID**: enterprise-payment
**Package**: `@suaveplan/payment`
**Target path**: `universal/commerce/payment`
**Prerequisite packages on Verdaccio**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`, `@suaveplan/idempotency`, `@suaveplan/crypto`, `@suaveplan/fsm`
---
## 0. Pre-flight (non-negotiable)
- [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each)
- [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
- [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest.
## 1. Scaffold
- [ ] Create `universal/commerce/payment/` with `src/`, `tests/`, `src/__benchmarks__/` and all feature subdirectories per the layout in `proposal.md`.
- [ ] Write `package.json` at version `0.1.0` with `"type": "module"`, `"sideEffects": false`, correct `exports` map, `dependencies`, and `devDependencies`.
- [ ] Write `tsconfig.json` extending `@suaveplan/typescript-config/library.json`.
- [ ] Write `vitest.config.ts` using `createTestConfig()` from `@suaveplan/testing/config`.
- [ ] Add `lint`, `typecheck`, `test`, `test:coverage`, `build`, `docs:check` scripts.
---
## 2. Types + errors
- [ ] `src/types/common.ts` — `PaymentStatus`, `PaymentIntent`, `PaymentTransition`, `Receipt`, `CreatePaymentOptions`, `PaymentWebhookEvent`, `PaymentAdapter` (interface), `PaymentStore` (interface), `PaymentServiceOptions`.
- [ ] `src/types/common.md` (≥200 words, both examples, API reference).
- [ ] `src/error-codes.ts` — `PaymentErrorCode` (8 codes), metadata, register, eager call.
- [ ] `src/constants/index.ts` — `ALLOWED_TRANSITIONS` map, `TERMINAL_STATUSES` set, `DEFAULT_CLOCK_SKEW_MS = 300_000`.
- [ ] `src/types/schemas.ts` + `schemas.md` — Zod schemas. Note: `PaymentIntent` schema must NOT include `pan`, `cvv`, or `expiry` fields.
---
## 3. Core modules
- [ ] **`src/core/payment-fsm/`** — `PaymentFsm`: wraps `@suaveplan/fsm`; defines all valid transitions per `ALLOWED_TRANSITIONS`; `transition(intent, to, key)` checks legality, throws `PAYMENT_INVALID_TRANSITION` if illegal, returns new `PaymentIntent` with updated status and appended `PaymentTransition`. Tests: all legal transitions, two illegal transitions, transition adds history entry.
- [ ] **`src/webhook/webhook-verifier/`** — `WebhookVerifier.verify(body, secret, timestamp, signature)`: constant-time HMAC via `@suaveplan/crypto`; checks `|now - timestamp| <= skewMs`; throws `PAYMENT_SIGNATURE_FAILED` on mismatch, `PAYMENT_SIGNATURE_FAILED` on expired. Tests: valid, tampered, expired, missing timestamp.
- [ ] **`src/receipt/receipt-generator/`** — `generateReceipt(intent): Receipt`: maps intent fields; `formattedAmount` uses `Intl.NumberFormat` with currency. Tests: GBP formatting, USD formatting, partial refund status, all terminal statuses.
- [ ] **`src/utils/amount.ts`** — `formatAmount`, `toMinorUnits`, `fromMinorUnits`, `addAmounts`, `subtractAmounts`. `subtractAmounts` throws `PAYMENT_AMOUNT_INVALID` if result < 0. Tests: all functions, negative subtraction throws.
- [ ] **`src/core/payment-service/`** — `PaymentService`: `create`, `authorise`, `capture`, `refund`, `cancel` — each wraps state transition with idempotency guard (from `options.idempotency`); calls `options.adapter` for provider interaction; stores result in `options.store`; emits event on `options.bus`. `getReceipt(intentId)` fetches from store and calls `generateReceipt`. Tests: happy path create→authorise→capture, duplicate idempotency key returns cached result, refund > captured amount throws, `cancel` from terminal state throws.
---
## 4. OTel instrumentation
- [ ] Span `payment.transition` with attributes `payment.from_status`, `payment.to_status`, `payment.provider`; counter `payment.transition_total`; histogram `payment.transition_duration_ms`.
---
## 5. Property-based tests (`tests/property.test.ts`)
- [ ] State machine reachability: every valid create→terminal path through `ALLOWED_TRANSITIONS` terminates in `captured`, `refunded`, `failed`, or `cancelled`.
- [ ] Amount arithmetic: `addAmounts(a, b) === addAmounts(b, a)` for non-negative integers; `fromMinorUnits(toMinorUnits(x, 'GBP'), 'GBP') === x` for 0–1 000 000.
---
## 6. Benchmarks (`src/__benchmarks__/payment.bench.ts`)
- [ ] `PaymentFsm.transition` 100 000 times.
- [ ] `generateReceipt` 100 000 times.
---
## 7. Docs + verify
- [ ] One `.md` per implementation file (≥200 words, both examples, API reference). Root `README.md`.
- [ ] `bunx turbo lint typecheck test --filter=@suaveplan/payment` — green.
- [ ] `bunx turbo test:coverage --filter=@suaveplan/payment` — 100%.
- [ ] `bun scripts/publish.ts --package @suaveplan/payment`.
- [ ] Archive: `openspec/archive/2026-04-24-enterprise-payment/`.