feat(api): add GET /api/subscriptions/status#506
Conversation
…x v2.0.0-beta.11 This commit merges the latest changes from the main branch and ensures compatibility with the updated @vercel/sandbox version. The API has been adjusted to reflect the renaming of Sandbox.sandboxId to Sandbox.name, with corresponding updates to method parameters. All relevant tests have been updated to mock the new Sandbox structure and verify functionality. Verification steps have been executed successfully, confirming no issues with installation, type checking, linting, or tests.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Rate limit exceeded
To continue reviewing without waiting, purchase usage credits in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (8)
📒 Files selected for processing (6)
📝 WalkthroughWalkthroughAdds a new Next.js API route and supporting server utilities to validate requests, query Stripe for active subscriptions (account and organization), determine pro status, and return JSON with CORS headers. Routes are forced dynamic and caching is disabled. ChangesSubscription status flow
Sequence DiagramsequenceDiagram
actor Client
participant Route as /api/subscriptions/status
participant Validator as validateGetSubscriptionStatusRequest
participant Auth as validateAuthContext
participant Handler as getSubscriptionStatusHandler
participant AcctSub as getActiveSubscriptionDetails
participant OrgSub as getOrgSubscription
participant Stripe as Stripe API
Client->>Route: GET ?accountId=...
Route->>Validator: validate request (query + auth)
Validator->>Auth: check auth context
alt auth fails
Auth-->>Validator: failure response
Validator-->>Route: 400/401 NextResponse (CORS)
Route-->>Client: error JSON
else auth succeeds
Validator-->>Route: { accountId }
Route->>Handler: handle request
Handler->>AcctSub: fetch account subscription (concurrent)
Handler->>OrgSub: fetch org subscription (concurrent)
AcctSub->>Stripe: list subscriptions (paginated)
OrgSub->>Stripe: list subscriptions (paginated)
Stripe-->>AcctSub: subscriptions
Stripe-->>OrgSub: subscriptions
AcctSub-->>Handler: first active or null
OrgSub-->>Handler: first active or null
Handler->>Handler: compute isPro
Handler-->>Route: 200 { isPro } (CORS)
Route-->>Client: 200 JSON { isPro }
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
4 issues found across 11 files
Confidence score: 2/5
- There is a high-confidence logic bug in
lib/stripe/isActiveSubscription.ts:isActiveSubscriptioncan return true for inactive non-trial statuses (like canceled), which creates clear user-facing subscription state errors. lib/stripe/getActiveSubscriptions.tsonly reads the first Stripe page (limit: 100), so organizations with subscriptions beyond page 1 may be treated as unsubscribed; this is a concrete regression risk in production data.- Risk is elevated further because
app/api/subscriptions/status/__tests__/route.test.tscurrently adds only a handler-export smoke test, so the new endpoint behavior is not strongly validated, andlib/stripe/getOrgSubscription.tsadds avoidable parallel API load/latency. - Pay close attention to
lib/stripe/isActiveSubscription.ts,lib/stripe/getActiveSubscriptions.ts,app/api/subscriptions/status/__tests__/route.test.ts- incorrect active-state logic, incomplete pagination, and weak behavioral test coverage are the main merge risks.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="lib/stripe/getOrgSubscription.ts">
<violation number="1" location="lib/stripe/getOrgSubscription.ts:18">
P2: Avoid fetching subscriptions for all orgs in parallel when only the first non-null result is needed; this causes unnecessary Stripe API calls and extra latency.</violation>
</file>
<file name="lib/stripe/isActiveSubscription.ts">
<violation number="1" location="lib/stripe/isActiveSubscription.ts:7">
P1: `isActiveSubscription` returns `true` for non-trial inactive statuses (e.g. canceled) because it only negates `isCanceledTrial` instead of checking allowed active statuses.</violation>
</file>
<file name="lib/stripe/getActiveSubscriptions.ts">
<violation number="1" location="lib/stripe/getActiveSubscriptions.ts:6">
P1: This only checks the first page of Stripe subscriptions (`limit: 100`), so valid subscriptions beyond page 1 can be missed. Paginate through all pages (or use Stripe auto-pagination) before filtering by `metadata.accountId`.</violation>
</file>
<file name="app/api/subscriptions/status/__tests__/route.test.ts">
<violation number="1" location="app/api/subscriptions/status/__tests__/route.test.ts:7">
P2: Custom agent: **Flag AI Slop and Fabricated Changes**
New test is only a handler-export smoke test and does not validate the endpoint behavior introduced by the PR.</violation>
</file>
Architecture diagram
sequenceDiagram
participant Client
participant Handler as Subscription Handler
participant Auth as Auth Service
participant DB as Supabase
participant Stripe as Stripe API
Note over Client,Stripe: GET /api/subscriptions/status?accountId={uuid}
Client->>Handler: NEW: Request with x-api-key or Bearer token
rect rgb(240, 240, 240)
Note right of Handler: Validation Phase
Handler->>Handler: Validate accountId is UUID
Handler->>Auth: NEW: validateAuthContext(request, accountId)
alt Auth Success
Auth-->>Handler: Return AuthContext (accountId, orgId, token)
else Auth Failure
Auth-->>Handler: Return 401/403 NextResponse
Handler-->>Client: Error JSON (mapped status)
end
end
rect rgb(230, 245, 255)
Note right of Handler: Subscription Lookup (Concurrent)
par Personal Check
Handler->>Stripe: NEW: list({ metadata: { accountId } })
Stripe-->>Handler: Personal subscriptions
and Organization Check
Handler->>DB: NEW: getAccountOrganizations({ accountId })
DB-->>Handler: List of organization_ids
loop For each Org
Handler->>Stripe: NEW: list({ metadata: { accountId: orgId } })
Stripe-->>Handler: Org subscriptions
end
end
end
rect rgb(240, 240, 240)
Note right of Handler: Business Logic
Handler->>Handler: NEW: isActiveSubscription()
Note over Handler: Check status (active/trialing) && !canceled_trial
alt Is Pro (Personal or Org)
Handler-->>Client: 200 OK { isPro: true } + CORS Headers
else Not Pro
Handler-->>Client: 200 OK { isPro: false } + CORS Headers
end
end
opt Unhandled Exception
Handler-->>Client: 500 Internal Server Error
end
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (1)
app/api/subscriptions/status/route.ts (1)
1-29: Route integration tests are minimal; consider expanding them.Tests do exist for the handler and validation layer (
getSubscriptionStatusHandler.test.tsandvalidateGetSubscriptionStatusRequest.test.ts), and they provide good coverage of the subscription logic. However,app/api/subscriptions/status/__tests__/route.test.tsonly verifies that handlers are exported—it doesn't invoke them with actualNextRequestobjects.Consider adding route-level integration tests that call
GETandOPTIONSwith real requests to catch any edge cases at the API boundary (request parsing, response formatting, CORS headers). This complements the existing handler tests and strengthens the overall test suite.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/api/subscriptions/status/route.ts` around lines 1 - 29, The route-level tests currently only check exports; add integration tests that call the exported OPTIONS and GET functions with real NextRequest objects to validate request parsing, CORS headers, and response formatting: for OPTIONS invoke OPTIONS() and assert status 200 and headers match getCorsHeaders(), and for GET construct NextRequest instances (including query/accountId scenarios and error cases) and assert the JSON body/status returned by GET(request) matches expected outputs (you can reuse/match behavior covered by getSubscriptionStatusHandler); if needed, mock or stub dependencies used by getSubscriptionStatusHandler to simulate paid/unpaid/org cases so the GET route-level tests exercise the full API boundary.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/stripe/getActiveSubscriptions.ts`:
- Around line 6-15: The current call to stripeClient.subscriptions.list in
getActiveSubscriptions.ts fetches up to 100 global subscriptions and can miss
the target account’s subscription; replace the naive list call with a targeted
search (use stripeClient.subscriptions.search with a query on
metadata.accountId:"{accountId}" and status:"active") so you only return
subscriptions for the given accountId, and if your Stripe SDK/version lacks
subscriptions.search fallback to listing by customer
(stripeClient.subscriptions.list with customer: stripeCustomerId and status:
"active") and then filter by metadata.accountId before producing
activeSubscriptions.
In `@lib/stripe/getOrgSubscription.ts`:
- Around line 18-20: getOrgSubscription currently always returns null because
getActiveSubscriptions filters on subscription.metadata?.accountId but
createStripeSession only sets metadata.accountId to a billing account ID, not an
organization ID; fix by creating an org-specific session creation and lookup:
add a new createOrgStripeSession (or extend createStripeSession with an org
flag) that sets metadata.organizationId (or metadata.orgId) when creating org
subscriptions, and update getActiveSubscriptions and
getActiveSubscriptionDetails (and getOrgSubscription) to look for
subscription.metadata.organizationId (or orgId) when resolving organization
subscriptions so org lookups can actually match real subscription metadata.
In `@lib/stripe/isActiveSubscription.ts`:
- Around line 3-9: The isActiveSubscription function currently treats any
non-trial, non-canceled subscription as active (returning true for statuses like
"past_due", "unpaid", "paused", etc.); update isActiveSubscription to explicitly
check subscription.status against the set of allowed active statuses (e.g.,
"active" and "trialing", and include "past_due" only if you intend to grant pro
access during retry windows) instead of relying on the isTrial/isCanceledTrial
logic—use subscription.status comparisons to decide subscriptionActive and
return that result (refer to isActiveSubscription, subscription.status, isTrial,
isCanceledTrial, subscriptionActive to locate and modify the logic).
In `@lib/stripe/validateGetSubscriptionStatusRequest.ts`:
- Around line 16-17: Rename the file
lib/stripe/validateGetSubscriptionStatusRequest.ts to
lib/stripe/validateGetSubscriptionStatusQuery.ts and rename the exported
function validateGetSubscriptionStatusRequest to
validateGetSubscriptionStatusQuery; update all imports/usages to reference
validateGetSubscriptionStatusQuery so callers load the new module name and
exported identifier, and ensure the file now follows the
validate<EndpointName>Query.ts convention for query-parameter validators.
- Around line 8-14: Export the existing querySchema and replace the hand-written
ValidatedGetSubscriptionStatusRequest with an exported type derived from the Zod
schema using z.infer; specifically make querySchema exported, remove the manual
type declaration, and add export type ValidatedGetSubscriptionStatusRequest =
z.infer<typeof querySchema> so the type always matches the schema (referencing
querySchema and ValidatedGetSubscriptionStatusRequest).
---
Nitpick comments:
In `@app/api/subscriptions/status/route.ts`:
- Around line 1-29: The route-level tests currently only check exports; add
integration tests that call the exported OPTIONS and GET functions with real
NextRequest objects to validate request parsing, CORS headers, and response
formatting: for OPTIONS invoke OPTIONS() and assert status 200 and headers match
getCorsHeaders(), and for GET construct NextRequest instances (including
query/accountId scenarios and error cases) and assert the JSON body/status
returned by GET(request) matches expected outputs (you can reuse/match behavior
covered by getSubscriptionStatusHandler); if needed, mock or stub dependencies
used by getSubscriptionStatusHandler to simulate paid/unpaid/org cases so the
GET route-level tests exercise the full API boundary.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7c94c870-dbab-4042-9d7d-6389d3ee4cd1
⛔ Files ignored due to path filters (4)
app/api/subscriptions/status/__tests__/route.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included byapp/**app/api/subscriptions/status/__tests__/routeTestMocks.tsis excluded by!**/__tests__/**and included byapp/**lib/stripe/__tests__/getSubscriptionStatusHandler.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included bylib/**lib/stripe/__tests__/validateGetSubscriptionStatusRequest.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included bylib/**
📒 Files selected for processing (7)
app/api/subscriptions/status/route.tslib/stripe/getActiveSubscriptionDetails.tslib/stripe/getActiveSubscriptions.tslib/stripe/getOrgSubscription.tslib/stripe/getSubscriptionStatusHandler.tslib/stripe/isActiveSubscription.tslib/stripe/validateGetSubscriptionStatusRequest.ts
| const subscriptions = await Promise.all(orgIds.map(orgId => getActiveSubscriptionDetails(orgId))); | ||
|
|
||
| return subscriptions.find(sub => sub !== null) ?? null; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find any code that creates a Stripe subscription/session with an organizationId or orgId
# in the metadata, to verify whether org subscriptions can be found by getOrgSubscription.
rg -n --type=ts -C4 'metadata.*org'
rg -n --type=ts -C4 'subscription_data.*org'
rg -n --type=ts -C4 'createStripeSession\|checkout.*session.*create' | grep -i 'org'Repository: recoupable/api
Length of output: 40
🏁 Script executed:
fd -t f -n 'getOrgSubscription|getActiveSubscriptionDetails|getActiveSubscriptions|createStripeSession' lib/stripe/Repository: recoupable/api
Length of output: 289
🏁 Script executed:
cat -n lib/stripe/getOrgSubscription.tsRepository: recoupable/api
Length of output: 1039
🏁 Script executed:
rg -n 'getOrgSubscription' --type=ts -B2 -A2Repository: recoupable/api
Length of output: 4446
🏁 Script executed:
fd -t f 'getActiveSubscriptionDetails' lib/stripe/ && cat -n lib/stripe/getActiveSubscriptionDetails.tsRepository: recoupable/api
Length of output: 579
🏁 Script executed:
fd -t f 'getActiveSubscriptions' lib/stripe/ && cat -n lib/stripe/getActiveSubscriptions.tsRepository: recoupable/api
Length of output: 899
🏁 Script executed:
fd -t f 'createStripeSession' lib/stripe/ && head -100 lib/stripe/createStripeSession.tsRepository: recoupable/api
Length of output: 950
getOrgSubscription silently returns null because subscriptions are never created with organization IDs in metadata.
getActiveSubscriptionDetails(orgId) calls getActiveSubscriptions(orgId), which filters subscriptions by subscription.metadata?.accountId === orgId (line 14 of getActiveSubscriptions.ts). However, the only subscription creation flow in the codebase (createStripeSession) sets metadata.accountId to the billing account ID, never an organization ID.
When getOrgSubscription searches for organization subscriptions, the filter in getActiveSubscriptions never matches because no subscription is created with an organization ID in metadata.accountId. This makes the org subscription path (used in getSubscriptionStatusHandler.ts) non-functional—it will always return null.
To enable organization subscriptions, either:
- Create a separate function (e.g.,
createOrgStripeSession) that explicitly setsmetadata.organizationIdormetadata.orgId, and updategetOrgSubscriptionto search by that field; or - Refactor
getOrgSubscriptionto use an alternative lookup mechanism.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/stripe/getOrgSubscription.ts` around lines 18 - 20, getOrgSubscription
currently always returns null because getActiveSubscriptions filters on
subscription.metadata?.accountId but createStripeSession only sets
metadata.accountId to a billing account ID, not an organization ID; fix by
creating an org-specific session creation and lookup: add a new
createOrgStripeSession (or extend createStripeSession with an org flag) that
sets metadata.organizationId (or metadata.orgId) when creating org
subscriptions, and update getActiveSubscriptions and
getActiveSubscriptionDetails (and getOrgSubscription) to look for
subscription.metadata.organizationId (or orgId) when resolving organization
subscriptions so org lookups can actually match real subscription metadata.
- Refactored the isActiveSubscription function to improve clarity and efficiency in determining subscription status. - Added comprehensive unit tests for isActiveSubscription to cover various subscription states, including active, trialing, and canceled scenarios. - Ensured that the function correctly handles null or undefined inputs, returning false as expected.
… enhance filtering - Refactored getActiveSubscriptions to implement pagination for fetching active subscriptions from Stripe, allowing for more than 100 results. - Introduced a constant PAGE_LIMIT for better maintainability of the subscription listing limit. - Enhanced filtering logic to ensure only subscriptions matching the specified accountId are returned. - Improved error handling to log issues encountered during the subscription fetching process.
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="lib/stripe/getActiveSubscriptions.ts">
<violation number="1" location="lib/stripe/getActiveSubscriptions.ts:13">
P1: Unbounded pagination through all Stripe subscriptions without a safety limit or customer filter. This loop will make N/100 sequential API calls for every subscription in the entire Stripe account. Consider adding a `customer` param to scope the query, or at minimum a max-pages safeguard to prevent runaway API calls and request timeouts.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
…subscription - Changed the implementation of getOrgSubscription to iterate through organization IDs and return the first active subscription found, improving efficiency by eliminating unnecessary parallel requests. - Removed the previous logic that collected all subscriptions and filtered them, simplifying the function's flow.
- Added tests for OPTIONS handler to verify it returns 200 status with CORS headers. - Implemented tests for GET handler to ensure it correctly forwards requests to getSubscriptionStatusHandler and returns the expected response. - Introduced beforeEach hook to clear mocks before each test, improving test isolation.
- Exported the querySchema for use in other modules, enhancing reusability. - Simplified the ValidatedGetSubscriptionStatusRequest type by inferring it directly from querySchema, improving type safety and maintainability.
…quest validation - Renamed `validateGetSubscriptionStatusRequest` to `validateGetSubscriptionStatusQuery` for clarity and consistency. - Updated references in `getSubscriptionStatusHandler` and related tests to use the new validation function. - Removed the deprecated `validateGetSubscriptionStatusRequest` file and its associated tests, streamlining the codebase.
…improve filtering - Introduced a maximum page limit for `subscriptions.list` calls to prevent excessive latency on large accounts. - Updated the function to stop fetching after the first page that contains a matching subscription, optimizing performance. - Added support for an optional `stripeCustomerId` parameter to scope the subscription list to a specific customer. - Enhanced unit tests to cover new functionality, including early termination on matches and pagination limits.
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="lib/stripe/getActiveSubscriptions.ts">
<violation number="1" location="lib/stripe/getActiveSubscriptions.ts:22">
P1: The hard 50-page cap can incorrectly return no subscription when matches exist beyond the cutoff.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
…or improved match retrieval - Eliminated the fixed page limit for `subscriptions.list` calls, allowing the function to paginate until all matches are found. - Updated the logic to break pagination if the cursor does not advance, preventing infinite loops. - Enhanced unit tests to verify behavior with no artificial page limits and to ensure correct handling of pagination scenarios.
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="lib/stripe/__tests__/getActiveSubscriptions.test.ts">
<violation number="1" location="lib/stripe/__tests__/getActiveSubscriptions.test.ts:43">
P2: Custom agent: **Enforce Clear Code Style and Maintainability Practices**
Test file exceeds the 100-line maintainability limit required by Rule 3.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
…mock handling - Simplified test setup by consolidating mock definitions for `stripeClient.subscriptions.list`. - Enhanced readability and maintainability of tests by using helper functions for mock data generation. - Ensured consistent behavior across tests by standardizing the way mock responses are defined and utilized.
There was a problem hiding this comment.
1 issue found across 2 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="lib/stripe/__tests__/getActiveSubscriptionsTestHelpers.ts">
<violation number="1" location="lib/stripe/__tests__/getActiveSubscriptionsTestHelpers.ts:5">
P2: Custom agent: **Module should export a single primary function whose name matches the filename**
Module exports multiple top-level functions and none matches the filename.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
…improved clarity - Consolidated subscription-related helper functions into a single `getActiveSubscriptionsTestHelpers` function. - Enhanced test readability by using destructured imports for mock data generation. - Improved maintainability of tests by centralizing mock definitions and reducing redundancy.
…scription Aligns the implementation with recoupable/docs#183: documents subscription status as a resource nested under the account it belongs to, identifies the account via path param, and returns the documented response shape. - New route: app/api/accounts/[id]/subscription - New response: { isPro, status, plan, source } (was { isPro }) - New handler/validator/mapper with unit tests covering account-active, org-active, neither-active, trialing-with-canceled_at, and unsupported Stripe statuses - Deletes the old query-param endpoint and helpers
|
You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment |
| * When `stripeCustomerId` is set, scopes the Stripe list to that customer. | ||
| * Paginates until Stripe reports no more pages (no fixed page cap — avoids missing matches deep in the list). | ||
| */ | ||
| export const getActiveSubscriptions = async (accountId: string, stripeCustomerId?: string) => { |
There was a problem hiding this comment.
YAGNI
- actually: unused stripeCustomerId is included in getActiveSubscriptions function.
- required: remove the unused stripeCustomerId to follow YAGNI principle.
| "past_due", | ||
| ]); | ||
|
|
||
| function toStatus(stripeStatus: Stripe.Subscription.Status): SubscriptionStatus { |
There was a problem hiding this comment.
SRP - new lib file for toStatus.ts
…ts own file - YAGNI: getActiveSubscriptions no longer accepts the unused stripeCustomerId parameter; corresponding test removed. - SRP: toStatus (Stripe status → SubscriptionStatus enum) lives in its own module with focused unit tests; buildSubscriptionResponse now imports it.
Manual preview testingPreview deployment: https://api-ngnpd6vm8-recoup.vercel.app SetupTwo API keys were used to exercise both branches of the response mapping:
Test matrix
ReproducePREVIEW=https://api-ngnpd6vm8-recoup.vercel.app
# Create a fresh agent + key (no auth required)
curl -sS -X POST "$PREVIEW/api/agents/signup" \
-H 'Content-Type: application/json' \
-d "{\"email\":\"agent+$(date +%s)@recoupable.com\"}"
# -> { account_id, api_key }
# Resolve account_id from a key (alternative)
curl -sS "$PREVIEW/api/accounts/id" -H "x-api-key: <KEY>"
# Inactive case
curl -sS "$PREVIEW/api/accounts/<ACCOUNT_ID>/subscription" -H "x-api-key: <KEY>"
# -> {"isPro":false,"status":"none","plan":null,"source":null}
# Active case — use a key with an active sub
curl -sS "$PREVIEW/api/accounts/<ACCOUNT_ID>/subscription" -H "x-api-key: <KEY>"
# -> {"isPro":true,"status":"trialing","plan":"pro","source":"account"}Notes for reviewer
|
The Recoup API endpoint moved from GET /api/subscriptions/status?accountId=
to GET /api/accounts/{id}/subscription as part of recoupable/api#506 and
recoupable/docs#183. Hitting the old path now returns 404, which would leave
all paying users with isSubscribed=false. ProStatusResponse stays narrow
({ isPro }); the wider response shape from the api side is ignored by
structural typing.
…requests to Recoup API (#1729) * refactor(subscription): update subscription status endpoint to proxy requests to Recoup API - Enhanced the GET /api/subscription/status endpoint to proxy requests to the Recoup API, allowing for streamlined access to subscription status. - Improved error handling and response formatting using NextResponse. - Updated the fetch logic in the useProStatus hook to include authorization headers and handle access tokens via Privy authentication. - Ensured that the query is only executed when the user is authenticated and has a valid account ID. * fix(subscription): improve error handling in subscription status endpoint - Added try-catch block around the fetch call to handle upstream fetch failures gracefully. - Updated the response to return a 502 Bad Gateway status with an error message when the fetch fails. * fix(subscription): enhance error logging and response handling in subscription status endpoint - Updated error handling to log the specific error message when upstream fetch fails. - Adjusted response logic to return null for 204 and 304 status codes, improving response consistency. * refactor(subscription): remove deprecated subscription status endpoint - Deleted the /api/subscription/status endpoint as it is no longer needed. - Updated the useProStatus hook to directly fetch from the Recoup API, improving code clarity and reducing redundancy. * fix(subscription): point useProStatus at the new account-scoped endpoint The Recoup API endpoint moved from GET /api/subscriptions/status?accountId= to GET /api/accounts/{id}/subscription as part of recoupable/api#506 and recoupable/docs#183. Hitting the old path now returns 404, which would leave all paying users with isSubscribed=false. ProStatusResponse stays narrow ({ isPro }); the wider response shape from the api side is ignored by structural typing. --------- Co-authored-by: Sweets Sweetman <sweetmantech@gmail.com>
Implements OpenAPI subscription status: required query accountId (UUID), x-api-key or Bearer auth, validateAccountIdOverride, and isPro from Stripe subscription metadata plus org coverage. Adds route, handler, validation, helpers, and unit tests.
Summary by cubic
Added GET
/api/accounts/{id}/subscriptionto return an account’s subscription status and source, replacing the old query-param endpoint. Returns{ isPro, status, plan, source }based on the account’s own or org-backed subscription.New Features
OPTIONShandlers with CORS.id(UUID) andx-api-keyor Bearer auth; forwards{ error }on failure.activeortrialingwithoutcanceled_at.Refactors
stripeCustomerIdfrom subscription lookups.toStatusto normalize Stripe statuses to the API enum.Written for commit c1ebf26. Summary will update on new commits.
Summary by CodeRabbit