docs(checkout): document pricing boundary and verification evidence
This commit is contained in:
parent
44b74e69c7
commit
812b75279a
|
|
@ -30,3 +30,4 @@ Production must use a managed secret store, TLS termination, a restricted databa
|
|||
|
||||
Identity: see [API contract](docs/identity-api.md) and [setup/release guide](docs/identity-operations.md).
|
||||
Phase 1C adds catalog, private addresses and inventory. See [commerce API](docs/commerce-api.md), [errors](docs/error-contract.md), [migrations](docs/migrations.md), [security preparation](docs/vapt-readiness.md) and [verification](docs/verification.md).
|
||||
Phase 1D adds versioned carts, coupons, atomic checkout and private order snapshots. See [checkout API and pricing boundary](docs/checkout-api.md). Payment remains disabled until tax, shipping and payment rules are finalized.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
# Phase 1D: carts, coupons and orders
|
||||
|
||||
All paths below start with /api/v1. Customer routes require an active bearer session and operate only on that user's organization and data. Customers do not need staff inventory permissions. Accounts currently use the existing provisioning flow.
|
||||
|
||||
| Method and path | Access |
|
||||
| -------------------------------------------- | --------------------------------------------------------------------------- |
|
||||
| GET /cart | Own cart |
|
||||
| PUT /cart/lines/:variantId | Own cart; body: quantity, version |
|
||||
| DELETE /cart/lines/:variantId | Own cart; body: version |
|
||||
| POST /checkout | Own cart; body: cartVersion, addressId, idempotencyKey, optional couponCode |
|
||||
| GET /orders, GET /orders/:id | Own orders |
|
||||
| POST /orders/:id/cancel | Own order |
|
||||
| GET /admin/orders, GET /admin/orders/:id | orders.read |
|
||||
| POST /admin/orders/:id/cancel | orders.manage |
|
||||
| GET/POST /coupons, PATCH /coupons/:id/status | coupons.manage |
|
||||
|
||||
Lists accept limit (1–100, default 25) and offset (0–10000). Staff order lists return summaries; private address snapshots are available only in authorized detail responses. New permissions are granted to existing system roles by migration; custom roles keep their explicit permissions.
|
||||
|
||||
## Cart and checkout contract
|
||||
|
||||
GET /cart returns version and lines with current product information. PUT sets an absolute quantity of 1–100. A cart contains at most 20 variants in one currency. Every edit requires the last version and increments it. Stale writes fail with CART_CHANGED. A cart does not hold stock.
|
||||
|
||||
POST /checkout requires a UUID idempotencyKey and the expected cartVersion. It rechecks product availability, calculates current server prices, validates a private saved address and any coupon, creates immutable snapshots and reserves stock for 15 minutes. It then clears the cart and increments its version. These writes and the audit record share one transaction; failures preserve the cart and roll back orders and holds.
|
||||
|
||||
A matching retry returns the original order with its current status even after cart clearing. Reusing the same key with changed input returns IDEMPOTENCY_CONFLICT. Keys are scoped to the user and organization. Use a new key for a new checkout intent.
|
||||
|
||||
Orders snapshot SKU, product/variant names, unit prices, quantities, line totals, currency, coupon rules, subtotal, discount, merchandise total and address. Later catalog/address edits do not alter them. Decimal output strings can omit trailing zeroes. Calculations use integer minor units and percentage discounts round half up to the nearest minor unit. SQL enforces line arithmetic and reconciles order subtotals at transaction commit.
|
||||
|
||||
## Pricing and payment boundary
|
||||
|
||||
Orders start as PENDING_PAYMENT but expose pricingStatus UNFINALIZED, paymentAvailable false, and null taxTotal, shippingTotal and payableTotal. Merchandise total is subtotal minus discount; it is not a final amount to charge. Null charges must never be rendered as free shipping or zero tax.
|
||||
|
||||
Tax and shipping rules have not been supplied. This phase makes no assumption about tax treatment or delivery charges and does not create payments. Before enabling payment, finalize and snapshot those rules through a new migration and implement the verified payment lifecycle in Phase 1E. A fully discounted order still requires that workflow.
|
||||
|
||||
## Coupon rules
|
||||
|
||||
POST /coupons accepts code, currency, minimumSubtotal, maxUses, perUserLimit, startsAt and endsAt. FIXED coupons also require a positive amount string with two fractional digits. PERCENT coupons require percentBps from 1 to 10000; 1000 means 10%. Codes are normalized to uppercase. Dates require explicit timezone offsets. Ends must follow starts.
|
||||
|
||||
Only one coupon can apply to an order. It must be active, within its date window, in the cart currency, above the minimum subtotal and within total and per-user usage limits. Fixed discounts are capped at subtotal. Rules are immutable; create a new code to change them. PATCH status accepts only active.
|
||||
|
||||
Usage is held by unexpired pending orders. Cancellation and expiry free that capacity. Future paid-order redemption must remain counted when payments are introduced. Ineligible customer responses remain generic; logs carry distinct diagnostic reasons without exposing private data.
|
||||
|
||||
## Inventory and order lifecycle
|
||||
|
||||
Checkout selects available stock across warehouses in stable stock-ID order, with a maximum of 200 candidate stock items per checkout. Stock row locks coordinate with standalone reservations and adjustments. An account may have at most ten unexpired pending orders.
|
||||
|
||||
Reservations reduce availability, not on-hand stock. Order holds cannot be committed/released through standalone inventory APIs. Cancellation releases all holds atomically and is idempotent; it does not rebuild the cart. After 15 minutes, pending orders display EXPIRED and their holds stop consuming availability without a cleanup job. Expired orders are not payable. The later payment phase must recheck expiry and define stock commitment before fulfillment.
|
||||
|
||||
The current organization lock serializes checkout with catalog, address, coupon and cart writes. This favors correctness within the existing architecture; benchmark contention on native PostgreSQL before scaling traffic. Native tests cover duplicate requests, coupon competition and checkout versus standalone inventory reservation.
|
||||
|
|
@ -7,3 +7,4 @@ Each defined error has a distinct code and message. The global filter logs the e
|
|||
Login failures deliberately share a public message to prevent enumeration; internal diagnostics distinguish causes. Unknown failures return INTERNAL_FAILURE with a safe message. Known database failures are classified centrally.
|
||||
|
||||
Responses include X-Request-Id and Cache-Control: no-store. Throttled responses include Retry-After seconds. Client request IDs are not trusted. Configure restricted log access, retention and alerting at deployment.
|
||||
Checkout codes are defined in src/common/errors/checkout-errors.ts. Coupon eligibility failures share a safe public response with separate internal diagnostic reasons.
|
||||
|
|
|
|||
|
|
@ -12,3 +12,4 @@ The check normalizes line endings and rejects modified or missing recorded migra
|
|||
Production uses `pnpm db:deploy`, then `pnpm db:status`. Never use db push in production. Schema diff tooling targets the whole prisma directory. Destructive changes need an expand/backfill/contract rollout and recovery planning.
|
||||
|
||||
Phase 1C appends four migrations after the original three: catalog/addresses, inventory, commerce integrity and inventory actor scope. SQL maintains additional integrity constraints and the append-only ledger trigger.
|
||||
Phase 1D appends checkout tables, reservation ownership, immutable snapshot guards and deferred order/line reconciliation. Earlier migrations are unchanged.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ Source: Mani Candles Commerce Platform specification and project pack created in
|
|||
- 1A (implemented): service bootstrap, configuration, database lifecycle, initial organization migration, health API, test/build baseline, team workflow.
|
||||
- 1B (implemented; native CI and deployment configuration pending): authentication, sessions, recovery, users, configurable RBAC, organization access, approval status, audit events. Test denied access and cross-organization access.
|
||||
- 1C (implemented; native concurrency CI pending): catalog, variants, collections, addresses, inventory ledger and reservations. Test concurrent reservation and stock reconciliation.
|
||||
- 1D: cart, checkout, orders, coupons and pricing snapshots. Test money precision, discount eligibility, retries and transaction rollback.
|
||||
- 1D (core implemented; tax/shipping rules, payment enablement and native CI pending): cart, checkout, orders, coupons and pricing snapshots. Test money precision, discount eligibility, retries and transaction rollback.
|
||||
- 1E: verified payments/refunds, shipping/tracking, returns, notifications and operational dashboard. Test signatures, replay, partial fulfillment and reconciliation.
|
||||
|
||||
## Phase 2: Internal operations
|
||||
|
|
|
|||
|
|
@ -11,3 +11,4 @@ Before release, run native PostgreSQL concurrency tests and Gitea CI, validate T
|
|||
Live SMTP and the recovery frontend remain pending. Recovery delivery is synchronous until the notification queue milestone; assess timing-based enumeration with the real adapter. No external email or production deployment was performed.
|
||||
|
||||
References: [OWASP ASVS](https://owasp.org/www-project-application-security-verification-standard/) and [Logging Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html). Conformance has not been independently assessed.
|
||||
Phase 1D adds version checks, scoped idempotency, immutable order snapshots, deferred total reconciliation, order-held stock, coupon quotas and resource bounds. Native checkout race tests are included but await TEST_DATABASE_URL. Tax/shipping pricing and payment enablement remain blocked on business rules and Phase 1E.
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
# Verification record — Phase 1C
|
||||
# Verification record — Phase 1D
|
||||
|
||||
- 131 passing tests across 21 suites; two native PostgreSQL concurrency tests are skipped without TEST_DATABASE_URL.
|
||||
- Coverage: 99.61% statements, 99.56% lines, 87.21% branches and 100% functions for measured application code.
|
||||
- 154 passing tests across 28 suites; five native PostgreSQL concurrency tests are skipped without TEST_DATABASE_URL (one suite is entirely native).
|
||||
- Coverage: 99.54% statements, 99.65% lines, 87.83% branches and 100% functions for measured application code.
|
||||
- Formatting, migration checksums, Prisma validation, strict TypeScript checks and production compilation pass.
|
||||
- All seven migrations execute through Prisma migrate deploy; status is up to date and schema diff reports no drift against the disposable embedded PostgreSQL engine. The original three migrations remain unchanged; four timestamped migrations were appended.
|
||||
- Tests cover publication, private addresses, stock reconciliation, reservation expiry/retry/commit, append-only ledgers, organization boundaries and safe HTTP errors.
|
||||
- Production dependency audit reports zero advisories after targeted transitive dependency overrides.
|
||||
- All eleven migrations execute through Prisma migrate deploy; status is up to date and schema diff reports no drift against the disposable embedded PostgreSQL engine. The prior seven migrations remain unchanged; four timestamped migrations were appended.
|
||||
- New tests cover private/versioned carts, exact discount arithmetic, coupon eligibility, immutable order snapshots, deferred subtotal reconciliation, authorization, stock allocation, idempotent checkout/cancellation and rollback after final-write failure.
|
||||
- Production dependency audit reports no known vulnerabilities. No dependency versions changed in this phase.
|
||||
|
||||
Native PostgreSQL concurrency and remote Gitea CI remain pending. CI provisions PostgreSQL 17 for competing reservations and recovery consumption. Embedded tests do not establish multi-connection locking behavior.
|
||||
Native PostgreSQL concurrency and remote Gitea CI remain pending. CI provisions PostgreSQL 17. Native tests cover recovery consumption, inventory competition, checkout replay, coupon competition and checkout versus standalone inventory reservations. Embedded tests do not establish multi-connection locking behavior.
|
||||
|
||||
No production deployment, real SMTP delivery or formal VAPT was performed. See [assessment preparation](vapt-readiness.md) for release checks.
|
||||
Tax/shipping rules have not been supplied. Orders expose unfinalized pricing, null payable totals and disabled payments. Full checkout-to-payment acceptance remains pending those rules and Phase 1E. No production deployment, real SMTP delivery or formal VAPT was performed. See [checkout contract](checkout-api.md) and [security assessment preparation](vapt-readiness.md).
|
||||
|
||||
Git author: mihir <motiyanimihir@gmail.com>. Branch: feat/catalog-inventory, based on fetched main.
|
||||
Git author: mihir <motiyanimihir@gmail.com>. Branch: feat/checkout-orders, based on merged Phase 1C at 72cb947.
|
||||
|
|
|
|||
Loading…
Reference in New Issue