Add real Shopify Billing API integration: Free/Starter/Growth/Pro plans
(app/lib/billing-plans.ts, priced per PRODUCT_STRATEGY.md §6) wired into
shopify.server.ts's billing config, a merchant-facing plan page
(app/routes/app.billing.tsx) using billing.request/billing.cancel, and
webhooks.app_subscriptions.update.tsx as the durable sync path for
Shop.tier (fires even when a merchant cancels from Shopify's own billing
page, not just from this app).
Gate the features actually built so far in both loader and action (never
just hidden in the UI, so a direct POST can't bypass a tier limit):
delivery zones/rates require Growth+, the dispatch dashboard requires
Starter+, and location count is capped per tier (Free=1, Starter=3,
Growth/Pro=unlimited). Split pure tier logic (app/lib/billing-plans.ts)
from DB-backed reads/writes (app/services/billing.server.ts) so the
client-rendered UpsellState component can import the Tier type without
pulling server code into the client bundle — same split as currency.ts.
Covered by tests/unit/billing-plans.test.ts (pure tier ranking/mapping)
and tests/integration/billing.test.ts (tier persistence and location-limit
enforcement against live Postgres).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bootstraps from Shopify's official shopify-app-template-remix (cloned
directly rather than via `shopify app init`, which requires an interactive
Partner login unavailable in this session):
- Prisma (SQLite dev / Postgres-ready) with baseline Session model + migration
- Vitest configured for unit tests, Playwright configured for E2E
- Redis client + BullMQ worker skeleton (app/lib/redis.server.ts, jobs/worker.ts)
- shopify.app.toml: minimal scopes, GDPR + orders webhooks wired (stub handlers),
app proxy config for the future storefront widget
- Stripped template-repo-only meta files (CLA, issue templates, demo product
page) and replaced CI with a lint+typecheck+test workflow
- Bumped @shopify/shopify-app-session-storage-prisma to resolve a duplicate
@shopify/shopify-api install that broke typecheck
- Dropped the Jest-only ESLint config (template default) since the project
standardizes on Vitest per IMPLEMENTATION_PLAN.md
Verified: npm install, lint, typecheck, unit tests, prisma migrate dev, and
npm run build all pass on Node 22 LTS.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>