From 77fd1869dbe694a7ff323a6fac07382e1f55b225 Mon Sep 17 00:00:00 2001 From: ced4568 Date: Thu, 27 Aug 2026 10:40:53 -0500 Subject: [PATCH] docs(stripe): document Phase 9.1 blocked status and live-mode readiness checklist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New: PHASE9_1_STRIPE_TEST_VALIDATION.md — baseline verification, the exact Stripe-credential search performed (none found), the Stripe architecture review (dynamic price_data, unchanged), what was proven vs. blocked per the mission's own step list, and the live-mode readiness checklist. Updated: STRIPE_INTEGRATION_RUNBOOK.md and WINTER_BREAK_LAUNCH_CHECKLIST.md to point at the new opt-in test and the blocked status. PHASE9_COMMERCE_AND_LAUNCH.md gets a short addendum. No application code changed this phase — no genuine defect was exposed without real Stripe traffic, so no regression rerun was required. --- docs/PHASE9_1_STRIPE_TEST_VALIDATION.md | 180 ++++++++++++++++++++++++ docs/PHASE9_COMMERCE_AND_LAUNCH.md | 6 + docs/STRIPE_INTEGRATION_RUNBOOK.md | 9 +- docs/WINTER_BREAK_LAUNCH_CHECKLIST.md | 6 + 4 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 docs/PHASE9_1_STRIPE_TEST_VALIDATION.md diff --git a/docs/PHASE9_1_STRIPE_TEST_VALIDATION.md b/docs/PHASE9_1_STRIPE_TEST_VALIDATION.md new file mode 100644 index 0000000..081509e --- /dev/null +++ b/docs/PHASE9_1_STRIPE_TEST_VALIDATION.md @@ -0,0 +1,180 @@ +# Phase 9.1 — Real Stripe TEST-Mode Integration Validation + +Status: **BLOCKED — no Stripe TEST credentials are available to this session.** This document +records exactly what was verified without them, exactly what Chase needs to provide, and the +permanent, opt-in validation script that will run the real proof the moment those credentials +exist. No live money was risked; no Stripe network call of any kind was made during this phase. + +## 1. Baseline verification + +- Branch: `main`. Working tree: clean at start. +- `git fetch origin` — HEAD and `origin/main` both `80a5f6e4576cb9b83a60c9e9b57e4ebc52b62f07`, + exactly the Phase 9 milestone commit. +- Phase 9 payment code and documentation inspected in full: `payment-provider.js`, + `commerce.js`, `webhook-routes.js`, `offerings.js`'s registration/conversion path, + `admin-offerings-routes.js`'s payment routes, `integration-routes.js`'s checkout/agreement/ + status routes, `platform/test/e2e-phase9.js`, `infra/scripts/drills/ + test-phase9-winter-camp-commerce.js`, `docs/STRIPE_INTEGRATION_RUNBOOK.md`, `docs/ + WINTER_BREAK_LAUNCH_CHECKLIST.md`. Nothing was changed as a result of this review — no defect + was found, and none was invented to justify a change (§1/§22's own instruction). + +## 2. Stripe TEST credential search — none found + +Searched, in order: process environment (`env | grep -i stripe`), every `.env`/`.env.example` +file reachable from this repository and the operator's home directory, this repository's own +Infisical project wiring (none exists — no `.infisical.json` in this repo), and this session's +own memory of prior credential-provisioning work (none references Stripe or this project). No +`STRIPE_SECRET_KEY`, no `STRIPE_WEBHOOK_SECRET`, in any test or live form, exists anywhere this +session can reach. + +**This is exactly STOP condition 1/2 from this phase's own authorization.** Per §2/§27: do not +fake validation, stop after documenting exactly what's needed. + +### What Chase needs to provide or configure + +1. A Stripe account (test mode is available on any account, including one with no live + business verification completed yet). +2. From the Stripe Dashboard, in **Test mode**: the test **Secret key** (`sk_test_...`) → + `STRIPE_SECRET_KEY`. +3. A webhook signing secret for a test endpoint — either a permanent test-mode webhook endpoint + configured in the dashboard, or the ephemeral one the Stripe CLI prints when running + `stripe listen --forward-to /api/webhooks/payment` → `STRIPE_WEBHOOK_SECRET`. Full + setup steps already documented in `docs/STRIPE_INTEGRATION_RUNBOOK.md`'s "Local testing + against real Stripe TEST mode" section (written during Phase 9, unchanged and still accurate). +4. Both values set as environment configuration on the running Platform API process + (`PAYMENT_PROVIDER=stripe` alongside them) — never committed to git, never placed in any + browser-reachable configuration. **No credential value appears anywhere in this document, any + commit, or any log produced during this phase.** + +Once those exist, run the permanent validation script this phase adds (§4 below) to produce the +actual proof this phase was asked to deliver. + +## 3. Stripe adapter architecture — confirmed, unchanged + +Inspected `payment-provider.js`'s `StripeProvider.createCheckoutSession()`: it uses **dynamic +Checkout line-item `price_data`** (`unit_amount`/`currency`/`product_data.name` supplied inline +per session), **not** pre-created Stripe Price/Product IDs. RootNode's `offerings` table remains +the sole source of truth for identity, price, and currency; Stripe never becomes a second +business-domain model — no Stripe object id is ever treated as anything other than an opaque +`provider_reference`. This matches §3's explicit constraint and required no change. + +## 4. Permanent opt-in Stripe integration test — added + +`platform/test/stripe-integration-test.js` (new). Default behavior (no environment variable +set): prints a skip message and exits `0` with **zero Stripe network traffic** — confirmed by +running it with no configuration at all. **Verified this run**: + +``` +$ node test/stripe-integration-test.js +Stripe integration test skipped (RUN_STRIPE_INTEGRATION_TEST is not "true") — no Stripe network traffic, no credentials required. +``` + +Enabled via `RUN_STRIPE_INTEGRATION_TEST=true`, reusing the exact `STRIPE_SECRET_KEY`/ +`STRIPE_WEBHOOK_SECRET`-adjacent naming Phase 9 already established (only `STRIPE_SECRET_KEY` is +actually needed by the script itself, to independently query Stripe's own API — the webhook +secret only needs to be configured on the server process, not read by this script). Guard +clauses verified directly, without needing real credentials: + +| Condition | Verified behavior | +|---|---| +| `OWNER_EMAIL`/`OWNER_PASSWORD` unset | Exits `1` with a clear message. | +| `STRIPE_SECRET_KEY` unset | Exits `1`, names the exact variable needed. | +| `STRIPE_SECRET_KEY` starts with `sk_live_` | **Refuses to run** — "TEST MODE ONLY" — exits `1`. | +| `STRIPE_SECRET_KEY` any other non-test shape | Refuses to run out of caution — exits `1`. | +| A syntactically valid `sk_test_...` value, no real stack running | Reaches `main()` cleanly, fails only on the expected connection error — confirms no syntax defect in the enabled code path. | + +When real test credentials exist and the dev stack is running with `PAYMENT_PROVIDER=stripe`, +this script will automatically and honestly prove, via pure API calls (no browser needed): + +- RootNode creates a **real** Stripe TEST Checkout Session (not RootNode's own claim — verified + by retrieving the session directly from Stripe's own `checkout.sessions.retrieve` API). +- The amount/currency Stripe actually recorded matches the offering's server-side price exactly, + even when the request is sent with a tampered `amount`/`currency` in the body — proving + server-authoritative pricing against **Stripe's own object**, the strongest form of that proof. +- `session.livemode === false` is asserted directly — the script itself refuses to consider its + own run valid against anything but a real TEST-mode session. +- RootNode's webhook route rejects an invalid/garbage signature cleanly, with **zero domain side + effect** (the registration's status is re-read and asserted unchanged). + +What it deliberately does **not** automate: actually completing Stripe's hosted Checkout page. +There is no supported pure-API way to "pay" a Checkout Session, and fabricating a signed +"payment succeeded" webhook for a session nobody actually paid would itself be exactly the kind +of faked validation this phase was told never to produce. Instead, the script prints the real +checkout URL and polls RootNode's own status endpoint for a bounded window (default 90s, +`STRIPE_INTEGRATION_TEST_WAIT_SECONDS`) while a human completes payment in a browser (Stripe's +test card `4242 4242 4242 4242`) or a parallel `stripe listen --forward-to` relays Stripe's real +event — see `docs/STRIPE_INTEGRATION_RUNBOOK.md`. If nothing arrives in that window (expected in +an unattended run), it says so plainly rather than reporting a false pass. + +Never runs as part of normal regression suites or CI — no other test file requires or imports it, +and its own default is a guaranteed no-op. + +## 5. What was actually proven this phase + +| Step from the mission spec | Result | +|---|---| +| 1. Baseline verification | Done — see §1. | +| 2. Stripe TEST-mode safety | No live-mode action was possible or attempted; no credentials of any kind exist. | +| 3. Stripe architecture review | Done — dynamic `price_data`, confirmed, unchanged (§3). | +| 4-20 (disposable offering through security revalidation, all requiring a real Stripe TEST payment/webhook round trip) | **Not performed — blocked on missing credentials.** Faking any of these was explicitly disallowed. | +| 21. Permanent opt-in integration test | **Built** (§4) — the automatable portion is real and will run the moment credentials exist; the human/CLI-dependent portion is honestly deferred, not faked. | +| 22. Application changes | None made — no genuine defect was exposed (none could be, without real Stripe traffic), so §22's "smallest correct fix" rule had nothing to apply to. | +| 23. Regression | No registration/agreement/enrollment/notification/frontend code changed this phase, so per §23's own conditional rule, no regression rerun was required. The new test file's default no-op path was verified directly (§4) instead. | +| 24. Documentation | This document, plus the update noted below. | +| 25. Live-mode readiness checklist | See §6. | + +## 6. Live-mode readiness checklist + +Everything Stripe-side is still pending exactly as `docs/STRIPE_INTEGRATION_RUNBOOK.md`'s "Going +live" section already lays out, restated here as a checklist per §25: + +**Stripe:** +- [ ] Business/account verification completed in the Stripe dashboard. +- [ ] Live API secret key (`sk_live_...`) generated. +- [ ] A permanent live-mode webhook endpoint created, pointed at the production API's + `/api/webhooks/payment` URL, and its signing secret (`whsec_...`) captured. +- [ ] Payout/bank details configured in Stripe. +- [ ] Business/support information (statement descriptor, support contact) configured in Stripe. + +**RootNode:** +- [ ] A real, HTTPS production URL for the Platform API (Stripe requires HTTPS for live webhooks). +- [ ] `STRIPE_SECRET_KEY`/`STRIPE_WEBHOOK_SECRET` (live values) provisioned into production + secret configuration only. +- [ ] Production deployment itself (explicitly out of scope for this phase and Phase 9 — + unchanged status). +- [ ] Backup/monitoring in place for the production database holding `payment_transactions`. +- [ ] The real Winter Break offering configured with its real price, once decided. + +**Business** (Chase's decisions, not invented here): final price, refund/cancellation policy, +camp dates, venue, capacity — all still open, tracked in `docs/ +WINTER_BREAK_LAUNCH_CHECKLIST.md`. + +**Legal** (unchanged from every prior phase's own list): the actual agreement/waiver text, +guardian/minor consent review, privacy/retention requirements — see `docs/ +STUDENT_GUARDIAN_DATA_MODEL.md`. + +Nothing above is marked complete — none of it has been verified, and this phase did not attempt +to invent or shortcut any of it. + +## 7. Bugs found + +None. No genuine defect was exposed because no real Stripe traffic was possible this phase. + +## 8. Security posture — reasoned about, not re-verified against real Stripe + +Every item in the mission's §20 checklist was already true and tested against the deterministic +provider in Phase 9 (`e2e-phase9.js`), and nothing about switching providers changes the code +path that enforces them (the same `commerce.js`/`webhook-routes.js` code runs regardless of +which provider is active — see §3). This phase did not and could not re-verify them against +REAL Stripe traffic specifically; that re-verification is exactly what `stripe-integration-test.js` +(§4) will perform automatically the moment credentials exist. + +## Final status + +**PHASE 9.1 BLOCKED — NO STRIPE TEST CREDENTIALS AVAILABLE.** + +No real Stripe TEST Checkout, real TEST payment, or real signed Stripe webhook was created or +observed this phase — none of the actions requiring them were attempted, per this phase's own +explicit instruction never to fake validation. The permanent, opt-in `stripe-integration-test.js` +is ready and waiting; provide `STRIPE_SECRET_KEY`/`STRIPE_WEBHOOK_SECRET` (test mode) and set +`RUN_STRIPE_INTEGRATION_TEST=true` to run the real proof. diff --git a/docs/PHASE9_COMMERCE_AND_LAUNCH.md b/docs/PHASE9_COMMERCE_AND_LAUNCH.md index df1a4b0..5c3ffa0 100644 --- a/docs/PHASE9_COMMERCE_AND_LAUNCH.md +++ b/docs/PHASE9_COMMERCE_AND_LAUNCH.md @@ -271,6 +271,12 @@ Real Stripe live-mode validation once credentials exist (`docs/STRIPE_INTEGRATIO text once reviewed; and, informed by real Winter Break Camp commercial operation, whether a waitlist is actually worth building. +**Phase 9.1 addendum**: real Stripe TEST-mode validation was attempted immediately after this +phase and remained blocked on the exact same missing credentials — see `docs/ +PHASE9_1_STRIPE_TEST_VALIDATION.md`. That phase added a permanent, opt-in integration test +(`platform/test/stripe-integration-test.js`, default no-op, refuses live-mode keys) ready to run +the real proof the moment `STRIPE_SECRET_KEY`/`STRIPE_WEBHOOK_SECRET` (test mode) exist. + --- ## PHASE 9 APPLICATION BUILD COMPLETE / LIVE STRIPE VALIDATION PENDING diff --git a/docs/STRIPE_INTEGRATION_RUNBOOK.md b/docs/STRIPE_INTEGRATION_RUNBOOK.md index 1e86e1c..2a0613d 100644 --- a/docs/STRIPE_INTEGRATION_RUNBOOK.md +++ b/docs/STRIPE_INTEGRATION_RUNBOOK.md @@ -74,8 +74,13 @@ test). ## Local testing against real Stripe TEST mode (once credentials exist) -Not yet exercised in this repo — no Stripe account/credentials have been provided as of this -phase. Once they exist: +Not yet exercised in this repo — no Stripe account/credentials have been provided as of Phase +9.1 either (see `docs/PHASE9_1_STRIPE_TEST_VALIDATION.md` for the exact search performed and +what's needed). Once they exist, `platform/test/stripe-integration-test.js` (added in Phase 9.1) +automates most of the steps below — set `RUN_STRIPE_INTEGRATION_TEST=true` plus +`STRIPE_SECRET_KEY`/`STRIPE_WEBHOOK_SECRET`/`OWNER_EMAIL`/`OWNER_PASSWORD` and run it; it refuses +to run against a live-mode key and makes zero Stripe network calls unless explicitly enabled. +Manually, or to understand what that script does: 1. Create (or use an existing) Stripe account, switch to **Test mode** in the dashboard. 2. Copy the test-mode **Secret key** (`sk_test_...`) into `STRIPE_SECRET_KEY`. diff --git a/docs/WINTER_BREAK_LAUNCH_CHECKLIST.md b/docs/WINTER_BREAK_LAUNCH_CHECKLIST.md index 1321aed..e1c0e15 100644 --- a/docs/WINTER_BREAK_LAUNCH_CHECKLIST.md +++ b/docs/WINTER_BREAK_LAUNCH_CHECKLIST.md @@ -46,6 +46,12 @@ legal reviewer, or Stripe, not from more code. ## Payments — pending real Stripe credentials +Phase 9.1 searched for Stripe TEST credentials specifically to run real validation and found +none available anywhere this session could reach — see `docs/ +PHASE9_1_STRIPE_TEST_VALIDATION.md` for the exact search and what Chase needs to provide. A +permanent, opt-in test (`platform/test/stripe-integration-test.js`) is ready to run the real +TEST-mode proof the moment `STRIPE_SECRET_KEY`/`STRIPE_WEBHOOK_SECRET` exist. + - [ ] Real Stripe account created/available (live mode). - [ ] `STRIPE_SECRET_KEY`/`STRIPE_WEBHOOK_SECRET` (live values) provisioned into production configuration only — see `docs/STRIPE_INTEGRATION_RUNBOOK.md`'s "Going live" section.