Skip to content

docs(payments): rewrite PAYMENT-DEPLOYMENT.md + add config verifier script#99

Open
TortoiseWolfe wants to merge 1 commit into
mainfrom
docs/payment-deployment-fixes
Open

docs(payments): rewrite PAYMENT-DEPLOYMENT.md + add config verifier script#99
TortoiseWolfe wants to merge 1 commit into
mainfrom
docs/payment-deployment-fixes

Conversation

@TortoiseWolfe
Copy link
Copy Markdown
Owner

@TortoiseWolfe TortoiseWolfe commented May 20, 2026

Summary

A payment-readiness audit (2026-05-20) found 7 bugs in docs/PAYMENT-DEPLOYMENT.md plus a missing utility script. This PR ships docs/script fixes only — no application code changes. Tracked as a side-quest emerging from the broader payment-readiness investigation.

The deeper structural gap (8 missing Edge Functions) is now tracked as a 6-issue epic: #100 (with sub-issues #101#106).

Bug fixes in docs/PAYMENT-DEPLOYMENT.md

# Old (wrong) New (correct)
1 STRIPE_SECRET_KEY in .env.local (security violation — secret in committed-shaped file) All secrets go to Supabase Vault via supabase secrets set. .env is for NEXT_PUBLIC_* only. Matches .env.example canonical guidance.
2 supabase secrets set PAYPAL_CLIENT_ID=... PAYPAL_CLIENT_ID is browser-safe (NEXT_PUBLIC_*), belongs in .env
3 Function names: stripe-create-payment, paypal-create-subscription, send-receipt-email Real names: stripe-webhook, paypal-webhook, send-payment-email
4 PAYPAL_WEBHOOK_ID not mentioned Added — .env.example:147 already documents it as a required Vault secret
5 PayPal events PAYMENT.SALE.* (legacy API) Modern Orders v2 events PAYMENT.CAPTURE.*
6 Stripe CLI install: brew install ... (Mac-only) Added Linux/WSL2 tarball install path
7 Doc implied "set keys + deploy + go-live" works today New "Status of the integration" table at the top makes the missing Phase 0 work explicit. Links to epic #100 for context.

New: scripts/check-payment-config.sh

Companion verifier. Reads .env + queries supabase secrets list to confirm all 9 required env values are configured. Style mirrors existing scripts. Smoke-tested locally.

Exit codes: 0 = all set, 1 = missing, 2 = supabase CLI not available.

What this PR explicitly does NOT do

Related work

🤖 Generated with Claude Code

@TortoiseWolfe TortoiseWolfe force-pushed the docs/payment-deployment-fixes branch from 284cae7 to 4c7b805 Compare May 20, 2026 16:42
TortoiseWolfe added a commit that referenced this pull request May 20, 2026
…servability (#108)

ROOT CAUSE (still WebKit-specific, but at a deeper layer than rounds 11-13)

Even with rounds 11+12+13 applied (native scroll listener, sustained-
hidden modal verification, timing-budget separation), webkit-msg 1/1
still intermittently fails on `messaging-scroll.spec.ts:266`
"T007-T008: Jump button". PR #99 run 26176580568 caught one such
failure with all 3 retries failing.

Forensic from the failure-time screenshot at
`/tmp/pr99-flake/extracted/resources/21b9a69b...png` (extracted from
the blob report): the page IS loaded, the conversation IS open, the
test IS clicking through the flow correctly. The button truly never
renders.

The race:

  1. Test sets `messageThread.scrollTop = ...` + dispatches a native
     scroll event (round-11 pattern: works around React's onScroll
     not catching synthetic events on WebKit).
  2. Native event fires → handleScroll() runs → setShowScrollButton
     (true) queues a React state update.
  3. Test reads `scrollInfo` via a separate `.evaluate(...)` call —
     this reads DOM values, NOT React state.
  4. Test asserts `expect(jumpButton).toBeVisible()` with 5s timeout.
  5. React schedules the state update for the next microtask /
     animation frame.
  6. WebKit's event loop on Linux CI under heavy concurrent shard
     load sometimes defers this render past the 5s assertion window.

The DOM scroll position IS correct (step 1 + 3 agree). What's missing
is observability of the *component's reaction* to the scroll.

THE FIX (round 14a — first of two)

Add `data-show-scroll-button={showScrollButton ? 'true' : 'false'}`
to the MessageThread wrapper div. This attribute mirrors React state
to the DOM during commit — visible to Playwright the moment the
state-update flushes. The test now polls this attribute (via
expect.poll with 50/100/200/500ms intervals) BEFORE asserting the
button is visible.

Why this is the structural fix and not another mitigation:

  - The attribute is a direct projection of the component's internal
    state — there's no in-between system (React reconciler vs WebKit
    event loop) for the test to race against.
  - The wrapper element is always in the DOM (it's the outer
    `relative h-full` container, not the scrollable child) — so the
    attribute query never returns null.
  - The component now exposes a stable, documented observability
    boundary for any future test that needs to know when the scroll-
    button state is true.
  - This pattern matches the round-10/-12 lesson: trust attribute
    polling over event-driven assumptions when crossing browser
    engine boundaries.

WHAT THIS DOES NOT DO

  - Does NOT remove round-11's native scroll listener (it's still
    correct for catching the dispatched scroll event)
  - Does NOT remove round-12's sustained-hidden verification (it's
    still correct for the modal-retry flake)
  - Does NOT seed conversation data — that's round 14b in a follow-up
    PR. Round 14b fixes a separate failure mode (thin conversations
    not having enough scroll height to enter the `if` branch).

ROUND 14 PROGRESS

  | Round | PR | Layer |
  |---|---|---|
  | 14a (this) | TBD | data-show-scroll-button attribute (observability) |
  | 14b | follow-up | seed 30 messages into test conversation (data shape) |

VERIFICATION

  - 31/31 MessageThread unit tests pass (no regression)
  - Type-check clean
  - Lint clean
  - The E2E test `T007-T008: Jump button` now polls a state mirror
    that fires in the same React render as the button itself —
    eliminating the inter-system race.

Co-authored-by: TurtleWolfe <TurtleWolfe@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…fig.sh

AUDIT FINDINGS

A payment-readiness audit (2026-05-20 session) found `docs/PAYMENT-DEPLOYMENT.md`
contained multiple bugs that misled forkers and risked secret leakage:

  1. Lines 73-79 told users to put STRIPE_SECRET_KEY / PAYPAL_CLIENT_SECRET
     in `.env.local`. SECURITY VIOLATION. The template deploys to GitHub
     Pages (static export); non-NEXT_PUBLIC_ vars are never read by a Next
     server runtime. Server logic runs in Supabase Edge Functions which
     read secrets via Deno.env.get() after they're set in Supabase Vault.
     The `.env.example` already documents this correctly at lines 138-181;
     the deployment doc contradicted it.

  2. Line 93 `supabase secrets set PAYPAL_CLIENT_ID=...` — wrong. PAYPAL_
     CLIENT_ID is browser-safe (`NEXT_PUBLIC_*`), belongs in `.env`.

  3. Lines 106-110 listed 5 function names — 3 were fictional:
     `stripe-create-payment`, `paypal-create-subscription`, `send-receipt-
     email`. Actual function names (verified): `stripe-webhook`, `paypal-
     webhook`, `send-payment-email`.

  4. The doc did NOT mention `PAYPAL_WEBHOOK_ID` as a required Vault
     secret, despite `.env.example:147` documenting it.

  5. PayPal event list (lines 144-148) used legacy `PAYMENT.SALE.*` events
     instead of the modern Orders v2 `PAYMENT.CAPTURE.*` events that the
     `paypal-webhook` source code actually listens for (verified by grep).

  6. Stripe CLI install instructions were Mac-only (`brew install ...`).
     This template's docker-first dev environment runs on WSL2 / Linux too.

  7. The doc presented a "fill in keys + deploy + go-live" flow that
     implicitly claimed the integration was operational. Cross-referencing
     `grep -rohE "functions/v1/[a-z-]+" src/lib/payments src/components`
     against `ls supabase/functions/` revealed 8 outbound Edge Functions
     called by browser code that do NOT exist in the repo:

       create-stripe-checkout, verify-stripe-session,
       create-stripe-subscription, create-paypal-order,
       capture-paypal-order, create-paypal-subscription,
       cancel-subscription, resume-subscription

     The TODO comment in `src/lib/payments/stripe.ts:48` confirms this:
     "// (Edge Function will be created in Phase 5)". Phase 5 was never
     completed. Without these functions, clicking "Pay" on `/payment-demo`
     fails with a 404 from a nonexistent function URL.

CHANGES IN THIS COMMIT

`docs/PAYMENT-DEPLOYMENT.md` is fully rewritten:

  - New "Status of the integration" table at the top makes the missing
    Phase 0 work explicit — operators see this before sinking time into
    setup
  - Fixed function names match what's actually in `supabase/functions/`
  - Secrets-belong-in-Vault is the single canonical guidance (no .env
    fallback advice)
  - PAYPAL_WEBHOOK_ID added to Step 3.2
  - PayPal event list updated to the modern `PAYMENT.CAPTURE.*` family
    (the webhook code still listens for `PAYMENT.SALE.*` too for legacy
    compatibility, documented)
  - Stripe CLI install includes both macOS (brew) and Linux/WSL2 (tar)
    paths
  - New Step 6 "Smoke-test locally (after Phase 0 ships)" makes the
    dependency on the missing functions explicit
  - Common-issues table includes "Pay button fails with 404 → Outbound
    Edge Functions not deployed (Phase 0 work)"
  - References point at the open issues tracking Phase 0 work

`scripts/check-payment-config.sh` is a new verifier script (executable):

  - Reads `.env` and grep'd lines that look like `VAR=value`, masks the
    value for display, flags placeholders (`xxxx...`, `your-anon-key`)
  - Queries `supabase secrets list` to confirm all 5 Vault secrets are
    set
  - Exit 0: all 9 values present
  - Exit 1: at least one missing (with remediation hint)
  - Exit 2: supabase CLI not available (with install hint)

  Style mirrors `scripts/prp-to-feature.sh` and other shipped scripts
  (bash, set -e, ANSI colors).

  Smoke-tested locally: correctly identifies that NEXT_PUBLIC_SUPABASE_URL
  and ANON_KEY are set (existing dev env), STRIPE/PAYPAL not set, and
  supabase CLI missing → exit 2 with install instruction.

WHAT THIS PR DOES NOT DO

  - Does NOT write the 8 missing Edge Functions (tracked as separate
    follow-up issues; estimated 2-3 dev days)
  - Does NOT un-skip the E2E payment tests under `tests/e2e/payment/`
    (they would still fail without Phase 0)
  - Does NOT touch any application code (`src/`)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@TortoiseWolfe TortoiseWolfe force-pushed the docs/payment-deployment-fixes branch from 4c7b805 to 724425c Compare May 20, 2026 21:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants