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.
181 lines
11 KiB
Markdown
181 lines
11 KiB
Markdown
# 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 <gateway>/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.
|