feat(web): load pricing from the public Capgo plan prices endpoint

Merged
RI
2d ago
+114-418

Summary

The pricing page stayed in USD because its prices came from a nightly snapshot that needed a Stripe key. It now loads them from the Capgo backend's public endpoint (GET /private/plan_prices/public, Cap-go/capgo.app#3929) in the visitor's browser.

  • Exact Stripe prices for the visitor's country, including the Solo regional discount (strike-through full price, "{n}% regional discount" badge, card and billing address note).
  • Discount only in the region's currency (backend discount.currencies): switching to another currency shows the full price, same rule as Checkout.
  • Currency selector lists the currencies the backend returns and is hidden until there is more than one.
  • Usage and credit amounts are converted at the rate of the Stripe plan prices and marked as estimates.
  • HTML and JSON-LD stay in USD; if the endpoint fails or is incomplete the page keeps the USD prices.
  • Removed: the capgo-plan-prices.json snapshot and its fetch, the STRIPE_PRICES_READ_KEY workflow secret, the /currency.json worker route and the copied country/tier tables.

Release

Works once the backend with #3929 is deployed (/private/plan_prices/public returns 404 until then; the page stays in USD meanwhile).

Test plan

  • bun test apps/web/test/pricing-currency.test.js
  • astro check, oxlint, typos
  • Dev server with a mocked Portugal response: Solo €6 with €10 struck through and "40% regional discount", Maker/Team/Enterprise in EUR, selector shows EUR/GBP/JPY/USD; switching to USD shows Solo at the full $12 with no discount.

šŸ¤– Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Pricing pages now load currency and plan prices from the plan catalog, with regional discounts and currency-appropriate price formatting.
    • Available currency options reflect the currencies provided by the catalog. If pricing data is unavailable or incomplete, the page retains its USD-rendered prices.
  • Bug Fixes
    • Data refreshes now fail when required plan data cannot be fetched, instead of silently continuing without it.