PR #165 (legal/contact pages + MarketingFooter) merged as ba45cfe.
All code blockers for self-serve cutover are now on main.
HANDOFF now flags that user is reporting trouble completing Stripe
live-mode activation (specific blocker not captured this session)
and lists likely candidates so next session can triage quickly:
apex DNS still missing at Namecheap, business-profile URL/phone
rejection, missing prod env var, webhook signing-secret mismatch,
or tax/identity verification step.
Adds the new /contact and /policies URLs to the Stripe business-
profile checklist, and a carry-forward note about the mailing-
address TODO that fires when the P.O. Box arrives. Appends a
2026-05-12 SESSION_LOG entry covering PR #164/#165 merges and the
current Stripe block.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
51 lines
6.7 KiB
Markdown
51 lines
6.7 KiB
Markdown
<!-- Keep under ~2K tokens. Old handoffs live in SESSION_LOG.md. Do not let this file accumulate history. -->
|
|
|
|
# HANDOFF.md
|
|
|
|
**Last updated:** 2026-05-12
|
|
|
|
**Active task:** Phase O cutover for self-serve signup. All code blockers are closed on `main` (PR #164 `3f04911`, PR #165 `ba45cfe`). **Currently blocked on Stripe live-mode activation** — user reports trouble completing it as of 2026-05-12 evening; specific blocker not captured in this session's doc update. Next session: ask the user for the exact Stripe error or stuck step, then triage. Likely candidates given prior session work: apex DNS still missing at Namecheap (Stripe's site verifier can't reach `resolutionflow.com` apex; `www` works); Stripe business profile rejecting one of the new URLs (`/contact`, `/policies`) or the phone number; missing prod env var (`STRIPE_SECRET_KEY` / `STRIPE_WEBHOOK_SECRET` / `VITE_STRIPE_PUBLISHABLE_KEY`); webhook signing-secret mismatch; tax/identity verification step blocking activation.
|
|
|
|
## Where this session ended
|
|
|
|
PR #165 squash-merged (`ba45cfe feat(legal): add /policies, /contact, /promotions pages + MarketingFooter (#165)`):
|
|
|
|
- **New pages**, all SPA, matching existing `/privacy` and `/terms` pattern: `/policies` (consolidated Customer Policies — customer service contact, return/refund/dispute policy, cancellation, U.S. legal and export restrictions, promotional terms; anchor IDs per subsection), `/contact` (phone **(470) 949-4131**, support/sales/billing/security inboxes, response-time SLAs), `/promotions` (stub stating no promotions currently active — satisfies Policies §6.2 cross-ref).
|
|
- **`MarketingFooter`** (`frontend/src/components/common/MarketingFooter.tsx`) extracted from inline landing footer and mounted on `/landing`, `/pricing`, `/contact-sales`. Reuses existing `landing-footer*` CSS — must be rendered inside a `.landing-page` wrapper (documented in a JSX comment) because `--lp-*` vars are scoped there. All four legal links (Privacy / Terms / Policies / Contact) are now reachable from every marketing surface.
|
|
- **Privacy and Terms closing sections** updated to point at `/contact` + `/policies` and the correct inbox per area (`security@` and `support@` respectively). Stale `hello@resolutionflow.com` mailto removed everywhere.
|
|
- **Mailing address** left as TODO comments in `ContactPage.tsx` and `PoliciesPage.tsx` (one each). Rendered publicly as "available on request — email support@". Fill in when the P.O. Box is purchased.
|
|
|
|
`tsc --project tsconfig.app.json --noEmit` and `eslint` clean. Local `vite build` and `tsc -b` are blocked by root-owned `node_modules/.tmp` and `node_modules/.vite-temp` cache directories — CI rebuilds from a clean env and was green.
|
|
|
|
Working tree clean (only pre-existing untracked files: `abc-feat-self-serve-signup-phase-2-design-...md`, `core.*`, `docs/architecture/`, `docs/tutorials/` — same set noted in prior handoffs as "do not stage").
|
|
|
|
Single alembic head: `4ce3e594cb87` (no schema changes in this PR).
|
|
|
|
## Resume point
|
|
|
|
**Phase O manual ops** — entirely user-side, gated on the apex DNS fix below:
|
|
|
|
1. **Stripe Dashboard live-mode:**
|
|
- 3 Products (Starter, Pro, Enterprise). Monthly Prices for Starter ($19.99) + Pro ($29.99). No Prices on Enterprise (sales-led).
|
|
- Customer Portal with plan-switching disabled.
|
|
- Webhook at `https://api.resolutionflow.com/api/v1/webhooks/stripe` with 5 events. Save live signing secret.
|
|
- **Business profile fields**: Customer service URL `https://resolutionflow.com/contact`. Refund/cancellation policy URL `https://resolutionflow.com/policies`. Terms `https://resolutionflow.com/terms`. Privacy `https://resolutionflow.com/privacy`. Phone `(470) 949-4131`. Mailing address per Stripe form (not required on website).
|
|
2. **Railway prod env**: `STRIPE_SECRET_KEY=sk_live_...`, `STRIPE_WEBHOOK_SECRET`, `STRIPE_PUBLISHABLE_KEY` + `VITE_STRIPE_PUBLISHABLE_KEY` (frontend redeploy required — Vite bake-at-build, Lesson 60), `OAUTH_REDIRECT_BASE=https://resolutionflow.com`, `SELF_SERVE_ENABLED=false` (still false at this point), `INTERNAL_TESTER_EMAILS=<allowlist>`, prod Google + Microsoft OAuth credentials.
|
|
3. **Sync against prod**: `railway run python -m scripts.sync_stripe_plan_ids`. Verify `plan_billing` rows have `sk_live_*` price IDs.
|
|
4. **Internal validation (Task 46)**: 9 scenarios with internal testers whose emails match `INTERNAL_TESTER_EMAILS`.
|
|
5. **Flag flip (Task 47)**: email pilots, set `SELF_SERVE_ENABLED=true` + `VITE_SELF_SERVE_ENABLED=true` (frontend redeploy). PostHog signup-funnel dashboard + Sentry alert at >1/hour Stripe webhook errors.
|
|
|
|
## Open issues from prior session (non-code, user-side)
|
|
|
|
- **Apex DNS missing.** `resolutionflow.com` (apex) returns no A/CNAME at the authoritative DNS (Namecheap per SOA `dns1.registrar-servers.com.`). When `www` was reconfigured in Railway, the apex record got dropped from the zone. `www` works (cert provisioned 2026-05-08 01:40 UTC, valid Let's Encrypt SAN). Symptom: apex unreachable from user's machine; Stripe verifier "URL couldn't be reached." User to re-add apex record at Namecheap (ALIAS Record host=`@` value=`c9g7uku8.up.railway.app`) or re-add the apex as a Railway custom domain and follow Railway's DNS instructions. The Railway path is more durable.
|
|
- **Edge HSTS sticky state on user's machine.** Browser remembers the earlier broken-cert visit. Fix: `edge://net-internals/#hsts` (delete `resolutionflow.com` and `www.resolutionflow.com`) + `#dns` clear host cache + `#sockets` flush.
|
|
|
|
## Carry-forward
|
|
|
|
- Annual pricing intentionally NOT implemented — user wants exit flexibility. Schema columns preserved as nullable. `sync_stripe_plan_ids.py` leaves annual fields NULL.
|
|
- `INTERNAL_TESTER_EMAILS` parsed comma-separated → normalized lowercase list. Anonymous callers always see the global flag — allowlist never leaks via unauthenticated request content (regression test enforces).
|
|
- Office-hours design doc at `~/.gstack/projects/chihlasm-resolutionflow/abc-feat-self-serve-signup-phase-2-design-20260507-112020.md` (documentation-builder thesis). NOT yet adopted as roadmap — gated on 3 cold calls with external Directors of Onboarding.
|
|
- Mailing address fill-in: search for `TODO: replace with full mailing address` in `frontend/src/pages/ContactPage.tsx` and `frontend/src/pages/PoliciesPage.tsx` (one each) once P.O. Box is purchased.
|
|
- Bot-crawlability of legal pages: still SPA-rendered. Stripe didn't enforce content scraping last time (issue turned out to be DNS). If a future vendor review flags it, pre-render with `vite-plugin-prerender-spa` (~half day).
|
|
- Frontend env additions for cutover: `VITE_SELF_SERVE_ENABLED`, `VITE_GOOGLE_CLIENT_ID`, `VITE_MS_CLIENT_ID`, `VITE_OAUTH_REDIRECT_BASE`, `VITE_CALENDLY_URL`, `VITE_STRIPE_PUBLISHABLE_KEY`.
|