Skip to content

004-03: API Generic Subscription Routes + Provider Discovery - #37

Merged
jaswinder6991 merged 1 commit into
mainfrom
004-03-subscription-routes
Jul 14, 2026
Merged

jaswinder6991 merged 1 commit into
mainfrom
004-03-subscription-routes

Conversation

@jaswinder6991

Copy link
Copy Markdown
Contributor

Closes #30. Part of Epic 004 (#27). Depends on #35 (merged). Companion to #36 (stake2pay plugin) — the two merge independently; together they make GET /subscriptions/providers list stake2pay end-to-end.

What

Seven generic, provider-agnostic subscription routes in api/src/, mirroring the epic-002 payment* pattern — zero hardcoded provider names (grep-verified):

  • subscriptionProviders — GET /subscriptions/providers — probes each registered plugin with listPlans() and skips non-implementers with the same empty-catch pattern as paymentProviders (a payment-only plugin fails locally, no network cost)
  • subscriptionPlans — GET /subscriptions/{provider}/plans
  • subscriptionCreate — POST /subscriptions/{provider} → SubscriptionAction
  • subscriptionGet — GET /subscriptions/{provider}/status?planId=&payerRef=
  • subscriptionCancel / subscriptionResume / subscriptionChange — POST /subscriptions/{provider}/(cancel|resume|change)

Subscription Zod schemas are inlined in api/src/contract.ts per existing convention, shape-identical to the canonical plugins/stake2pay/src/schema.ts.

payerRef session defaulting

The everything-dev host already resolves better-near-auth's NEAR account into every plugin request context as context.walletAddress (from getContext().near.primaryAccountId — verified in host/src/services/auth.ts). This PR adds walletAddress to the api plugin's context schema and defaults payerRef from it: explicit input overrides; get/cancel/resume/change throw BAD_REQUEST when neither exists; create tolerates absence (hosted providers collect identity at checkout). Notably the better-auth internal userId is not used as a fallback — it's meaningless as an on-chain account ref, so email-only sessions must pass payerRef explicitly.

Tests

36 tests green (19 baseline + 17 new). The new e2e-subscriptions.test.ts registers the real pingpay + stripe plugins plus an in-test fake plugin implementing the real SubscriptionContract, then exercises the full HTTP surface (RPC + OpenAPI): discovery excludes payment-only providers, REST paths resolve, unknown provider → NOT_FOUND, and payerRef default/override/BAD_REQUEST on every applicable route (walletAddress injected from a test header simulating the host session middleware). Existing payment* routes untouched and green.

cd api && bun run test, bun run typecheck, and biome on changed files all pass. Generated type files are untracked (nothing regenerated staged).

Note for reviewers

The new test file uses an isolated pglite dir instead of the shared pglite:.bos/api/:memory: — that URL is a pre-existing footgun (:memory: maps to a literal on-disk path shared across vitest forks, and parallel integration files can race on it). Worth a small follow-up ticket; existing test files were left as-is.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FdKukDnRTrshKqkQ8JXwVj

Mirror the epic-002 payment route pattern for subscriptions: 7 new
contract routes (subscriptionProviders, subscriptionPlans,
subscriptionCreate, subscriptionGet, subscriptionCancel,
subscriptionResume, subscriptionChange) with dynamic provider factory
lookup and zero hardcoded provider names in api/src/.

- discovery probes each registered plugin via listPlans and skips
  payment-only providers with the same empty-catch pattern as
  paymentProviders
- subscription schemas inlined in api/src/contract.ts, shape-identical
  to plugins/stake2pay/src/schema.ts (integer-string amounts,
  discriminated SubscriptionActionSchema, status enum)
- payerRef defaults to the session's NEAR account exposed by the host
  as context.walletAddress (better-near-auth primaryAccountId);
  explicit input overrides; get/cancel/resume/change throw BAD_REQUEST
  without either, create allows absence for hosted checkout
- E2E suite registers real pingpay/stripe plugins plus an in-test fake
  SubscriptionContract plugin, covering discovery exclusion, dynamic
  delegation over RPC and OpenAPI, NOT_FOUND for unknown providers and
  payerRef defaulting

Closes #30

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FdKukDnRTrshKqkQ8JXwVj
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.

004-03: API Generic Subscription Routes + Provider Discovery

1 participant