Repository navigation
004-03: API Generic Subscription Routes + Provider Discovery - #37
Merged
Merged
Conversation
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
This was referenced Jul 14, 2026
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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/providerslist stake2pay end-to-end.What
Seven generic, provider-agnostic subscription routes in
api/src/, mirroring the epic-002payment*pattern — zero hardcoded provider names (grep-verified):subscriptionProviders—GET /subscriptions/providers— probes each registered plugin withlistPlans()and skips non-implementers with the same empty-catch pattern aspaymentProviders(a payment-only plugin fails locally, no network cost)subscriptionPlans—GET /subscriptions/{provider}/planssubscriptionCreate—POST /subscriptions/{provider}→SubscriptionActionsubscriptionGet—GET /subscriptions/{provider}/status?planId=&payerRef=subscriptionCancel/subscriptionResume/subscriptionChange—POST /subscriptions/{provider}/(cancel|resume|change)Subscription Zod schemas are inlined in
api/src/contract.tsper existing convention, shape-identical to the canonicalplugins/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(fromgetContext().near.primaryAccountId— verified inhost/src/services/auth.ts). This PR addswalletAddressto the api plugin's context schema and defaultspayerReffrom it: explicit input overrides;get/cancel/resume/changethrowBAD_REQUESTwhen neither exists;createtolerates absence (hosted providers collect identity at checkout). Notably the better-auth internaluserIdis not used as a fallback — it's meaningless as an on-chain account ref, so email-only sessions must passpayerRefexplicitly.Tests
36 tests green (19 baseline + 17 new). The new
e2e-subscriptions.test.tsregisters the real pingpay + stripe plugins plus an in-test fake plugin implementing the realSubscriptionContract, 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 (walletAddressinjected 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