Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions GITHUB_ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ Every issue inherits `AGENTS.md` and the definition of done in `IMPLEMENTATION_P
| 9 | AUTH-003 | Introduce scoped admin capabilities, MFA, and recent authentication | P1 | L | proposed | AUTH-001, AUTH-002, SEC-001 |
| 10 | ABUSE-001 | Enforce native App Check and privacy-preserving abuse limits | P0 | L | ready | CI-001 baseline |
| 11 | PAY-001 | Add strict request schemas and immutable monetary snapshots | P0 | L | ready | ABUSE-001 interface agreed |
| 12 | PROMO-001 | Disable unmodeled Stripe promotions until discounts are authoritative | P0 | S | partial_implemented_locally | PAY-001 for final discount contract |
| 12 | PROMO-001 | Disable unmodeled Stripe promotions until discounts are authoritative | P0 | S | source/tests complete under open GitHub issue #102; provider inventory and deployment remain owner action | PAY-001 for any future discount contract |
| 13 | PAY-002 | Implement idempotent payment commands and explicit state machines | P0 | L | ready after PAY-001 | CONFIG-001, PAY-001 |
| 14 | PAY-003 | Build idempotent, async-aware Stripe webhook ingestion | P0 | L | partial_implemented_locally | CONFIG-001, PAY-001/PAY-002 target contract |
| 14 | PAY-003 | Build idempotent, async-aware Stripe webhook ingestion | P0 | L | PAY-003A source merged in #101; PAY-003B/C remain open; not deployed | CONFIG-001, PAY-001/PAY-002 target contract |
| 15 | RACE-001 | Add transactional race-capacity reservations | P0 | L | proposed | PAY-002, PAY-003 event contract |
| 16 | MERCH-001 | Add SKU variants and transactional inventory reservations | P0 | L | proposed | PAY-002, PAY-003 event contract |
| 17 | PAY-004 | Make cancellation authoritative and replace reusable late Payment Links | P0 | L | proposed | PAY-002, PAY-003, RACE-001 |
Expand Down Expand Up @@ -567,7 +567,7 @@ Keep schemas close to the trusted Functions boundary and expose derived TypeScri
## PROMO-001 — Disable unmodeled Stripe promotions until discounts are authoritative

**Labels:** `priority:P0`, `type:security`, `area:stripe`, `size:S`
**Status:** `partial_implemented_locally`; creation and webhook defenses exist, but direct creator-payload tests and the future discount contract remain open
**Status:** source and exact tests are complete under open GitHub issue [#102](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/102); pre-change Session/provider inventory, protected deployment, provider readback, and live verification remain open, so do not close the GitHub issue yet
**Depends on:** PAY-001 for any future enabled discount model

### Problem
Expand All @@ -577,17 +577,18 @@ The original race and merchandise Checkout creators enabled arbitrary Dashboard
### Scope

- Set `allow_promotion_codes: false` in every current Checkout Session creator.
- Treat any nonzero Stripe `amount_discount` as a permanent anomaly requiring review while discounts are disabled.
- Add `schemaVersion: "1"` only to newly created Session and PaymentIntent metadata. This is an additive marker, not schema enforcement: legacy metadata remains accepted, and allowlisting/migration stays with PAY-003B.
- Require a complete Stripe adjustment breakdown and treat unknown or nonzero discount, tax, or shipping amounts as permanent anomalies requiring review while those features are disabled.
- Add creator payload tests and signed webhook tests, including a 100% discount and legacy outstanding Session.
- Inventory and disable/restrict active promotion configuration or outstanding Sessions in test/staging before release; production provider changes belong to OPS-001.
- If promotions are later desired, open a new issue under PAY-001/PAY-002 for server-approved code/campaign eligibility, immutable discount snapshot, actual total/refund/reconciliation, limits, expiry, audit, and migration before changing this guard.

### Acceptance criteria

- [ ] Neither race nor merchandise Session permits Stripe promotion-code entry.
- [ ] A nonzero discounted Session never becomes locally paid/fulfilled and is durably quarantined/alerted.
- [ ] Zero-discount Sessions continue through normal amount/currency validation.
- [ ] No UI or API claims a promotion is accepted.
- [x] Neither race nor merchandise Session payload permits Stripe promotion-code entry; automatic tax is also explicit-off.
- [x] Unknown or nonzero discount, tax, or shipping adjustments never make a local record paid/fulfilled and create durable review evidence.
- [x] Complete all-zero adjustment Sessions continue through normal amount/currency validation.
- [x] No checkout UI or API claims a promotion is accepted.
- [ ] Provider inventory/release checklist accounts for Sessions created before the change.

### Tests/evidence
Expand Down
4 changes: 2 additions & 2 deletions GITHUB_ISSUE_SLICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ An agent-ready child has one observable outcome, one trust boundary, explicit de
| --- | --- | --- | --- |
| CONFIG-001A | ready; CI-001A | Add typed server configuration requiring environment name, validated HTTPS/local origin, explicit Stripe mode, and environment/key compatibility; remove production fallbacks. | Missing/malformed/local-live tests fail before Firestore/Stripe/email. |
| CONFIG-001B | proposed; CONFIG-001A | Implement a server-owned global/per-domain commerce kill switch checked by every Session/refund/catalog command while webhook/reconciliation remain active. | Disabled-command tests prove no external call; webhook/reconcile tests still pass. |
| PROMO-001 | implemented_locally; PAY-001A for completion | Disable promotion codes in both Session creators and quarantine any nonzero Stripe discount until an approved snapshot contract exists. | Session payload assertions plus signed discounted-Session quarantine tests. |
| PROMO-001 | source/tests complete in [#102](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/102); provider inventory and deployment remain owner action; PAY-001A for future enablement | Disable promotion entry and automatic tax in both Session creators; require a complete Stripe breakdown; quarantine unknown or nonzero discount, tax, or shipping adjustments. | Exact Session payload assertions and signed zero/partial/full/malformed/failed/expired adjustment tests pass; pre-change Session/provider inventory and live verification remain open. |
| PAY-001A | ready; CI-001A | Add shared strict-object, bounds/depth, integer-cents/currency/date/URL/email primitives, safe errors, and redacted logging. | Prototype/deep/Unicode/money/unknown-key tests prove no external call. |
| PAY-001B | proposed; PAY-001A | Apply race/volunteer/free schemas, validate server event custom fields/options, and persist price/waiver snapshots. | Positive/hostile dynamic-field and snapshot tests. |
| PAY-001C | proposed; PAY-001A | Apply merchandise/variant/quantity schemas and immutable merchandise price snapshots. | Variant/quantity/money/payload tests. |
Expand All @@ -63,7 +63,7 @@ An agent-ready child has one observable outcome, one trust boundary, explicit de

| ID | Status/dependency | Bounded deliverable | Close evidence |
| --- | --- | --- | --- |
| PAY-003A | implemented_locally | Review/land current ingress slice: signature/method, explicit livemode, payment mode, event ledger, reference resolution, money/discount checks, monotonic refunds, terminal guards, quarantine, generic errors, webhook-only secret. | Functions lint; signed positive/negative/duplicate/out-of-order unit suite and residual-gap notes. |
| PAY-003A | source merged in [#101](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/101) via [PR #140](https://github.com/Run-MPRC/Run-MPRC.github.io/pull/140); not deployed | Review/land current ingress slice: signature/method, explicit livemode, payment mode, event ledger, reference resolution, money/adjustment checks, monotonic refunds, terminal guards, quarantine, generic errors, webhook-only secret. | Node 20 Functions lint and signed positive/negative/duplicate/out-of-order unit suite passed; protected deployment, Stripe destination rehearsal, and live verification remain open. |
| PAY-003B | proposed; PAY-002A–D, RACE/MERCH reducers | Move event handling to strict versioned schemas and canonical reducers; atomically finalize/release counters and outbox projections. | Emulator transactions for both domains, distinct duplicates, reorder, async and terminal cases. |
| PAY-003C | owner_action; A/B, CI-001A | Add retry/quarantine worker/lease semantics, TTL/dead-letter monitoring, and Stripe CLI test-mode delivery rehearsal. | Redacted signed immediate/delayed/retry/rotation evidence and alerts. |
| RACE-001A | proposed; PAY-002A | Add capacity/hold schema, expiry/indexes, compatibility reader, and dry-run counter backfill. | Full/closed/legacy fixture report and idempotent rerun. |
Expand Down
4 changes: 2 additions & 2 deletions IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,9 @@ Use structured correlation across checkout request, business record, Stripe Sess
The first tranche deliberately combines one urgent safety repair with the most complex payment foundation:

1. **SAFETY-001 — callback and emulator isolation:** #99 supplies the source/test boundary for same-origin callback preservation and loopback Auth/Firestore/Functions development. Provider calls, optimized previews, staging, and live callbacks remain separately gated.
2. **PAY-003 — webhook event inbox and safe reducer:** add event replay protection, async-aware payment confirmation, expected total/currency validation, direct metadata lookup, terminal-state guards, and lifecycle tests.
2. **PAY-003 — webhook event inbox and safe reducer:** PAY-003A [#101](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/101) source merged through [PR #140](https://github.com/Run-MPRC/Run-MPRC.github.io/pull/140) as `87bd1210575882cdd06214bcb98ed38ce00f71c2`, adding event replay protection, async-aware payment confirmation, expected total/currency validation, direct metadata lookup, terminal-state guards, and lifecycle tests. Firebase and Stripe live behavior remain unproven; PAY-003B/C remain open.
3. **SEC-001 — Firestore trust boundaries:** #100 merged through [PR #123](https://github.com/Run-MPRC/Run-MPRC.github.io/pull/123) at `a7fc301e85b0aeabe396e771faea21d3fc8e7b2b`. Source and 295 Rules tests remove recursive browser-admin authority. Firebase deployment explicitly skipped, so deployed/live Rules remain unproven under #105.
4. **PROMO-001 — fail-safe monetary policy:** disable promotion entry and quarantine unexplained discount/tax/shipping adjustments. This safety default is implemented locally; creator tests/provider inventory remain.
4. **PROMO-001 — fail-safe monetary policy:** [#102](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/102) disables promotion entry and automatic tax in both current creators, adds exact payload tests, and quarantines unknown or nonzero discount/tax/shipping adjustments on success, failure, and expiry. The private pre-change Session/provider inventory, protected deployment, provider readback, and live verification remain owner actions; no discount, tax, or shipping feature is authorized.
5. **Identity/config next:** AUTH-001A [#98](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/98) merged at `ce22c110`, proving source/tests for unverified-target rejection at the existing role-grant endpoints. Its Firebase deployment skipped, so backend-live status is unproven and the remaining AUTH-001 parent work stays open. Continue CONFIG-001 and the uncompleted AUTH-001 guards/Rules/refresh/audit scope without duplicating #98.
6. **Then:** PAY-001/PAY-002 so checkout creation, refunds, and every later handler share strict schemas and idempotent state.
7. **Then:** RACE-001 and MERCH-001 transactional reservations.
Expand Down
30 changes: 30 additions & 0 deletions OPERATIONS_RUNBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,36 @@ Required initial event set is documented in [STRIPE_COMMERCE_DESIGN.md](./STRIPE

For secret rotation, support the provider-approved overlap or controlled switchover, send a signed test event, verify the new secret, then retire the old secret and record evidence.

### Promotion-adjustment safety release — BLOCKED

**Purpose:** prevent an older promotion-enabled Stripe Session or provider setting from bypassing the repository guard.

**Approver:** treasurer plus platform owner; the Stripe account owner performs provider readback.

**Prerequisites:** merged [#102](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/102), green exact-commit checks, an isolated staging Firebase project, Stripe test mode, and a reviewed protected release plan that names `createCheckoutSession`, `createMerchCheckout`, and `stripeWebhook` together.

The current protected release plan names only the profile-recovery Functions. It cannot deploy this Stripe slice. Do not call #102 deployed until a separately reviewed immutable plan includes all three Stripe Functions and verifies them after deployment.

1. Have the Stripe account owner privately count still-open Sessions created before the #102 release candidate.
2. Record only redacted counts, mode, date range, and disposition. Do not copy customer details, Session URLs, promotion-code values, or provider identifiers into GitHub, email, screenshots, or AI.
3. Privately confirm whether promotion codes, coupons, automatic tax, or shipping rates can affect the test/staging Checkout configuration.
4. Expire or reconcile test/staging Sessions under the approved plan. Do not change production provider state in this procedure.
5. Run made-up race and merchandise payments in Stripe test mode. A complete all-zero completion must continue. An adjustment or missing breakdown on completion, failure, or expiry must create review evidence and never become paid/fulfilled. An ordinary all-zero failure or expiry cancels without creating a new warning.
6. Reconcile Stripe test totals with the synthetic Firebase records.
7. Release the three named Functions together only through the protected exact-commit gate.
8. Read back the deployed Functions and Stripe event destination separately.
9. Record Firebase deployment, provider readback, and observed behavior as three different results.

**Expected result:** new Sessions cannot accept promotion entry or automatic tax, and the webhook accepts only a complete all-zero adjustment breakdown. Failed or expired Sessions still cancel while keeping any review flag.

**Stop conditions:** missing owner, production mode, real customer/member data, an old open Session with no approved disposition, a release plan missing one Function, skipped/partial Firebase work, or unavailable provider readback.

**Success proof:** redacted private inventory, exact merge commit, exact CI run, protected release run, three Function readbacks, test-mode event evidence, and separate Stripe owner confirmation.

**Undo:** use one reviewed safe roll-forward or revert through the same three-Function gate. Do not re-enable promotions, edit payment records, delete webhook ledgers, or expire production Sessions by hand.

**Escalation:** treasurer plus platform owner; add the security owner for a suspected bypass and the privacy owner if customer information appeared.

## 9. Staging dress rehearsal

Before every major payment release:
Expand Down
Loading
Loading