Skip to content

feat(paddle): wire frontend overlay to /api/billing/config#141

Open
Rasbandit wants to merge 6 commits into
mainfrom
feat/paddle-overlay
Open

feat(paddle): wire frontend overlay to /api/billing/config#141
Rasbandit wants to merge 6 commits into
mainfrom
feat/paddle-overlay

Conversation

@Rasbandit
Copy link
Copy Markdown
Member

Summary

  • Rewrite billing-page.tsx around @paddle/paddle-js overlay — fetch /api/billing/config, initializePaddle, Paddle.Checkout.open with customer / customData / successUrl.
  • Add useBillingConfig() React Query hook with staleTime: Infinity (per-session immutable).
  • Allow https://*.paddle.com in the SPA CSP (script-src, connect-src, frame-src) — the overlay loads from cdn.paddle.com, renders an iframe on sandbox-buy.paddle.com, and calls sandbox-api.paddle.com. The previous CSP blocked all three.
  • Add repo-root Makefile ported from engram-workspace (drops cd backend/ prefixes, swaps npmbun, strips plugin-only targets).
  • Track repo-local .mcp.json (paddle-docs MCP server) and the design handoff plan at docs/superpowers/plans/2026-05-14-paddle-frontend-overlay.md.

Backend cutover landed in #132; this PR is the frontend half.

Test plan

  • bun run build and tsc --noEmit clean.
  • mix compile --warnings-as-errors clean.
  • Manual: /billing renders Starter + Pro cards, no "Stripe" copy anywhere.
  • Sandbox checkout end-to-end with 4242 4242 4242 4242 — Paddle created sub_01krn5g6t9stkvmkzwq2r1vh50 for user 14, status trialing, period end 2026-05-22.
  • subscription.created webhook delivered through cloudflared tunnel → row landed in subscriptions (tier=starter, status=trialing, paddle_customer_id + paddle_subscription_id populated).
  • /billing flipped to "Free Trial" + "7 days remaining" + "Manage subscription" link.
  • /api/billing/portal redirected to a Paddle-hosted portal session.

Out of scope (follow-up PRs)

  • Affiliate / utm cookie capture into customData.
  • Marketing-site checkout (separate frontend).
  • Annual price IDs ($50 / $100) when those prices exist in Paddle.
  • Vitest infrastructure for the frontend (no unit tests for billing-page.tsx yet; backend has full webhook + event coverage in feat(paddle): replace Stripe with Paddle (MoR) end-to-end #132).
  • Production deployment: Fly env wiring + Paddle live destination URL.

🤖 Generated with Claude Code

Rasbandit and others added 6 commits May 14, 2026 19:11
Replaces the stale Stripe-style frontend with Paddle.js overlay
checkout. Adds `@paddle/paddle-js` and a `useBillingConfig` hook
that fetches `/api/billing/config` (token, environment, price IDs,
customer email, custom_data). `BillingPage` initializes Paddle
once on config load and `Paddle.Checkout.open` runs on plan-card
click with `customer`, `customData`, and a `successUrl` back to
`/billing?status=success`. Buttons stay disabled until the Paddle
instance resolves. Portal button still hits `/api/billing/portal`
(server returns the hosted Paddle URL); "Manage subscription in
Stripe" copy stripped.

Backend cutover landed in #132. Plan: docs/superpowers/plans/
2026-05-14-paddle-frontend-overlay.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Registers `paddle-docs` HTTP MCP server (hosted by Kapa.ai at
paddlehq.mcp.kapa.ai) in repo `.mcp.json` so Claude can query
live Paddle documentation during integration work. Checks in
the handoff plan that drove the frontend rewrite shipped in the
prior commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Single-repo port of the workspace Makefile. Drops `cd backend/`
prefixes (backend is the repo root here), strips `plugin-*`
targets (plugin lives in a separate repo), and swaps frontend
`npm` for `bun` to match the repo's lockfile choice (#35 / 6ac17fa).
Targets that referenced missing scripts (test_plan.sh, deploy.sh,
e2e/unit_tests) are omitted rather than ported broken.

Top targets for local dev:
- `make dev` / `make dev-selfhost` — Phoenix at :4000 / :4001 with
  the matching .env.local file.
- `make frontend-dev` — Vite hot-reload at :5173. Phoenix watchers
  are disabled in config/dev.exs to avoid orphan node processes,
  so this is opt-in.
- `make backend-up/down` — full Docker stack (docker-compose.elixir).
- `make help` — auto-generated target listing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@paddle/paddle-js dynamically loads paddle.js from cdn.paddle.com,
opens the checkout iframe on (sandbox-)buy.paddle.com, and talks
to (sandbox-)api.paddle.com. The strict CSP installed by the SPA
pipeline blocked all three, so the overlay never rendered.

Wildcard `https://*.paddle.com` covers cdn / buy / sandbox-buy /
api / sandbox-api in one entry. Same change applies to script-src,
connect-src, and frame-src.

img-src already permits `https:`, style-src already has
'unsafe-inline'.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captures state post PR #141 so brainstorming can resume after
/compact: PR status, current Paddle sandbox resources, codebase
facts already gathered (router, AuthGuard, no TOS tracking), and
the clarifying questions queued for the user.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rasbandit added a commit that referenced this pull request May 15, 2026
Also includes fix on top: AgreementPage navigates to /onboard after
acceptance so OnboardRedirect can forward to billing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant