Migrate stripe subscription checkout sessions#493
Conversation
- Added "endOfLine": "lf" to the Prettier configuration for consistent line endings. - Introduced "stripe" and "uuid" packages in package.json for enhanced functionality. - Updated pnpm-lock.yaml to reflect the addition of new dependencies.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a dynamic API route plus new Stripe helper modules to validate/authenticate requests, create Stripe Checkout subscription sessions (accountId as client_reference_id, accountId metadata, 30-day trial), and return CORS-enabled JSON responses. Changes
Sequence DiagramsequenceDiagram
participant Client
participant APIRoute as "API Route\n(app/api/subscriptions/sessions/route.ts)"
participant Handler as "createSubscriptionSessionHandler\n(lib/stripe)"
participant Validator as "validateCreateSubscriptionSessionRequest\n(lib/stripe)"
participant Auth as "validateAuthContext"
participant Stripe as "Stripe Client\n(lib/stripe/client)"
Client->>APIRoute: POST /api/subscriptions/sessions
APIRoute->>Handler: delegate request
Handler->>Validator: validateCreateSubscriptionSessionRequest(request)
Validator->>Validator: parse JSON & zod validate
Validator->>Auth: validateAuthContext(request)
Auth-->>Validator: accountId OR NextResponse(error)
Validator-->>Handler: { accountId, successUrl } OR NextResponse(error)
Handler->>Stripe: createStripeSession(accountId, successUrl)
Stripe-->>Handler: Session { id, url }
Handler-->>Client: 200 { id, url } + CORS headers
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 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.
Actionable comments posted: 6
🧹 Nitpick comments (3)
lib/stripe/mapToSubscriptionSessionError.ts (1)
4-4: Rename the file to match the exported function.
lib/stripe/mapToSubscriptionSessionError.tsexportsmapToSubscriptionSessionErrorResponse, which breaks the repo's file/function naming rule forlib/**/*.ts. This should live inlib/stripe/mapToSubscriptionSessionErrorResponse.ts.As per coding guidelines, "The file name MUST match the exported function name."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/stripe/mapToSubscriptionSessionError.ts` at line 4, The file name lib/stripe/mapToSubscriptionSessionError.ts does not match the exported function mapToSubscriptionSessionErrorResponse; rename the file to mapToSubscriptionSessionErrorResponse.ts and update any imports that reference the old path so they import from lib/stripe/mapToSubscriptionSessionErrorResponse and continue exporting the same function name (mapToSubscriptionSessionErrorResponse) to satisfy the repo's file/function naming rule.lib/stripe/createSubscriptionSessionSchemas.ts (1)
3-8: Put this body schema in avalidate...Bodyfile and export its inferred type.This is request-body validation, but
createSubscriptionSessionSchemas.tsdoesn't follow the repo's validation-file convention and doesn't publish the inferred body type alongside the schema. Moving it tovalidateCreateSubscriptionSessionBody.tswould keep the contract and validator aligned.Based on learnings, "Create validate functions in
validate<EndpointName>Body.tsorvalidate<EndpointName>Query.tsfiles that export both the schema and inferred TypeScript type."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/stripe/createSubscriptionSessionSchemas.ts` around lines 3 - 8, Move the request-body schema into a new file named validateCreateSubscriptionSessionBody.ts, export the schema as createSubscriptionSessionBodySchema and also export its inferred TypeScript type (e.g., export type CreateSubscriptionSessionBody = z.infer<typeof createSubscriptionSessionBodySchema>); update any imports/usages that previously referenced createSubscriptionSessionSchemas to import the schema/type from the new validateCreateSubscriptionSessionBody.ts module and remove or repurpose the original createSubscriptionSessionSchemas.ts so the repository follows the validate<EndpointName>Body.ts convention.lib/stripe/validateCreateSubscriptionSessionRequest.ts (1)
12-42: This validator is starting to take on too many responsibilities.It now parses JSON, maps schema errors, invokes auth, remaps auth errors, and builds the validated payload. Splitting body parsing and auth/error mapping into small helpers would keep it under the repo's size threshold and make the branches easier to test.
As per coding guidelines, "Flag functions longer than 20 lines" and "Keep functions small and focused."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/stripe/validateCreateSubscriptionSessionRequest.ts` around lines 12 - 42, validateCreateSubscriptionSessionRequest is doing too much (JSON parsing, schema validation error mapping, auth invocation and remapping, and payload construction); extract responsibilities into small helpers: create a parseJsonBody(request) helper that returns body or a NextResponse for invalid JSON, a validateRequestSchema(body, createSubscriptionSessionBodySchema) helper that returns parsed.data or a NextResponse for schema errors (use the same first-issue mapping logic), and an authAndMap(request, accountId) helper that wraps validateAuthContext and maps NextResponse via mapToSubscriptionSessionErrorResponse; then have validateCreateSubscriptionSessionRequest call these helpers in sequence and return the final validated payload ({ accountId: authContext.accountId, successUrl: parsed.data.successUrl })—keep references to createSubscriptionSessionBodySchema, validateAuthContext, and mapToSubscriptionSessionErrorResponse so the logic is easy to locate.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/api/subscriptions/sessions/route.ts`:
- Around line 17-29: Add endpoint tests for the new billing route by exercising
the exported POST handler (route.ts -> POST) and its underlying
createSubscriptionSessionHandler: add a happy-path test that returns session
id/url, and tests for invalid JSON input, schema validation failures, auth
failure, and Stripe API failure; in each test mock/stub the relevant
dependencies (request body parsing, the schema validator used inside
createSubscriptionSessionHandler, auth/token check, and the Stripe client call)
to simulate the different responses and assert the proper HTTP status and error
payloads are returned. Ensure you include both success and all listed error
branches (invalid JSON, schema error, auth failure, Stripe failure) and use the
same handler symbols (POST, createSubscriptionSessionHandler) so tests remain
robust to refactors.
In `@lib/stripe/client.ts`:
- Line 3: The module currently silences TypeScript by using "as string" for
process.env.STRIPE_SK and instantiates stripeClient immediately; instead
validate that process.env.STRIPE_SK is defined at module init and fail fast if
not (e.g., read into a local stripeKey, if falsy throw a clear Error or log and
process.exit(1)), then pass that validated stripeKey into the Stripe constructor
when creating the stripeClient constant to avoid delayed runtime failures.
In `@lib/stripe/createStripeSession.ts`:
- Around line 11-25: Extract the hardcoded Stripe price ID and trial period into
a new config module and update session creation to use those getters: create a
lib/stripe/config-like module that exports constants and functions (e.g.,
getStripePriceId(), getStripeTrialDays()) which read from environment variables
and throw a clear error if missing, then replace the hardcoded
"price_1RyDFD00JObOnOb53PcVOeBz" and subscription_data.trial_period_days = 30 in
the sessionData object (and any duplicated test values) to call those getters so
sessionData (and any tests) use the centralized config values with proper error
handling.
In `@lib/stripe/createSubscriptionSessionHandler.ts`:
- Around line 27-30: In the catch block of createSubscriptionSessionHandler,
stop returning raw error.message and a 400; instead keep logging the full error
(e.g., console.error("[createSubscriptionSessionHandler]", error)) but return a
generic server-side response such as NextResponse.json({ error: "Failed to
create subscription session" }, { status: 502, headers: getCorsHeaders() }) to
avoid leaking provider/internal details and to correctly classify server errors;
update the return to use this generic message/status and ensure getCorsHeaders()
and NextResponse.json remain used for the response.
In `@lib/stripe/createSubscriptionSessionSchemas.ts`:
- Line 5: The successUrl field currently only checks URL syntax; update the Zod
schema (the successUrl validator in createSubscriptionSessionSchemas.ts) to
restrict destinations by either enforcing a relative path (e.g., must start with
"/") or by parsing the URL and validating its origin against a maintained
allowlist of trusted hosts/schemes before acceptance; implement the check as
part of the successUrl refinement or custom validator so any non-allowed
absolute URL is rejected and a clear validation error is returned.
In `@lib/stripe/validateCreateSubscriptionSessionRequest.ts`:
- Around line 31-33: The code is deriving accountId from the request body by
passing parsed.data.accountId into validateAuthContext; remove that override and
call validateAuthContext(request) so the account is derived from the
authentication token instead of the body, and then remove accountId from the
request body/schema (so validateCreateSubscriptionSessionRequest no longer
expects parsed.data.accountId). Update the call site (validateAuthContext in
validateCreateSubscriptionSessionRequest.ts) to omit the second argument and
adjust any related schema or parsing that included accountId.
---
Nitpick comments:
In `@lib/stripe/createSubscriptionSessionSchemas.ts`:
- Around line 3-8: Move the request-body schema into a new file named
validateCreateSubscriptionSessionBody.ts, export the schema as
createSubscriptionSessionBodySchema and also export its inferred TypeScript type
(e.g., export type CreateSubscriptionSessionBody = z.infer<typeof
createSubscriptionSessionBodySchema>); update any imports/usages that previously
referenced createSubscriptionSessionSchemas to import the schema/type from the
new validateCreateSubscriptionSessionBody.ts module and remove or repurpose the
original createSubscriptionSessionSchemas.ts so the repository follows the
validate<EndpointName>Body.ts convention.
In `@lib/stripe/mapToSubscriptionSessionError.ts`:
- Line 4: The file name lib/stripe/mapToSubscriptionSessionError.ts does not
match the exported function mapToSubscriptionSessionErrorResponse; rename the
file to mapToSubscriptionSessionErrorResponse.ts and update any imports that
reference the old path so they import from
lib/stripe/mapToSubscriptionSessionErrorResponse and continue exporting the same
function name (mapToSubscriptionSessionErrorResponse) to satisfy the repo's
file/function naming rule.
In `@lib/stripe/validateCreateSubscriptionSessionRequest.ts`:
- Around line 12-42: validateCreateSubscriptionSessionRequest is doing too much
(JSON parsing, schema validation error mapping, auth invocation and remapping,
and payload construction); extract responsibilities into small helpers: create a
parseJsonBody(request) helper that returns body or a NextResponse for invalid
JSON, a validateRequestSchema(body, createSubscriptionSessionBodySchema) helper
that returns parsed.data or a NextResponse for schema errors (use the same
first-issue mapping logic), and an authAndMap(request, accountId) helper that
wraps validateAuthContext and maps NextResponse via
mapToSubscriptionSessionErrorResponse; then have
validateCreateSubscriptionSessionRequest call these helpers in sequence and
return the final validated payload ({ accountId: authContext.accountId,
successUrl: parsed.data.successUrl })—keep references to
createSubscriptionSessionBodySchema, validateAuthContext, and
mapToSubscriptionSessionErrorResponse so the logic is easy to locate.
🪄 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: bf056a7f-5e18-47f1-af1a-bce3fc86954b
⛔ Files ignored due to path filters (8)
.gitattributesis excluded by none and included by none.prettierrcis excluded by none and included by nonelib/stripe/__tests__/createStripeSession.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included bylib/**lib/stripe/__tests__/createSubscriptionSessionHandler.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included bylib/**lib/stripe/__tests__/mapToSubscriptionSessionError.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included bylib/**lib/stripe/__tests__/validateCreateSubscriptionSessionRequest.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included bylib/**package.jsonis excluded by none and included by nonepnpm-lock.yamlis excluded by!**/pnpm-lock.yamland included by none
📒 Files selected for processing (7)
app/api/subscriptions/sessions/route.tslib/stripe/client.tslib/stripe/createStripeSession.tslib/stripe/createSubscriptionSessionHandler.tslib/stripe/createSubscriptionSessionSchemas.tslib/stripe/mapToSubscriptionSessionError.tslib/stripe/validateCreateSubscriptionSessionRequest.ts
| /** | ||
| * POST /api/subscriptions/sessions: creates a Stripe subscription checkout session. | ||
| * | ||
| * @param request - The incoming HTTP request. | ||
| * @returns A NextResponse with session `id` and `url`, or an error body. | ||
| */ | ||
| export async function POST(request: NextRequest) { | ||
| return createSubscriptionSessionHandler(request); | ||
| } | ||
|
|
||
| export const dynamic = "force-dynamic"; | ||
| export const fetchCache = "force-no-store"; | ||
| export const revalidate = 0; |
There was a problem hiding this comment.
Please add endpoint coverage for this new billing route.
I don't see tests in this change for the happy path plus the invalid JSON, schema error, auth failure, and Stripe failure branches. For a new public payment endpoint, that leaves the main regression paths unguarded.
As per coding guidelines, "Write tests for new API endpoints covering all success and error paths."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/api/subscriptions/sessions/route.ts` around lines 17 - 29, Add endpoint
tests for the new billing route by exercising the exported POST handler
(route.ts -> POST) and its underlying createSubscriptionSessionHandler: add a
happy-path test that returns session id/url, and tests for invalid JSON input,
schema validation failures, auth failure, and Stripe API failure; in each test
mock/stub the relevant dependencies (request body parsing, the schema validator
used inside createSubscriptionSessionHandler, auth/token check, and the Stripe
client call) to simulate the different responses and assert the proper HTTP
status and error payloads are returned. Ensure you include both success and all
listed error branches (invalid JSON, schema error, auth failure, Stripe failure)
and use the same handler symbols (POST, createSubscriptionSessionHandler) so
tests remain robust to refactors.
There was a problem hiding this comment.
Already implemented
There was a problem hiding this comment.
🧩 Analysis chain
Let me verify the existing test coverage.
🏁 Script executed:
#!/bin/bash
# Find all test files related to the stripe subscription session work
fd -e test.ts -e spec.ts -e test.tsx -e spec.tsx | grep -i -E "(stripe|subscription|session)" || true
# Also search broadly for any test files referencing the handler or route
rg -rn "createSubscriptionSessionHandler\|POST.*subscription\|subscriptions/sessions" --type=ts -g "**/*.test.*" -g "**/*.spec.*" -l 2>/dev/null || true
# List all test files in the lib/stripe directory
fd -e test.ts -e spec.ts . lib/stripe 2>/dev/null || true
# Look for test files anywhere mentioning the key symbols
rg -rn "createSubscriptionSessionHandler\|validateCreateSubscriptionSessionRequest\|mapToSubscriptionSessionError\|createStripeSession" --type=ts -l 2>/dev/null | head -30
Length of output: 0
Let me cast a wider net.
🏁 Script executed:
#!/bin/bash
# Find ALL test/spec files in the repo
echo "=== All test/spec files ==="
fd -e test.ts -e spec.ts -e test.tsx -e spec.tsx | head -50
# Search for test files with any naming pattern
echo "=== Files with .test. or .spec. in name ==="
find . -name "*.test.*" -o -name "*.spec.*" | grep -v node_modules | grep -v ".next" | head -50
# Check __tests__ directories
echo "=== __tests__ directories ==="
find . -type d -name "__tests__" | grep -v node_modules | head -20
# Check if there's a tests or test directory at root
echo "=== test/tests directories ==="
find . -maxdepth 3 -type d -name "test" -o -type d -name "tests" | grep -v node_modules | head -20
# List files in lib/stripe
echo "=== lib/stripe contents ==="
ls lib/stripe/ 2>/dev/null || find . -path "*/lib/stripe*" -not -path "*/node_modules/*" | head -30
Length of output: 343
@ahmednahima0-beep, I scanned the entire branch for test files (.test.ts, .spec.ts, __tests__/ directories, etc.) and found none. The lib/stripe/ directory only contains the six implementation files:
lib/stripe/client.ts
lib/stripe/createStripeSession.ts
lib/stripe/createSubscriptionSessionHandler.ts
lib/stripe/createSubscriptionSessionSchemas.ts
lib/stripe/mapToSubscriptionSessionError.ts
lib/stripe/validateCreateSubscriptionSessionRequest.ts
No corresponding test files exist for these modules or for the app/api/subscriptions/sessions/route.ts route handler.
Per the coding guidelines, tests are required for new API endpoints covering all success and error paths. For this endpoint, the missing test cases are:
| Branch | What to assert |
|---|---|
| ✅ Happy path | Returns { id, url } with HTTP 200 |
| ❌ Invalid JSON body | Returns { error: "Invalid JSON body" } with HTTP 400 |
| ❌ Schema validation failure | Returns first Zod issue message with HTTP 400 |
| ❌ Auth failure | Passes auth error through mapToSubscriptionSessionError, preserving original status |
❌ Stripe session missing url |
Returns { error: "Checkout session URL missing" } with HTTP 400 |
| ❌ Stripe API throws | Returns { error: <message or "failed"> } with HTTP 400 |
Could you please add the test file(s)? If you'd like, I can generate a starter test suite for all six branches. Just let me know!
🧠 Learnings used
Learnt from: CR
Repo: recoupable/api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-17T19:09:32.948Z
Learning: Applies to app/api/**/route.ts : Write tests for new API endpoints covering all success and error paths
There was a problem hiding this comment.
Already added all tests
There was a problem hiding this comment.
🧩 Analysis chain
Let me verify the tests are present on the branch.
🏁 Script executed:
#!/bin/bash
echo "=== All test/spec files in repo ==="
find . -name "*.test.*" -o -name "*.spec.*" | grep -v node_modules | grep -v ".next" | grep -v ".git" | sort
echo ""
echo "=== __tests__ directories ==="
find . -type d -name "__tests__" | grep -v node_modules | grep -v ".git"
echo ""
echo "=== lib/stripe directory contents ==="
find . -path "*/lib/stripe*" -not -path "*/node_modules/*" -not -path "*/.git/*" | sort
echo ""
echo "=== Any file referencing createSubscriptionSessionHandler ==="
rg -rn "createSubscriptionSessionHandler" --type=ts -l 2>/dev/null
echo ""
echo "=== Any file referencing subscriptions/sessions route ==="
rg -rn "subscriptions/sessions\|POST.*session\|createStripeSession\|mapToSubscriptionSessionError\|validateCreateSubscriptionSession" --type=ts -l 2>/dev/null | head -30
Length of output: 588
@ahmednahima0-beep, I re-scanned the branch and the results are the same as before — no test files exist anywhere in the repository:
=== All test/spec files in repo ===
(empty)
=== __tests__ directories ===
(empty)
=== lib/stripe directory contents ===
./lib/stripe/client.ts
./lib/stripe/createStripeSession.ts
./lib/stripe/createSubscriptionSessionHandler.ts
./lib/stripe/createSubscriptionSessionSchemas.ts
./lib/stripe/mapToSubscriptionSessionError.ts
./lib/stripe/validateCreateSubscriptionSessionRequest.ts
It's possible the tests were added locally but not yet pushed to the branch. Could you push the commits with the test files so they're visible on the PR?
In the meantime, if it would help, I can generate a complete starter test suite covering all the required branches:
| Branch | File under test |
|---|---|
✅ Happy path — returns { id, url } / HTTP 200 |
route.ts → POST |
| ❌ Invalid JSON body — HTTP 400 | validateCreateSubscriptionSessionRequest.ts |
❌ Schema validation failure (missing/invalid successUrl) — HTTP 400 |
createSubscriptionSessionSchemas.ts |
❌ Auth failure — preserves original status via mapToSubscriptionSessionError |
validateCreateSubscriptionSessionRequest.ts |
❌ Stripe session missing url — HTTP 400 |
createSubscriptionSessionHandler.ts |
| ❌ Stripe API throws — HTTP 500 | createSubscriptionSessionHandler.ts |
Just say the word and I'll scaffold the test files!
🧠 Learnings used
Learnt from: CR
Repo: recoupable/api PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-17T19:09:32.948Z
Learning: Applies to app/api/**/route.ts : Write tests for new API endpoints covering all success and error paths
|
|
||
| export const createSubscriptionSessionBodySchema = z | ||
| .object({ | ||
| successUrl: z.string().min(1, "successUrl is required").url("successUrl must be a valid URL"), |
There was a problem hiding this comment.
Restrict successUrl to trusted destinations.
This only checks that successUrl is syntactically a URL, so any authenticated caller can create a checkout flow that redirects to an arbitrary site after payment. For a billing endpoint, that is an open-redirect/phishing risk. Prefer a relative path or enforce an allowlist of approved hosts/schemes before sending it to Stripe.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/stripe/createSubscriptionSessionSchemas.ts` at line 5, The successUrl
field currently only checks URL syntax; update the Zod schema (the successUrl
validator in createSubscriptionSessionSchemas.ts) to restrict destinations by
either enforcing a relative path (e.g., must start with "/") or by parsing the
URL and validating its origin against a maintained allowlist of trusted
hosts/schemes before acceptance; implement the check as part of the successUrl
refinement or custom validator so any non-allowed absolute URL is rejected and a
clear validation error is returned.
There was a problem hiding this comment.
5 issues found across 15 files
Confidence score: 2/5
- Merge risk is high because there are multiple high-severity, high-confidence issues in Stripe error handling and initialization paths, with potential user-facing failures and internal detail exposure.
lib/stripe/createSubscriptionSessionHandler.tsandlib/stripe/mapToSubscriptionSessionError.tscurrently return raw upstream/internal error text for server-side failures; this can leak implementation details and should be replaced with a fixed 500-safe message.lib/stripe/client.tsconstructs the Stripe client at module load with an asserted env var, which can fail at import/build time whenSTRIPE_SKis missing; this can break runtime startup rather than just a single request path.- Pay close attention to
lib/stripe/createSubscriptionSessionHandler.ts,lib/stripe/mapToSubscriptionSessionError.ts,lib/stripe/client.ts, andlib/stripe/createStripeSession.ts- sanitize 5xx responses, avoid import-time client failures, and move the hardcoded price ID to configuration.
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/createSubscriptionSessionHandler.ts">
<violation number="1" location="lib/stripe/createSubscriptionSessionHandler.ts:29">
P1: Use a generic 500 response in this catch block instead of returning `error.message` with status 400.
(Based on your team's feedback about avoiding raw exception text in API error responses.) [FEEDBACK_USED]</violation>
</file>
<file name="lib/stripe/mapToSubscriptionSessionError.ts">
<violation number="1" location="lib/stripe/mapToSubscriptionSessionError.ts:4">
P2: Custom agent: **Module should export a single primary function whose name matches the filename**
Primary exported function name does not match the filename (mapToSubscriptionSessionError vs mapToSubscriptionSessionErrorResponse).</violation>
<violation number="2" location="lib/stripe/mapToSubscriptionSessionError.ts:12">
P1: Do not forward upstream `error/message` text for 5xx responses; return a fixed "Internal server error" message to avoid leaking internal details.
(Based on your team's feedback about masking internal details in 500 responses.) [FEEDBACK_USED]</violation>
</file>
<file name="lib/stripe/createStripeSession.ts">
<violation number="1" location="lib/stripe/createStripeSession.ts:14">
P2: Avoid hardcoding the Stripe Price ID in code; load it from configuration so checkout sessions don’t break across environments or price changes.</violation>
</file>
<file name="lib/stripe/client.ts">
<violation number="1" location="lib/stripe/client.ts:3">
P1: Avoid top-level Stripe client construction with an asserted env var; on stripe-node v17+ this can fail at import/build time when `STRIPE_SK` is unavailable.</violation>
</file>
Architecture diagram
sequenceDiagram
participant Client
participant Route as API Route (Next.js)
participant Handler as Session Handler
participant Auth as Auth Service
participant Stripe as Stripe API
Note over Client,Stripe: NEW: Stripe Subscription Checkout Flow
Client->>Route: OPTIONS (CORS Preflight)
Route-->>Client: 200 OK + CORS Headers
Client->>Route: POST /api/subscriptions/sessions
Route->>Handler: createSubscriptionSessionHandler(req)
Handler->>Handler: NEW: Validate JSON & Zod Schema
alt Invalid Input
Handler-->>Client: 400 Bad Request { error }
end
Handler->>Auth: validateAuthContext(request, accountId)
alt Auth Failure
Auth-->>Handler: NextResponse (Error)
Handler->>Handler: NEW: mapToSubscriptionSessionErrorResponse()
Handler-->>Client: 4xx Error { error: message }
else Auth Success
Auth-->>Handler: AuthContext (accountId)
end
Handler->>Stripe: NEW: Create Checkout Session (STRIPE_SK)
Note right of Stripe: Sets 30-day trial,<br/>fixed price ID,<br/>and metadata.accountId
alt Stripe Success
Stripe-->>Handler: Session Object (id, url)
Handler-->>Client: 200 OK { id, url }
else Stripe Error / URL Missing
Stripe-->>Handler: Error / Null URL
Handler-->>Client: 400 Bad Request { error }
end
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
…ToSubscriptionSessionError - Updated the function name from `mapToSubscriptionSessionErrorResponse` to `mapToSubscriptionSessionError` for consistency and clarity. - Adjusted import statements and references in related files to reflect the new function name.
There was a problem hiding this comment.
♻️ Duplicate comments (1)
lib/stripe/validateCreateSubscriptionSessionRequest.ts (1)
31-33:⚠️ Potential issue | 🟠 MajorRemove request-body account override from auth validation.
Passing
parsed.data.accountIdintovalidateAuthContextreintroduces body-driven account selection; account identity should be derived from auth context only for this endpoint.Suggested fix
- const authContext = await validateAuthContext(request, { - accountId: parsed.data.accountId, - }); + const authContext = await validateAuthContext(request);Based on learnings, "Applies to app/api/**/route.ts : Never use
account_idin request bodies or tool schemas; always derive the account ID from authentication."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/stripe/validateCreateSubscriptionSessionRequest.ts` around lines 31 - 33, The auth validation currently overrides the account identity by passing parsed.data.accountId into validateAuthContext; remove that override so account ID is derived solely from the authentication context — locate the call to validateAuthContext in validateCreateSubscriptionSessionRequest.ts (where authContext is assigned) and change it to call validateAuthContext without the second argument (remove parsed.data.accountId reference and any related logic that treats the request body as the source of account identity).
🧹 Nitpick comments (2)
lib/stripe/validateCreateSubscriptionSessionRequest.ts (2)
4-10: Align validate-file structure with the validateBody/validateQuery convention.For
lib/**/validate*.ts, keep the validation schema and inferred type exported together in avalidate<EndpointName>Body.ts/validate<EndpointName>Query.tsfile instead of splitting schema/type across separate files.As per coding guidelines, "Create validate functions in
validate<EndpointName>Body.tsorvalidate<EndpointName>Query.tsfiles that export both the schema and inferred TypeScript type."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/stripe/validateCreateSubscriptionSessionRequest.ts` around lines 4 - 10, The validateCreateSubscriptionSessionRequest.ts file splits the validation schema and inferred type; align with the convention by consolidating them into a validateCreateSubscriptionSessionBody.ts file that exports createSubscriptionSessionBodySchema and an inferred TypeScript type (replace ValidatedCreateSubscriptionSessionRequest) together; update the export name to something like CreateSubscriptionSessionBody (or CreateSubscriptionSessionBodyType) inferred from createSubscriptionSessionBodySchema using z.infer, remove the standalone ValidatedCreateSubscriptionSessionRequest, and update any imports that referenced the old file to import both the schema and the inferred type from validateCreateSubscriptionSessionBody.ts (you can keep mapToSubscriptionSessionError usage elsewhere unchanged).
12-42: Split the validator into smaller helpers (parse, schema, auth).This function is over 20 lines and mixes multiple responsibilities; extracting helpers will improve readability and testability.
As per coding guidelines, "Flag functions longer than 20 lines ... Keep functions small and focused."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/stripe/validateCreateSubscriptionSessionRequest.ts` around lines 12 - 42, The validateCreateSubscriptionSessionRequest function is doing parsing, schema validation, and auth checks in one long block; split it into three small helpers: a parseJson(request) helper that reads request.json() and returns either parsed body or a NextResponse error, a validateCreateSubscriptionSessionBody(body) helper that calls createSubscriptionSessionBodySchema.safeParse and returns typed data or a NextResponse with the first issue message, and an authorizeCreateSubscriptionSession(request, accountId) helper that wraps validateAuthContext and mapToSubscriptionSessionError to return either an authContext or a NextResponse; then refactor validateCreateSubscriptionSessionRequest to call these three helpers in sequence and return the final validated object (using symbols validateCreateSubscriptionSessionRequest, createSubscriptionSessionBodySchema.safeParse, validateAuthContext, mapToSubscriptionSessionError to locate code).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@lib/stripe/validateCreateSubscriptionSessionRequest.ts`:
- Around line 31-33: The auth validation currently overrides the account
identity by passing parsed.data.accountId into validateAuthContext; remove that
override so account ID is derived solely from the authentication context —
locate the call to validateAuthContext in
validateCreateSubscriptionSessionRequest.ts (where authContext is assigned) and
change it to call validateAuthContext without the second argument (remove
parsed.data.accountId reference and any related logic that treats the request
body as the source of account identity).
---
Nitpick comments:
In `@lib/stripe/validateCreateSubscriptionSessionRequest.ts`:
- Around line 4-10: The validateCreateSubscriptionSessionRequest.ts file splits
the validation schema and inferred type; align with the convention by
consolidating them into a validateCreateSubscriptionSessionBody.ts file that
exports createSubscriptionSessionBodySchema and an inferred TypeScript type
(replace ValidatedCreateSubscriptionSessionRequest) together; update the export
name to something like CreateSubscriptionSessionBody (or
CreateSubscriptionSessionBodyType) inferred from
createSubscriptionSessionBodySchema using z.infer, remove the standalone
ValidatedCreateSubscriptionSessionRequest, and update any imports that
referenced the old file to import both the schema and the inferred type from
validateCreateSubscriptionSessionBody.ts (you can keep
mapToSubscriptionSessionError usage elsewhere unchanged).
- Around line 12-42: The validateCreateSubscriptionSessionRequest function is
doing parsing, schema validation, and auth checks in one long block; split it
into three small helpers: a parseJson(request) helper that reads request.json()
and returns either parsed body or a NextResponse error, a
validateCreateSubscriptionSessionBody(body) helper that calls
createSubscriptionSessionBodySchema.safeParse and returns typed data or a
NextResponse with the first issue message, and an
authorizeCreateSubscriptionSession(request, accountId) helper that wraps
validateAuthContext and mapToSubscriptionSessionError to return either an
authContext or a NextResponse; then refactor
validateCreateSubscriptionSessionRequest to call these three helpers in sequence
and return the final validated object (using symbols
validateCreateSubscriptionSessionRequest,
createSubscriptionSessionBodySchema.safeParse, validateAuthContext,
mapToSubscriptionSessionError to locate code).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 2a914f53-62a7-4169-b5a4-c1bd3dff1328
⛔ Files ignored due to path filters (2)
app/api/subscriptions/sessions/__tests__/route.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included byapp/**lib/stripe/__tests__/mapToSubscriptionSessionError.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included bylib/**
📒 Files selected for processing (2)
lib/stripe/mapToSubscriptionSessionError.tslib/stripe/validateCreateSubscriptionSessionRequest.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- lib/stripe/mapToSubscriptionSessionError.ts
There was a problem hiding this comment.
1 issue found across 4 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="app/api/subscriptions/sessions/__tests__/route.test.ts">
<violation number="1" location="app/api/subscriptions/sessions/__tests__/route.test.ts:1">
P2: Custom agent: **Enforce Clear Code Style and Maintainability Practices**
New test file exceeds the 100-line file size limit required by the maintainability rule.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
- Changed the ACCOUNT constant in route.test.ts from "123e4567-e89b-12d3-a456-426614174000" to "123e4567-e89b-12d3-a456-426614174001" to ensure consistency in test cases.
…ndling - Introduced the STRIPE_SK environment variable in .env.example to ensure proper configuration for Stripe integration. - Updated the Stripe client initialization to throw an error if STRIPE_SK is not set, enhancing robustness. - Modified error handling in createSubscriptionSessionHandler to return a 500 status with a generic error message for internal server errors. - Adjusted validation logic in validateCreateSubscriptionSessionRequest to remove the optional accountId field, simplifying the request structure. - Enhanced mapToSubscriptionSessionError to mask upstream error messages for 5xx responses, improving security. - Updated tests to reflect changes in error handling and validation logic.
…ent access - Merged shared and artist toolkits into the actions catalog to ensure all executable actions are listed, reflecting the full access available to the chat agent. - Updated the executeConnectorAction function to also utilize the merged toolkit, ensuring that all actions listed in the catalog are executable. - Extracted ConnectorActionNotFoundError into a separate file for cleaner error handling and improved test organization. - Rewritten tests to mock the new toolkit structure, ensuring comprehensive coverage of the updated functionality.
There was a problem hiding this comment.
1 issue found across 3 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__/client.test.ts">
<violation number="1" location="lib/stripe/__tests__/client.test.ts:16">
P2: Environment restoration is unsafe: assigning `saved` back directly can leave `STRIPE_SK` in an incorrect state when the original value was undefined. Restore by deleting the key when `saved` is undefined.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
…ants - Updated createStripeSession to utilize STRIPE_SUBSCRIPTION_PRICE_ID and STRIPE_SUBSCRIPTION_TRIAL_PERIOD_DAYS constants for better maintainability and configuration management. - Enhanced tests for createStripeSession to reflect these changes, ensuring consistency in the subscription parameters. - Improved error handling in client tests by refining the management of the STRIPE_SK environment variable.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
lib/stripe/createStripeSession.ts (1)
9-33: ⚡ Quick winRefactor
createStripeSessioninto a slimmer orchestrator function.
createStripeSessionis currently >20 lines (Line 9–33). Extract payload construction to a local helper so the exported function stays focused on orchestration.♻️ Proposed refactor
import type Stripe from "stripe"; import { v4 as uuidV4 } from "uuid"; import stripeClient from "@/lib/stripe/client"; import { STRIPE_SUBSCRIPTION_PRICE_ID, STRIPE_SUBSCRIPTION_TRIAL_PERIOD_DAYS, } from "@/lib/stripe/config"; +function buildStripeSubscriptionSessionData( + accountId: string, + successUrl: string, +): Stripe.Checkout.SessionCreateParams { + const metadata = { accountId }; + + return { + line_items: [ + { + price: STRIPE_SUBSCRIPTION_PRICE_ID, + quantity: 1, + }, + ], + mode: "subscription", + client_reference_id: uuidV4(), + metadata, + subscription_data: { + metadata, + trial_period_days: STRIPE_SUBSCRIPTION_TRIAL_PERIOD_DAYS, + }, + success_url: successUrl, + }; +} + export async function createStripeSession( accountId: string, successUrl: string, ): Promise<Stripe.Checkout.Session> { - const metadata = { accountId }; - - const sessionData: Stripe.Checkout.SessionCreateParams = { - line_items: [ - { - price: STRIPE_SUBSCRIPTION_PRICE_ID, - quantity: 1, - }, - ], - mode: "subscription", - client_reference_id: uuidV4(), - metadata, - subscription_data: { - metadata, - trial_period_days: STRIPE_SUBSCRIPTION_TRIAL_PERIOD_DAYS, - }, - success_url: successUrl, - }; - - return stripeClient.checkout.sessions.create(sessionData); + return stripeClient.checkout.sessions.create( + buildStripeSubscriptionSessionData(accountId, successUrl), + ); }As per coding guidelines, "Flag functions longer than 20 lines" and "Keep functions small and focused."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/stripe/createStripeSession.ts` around lines 9 - 33, The createStripeSession function assembles the Stripe payload inline making the exported function >20 lines; extract the payload construction into a small local helper (e.g., buildStripeSessionParams or createStripePayload) that accepts accountId and successUrl and returns a Stripe.Checkout.SessionCreateParams object (containing line_items using STRIPE_SUBSCRIPTION_PRICE_ID, client_reference_id: uuidV4(), metadata, subscription_data with trial_period_days, and success_url), then reduce createStripeSession to simply call that helper and pass its result to stripeClient.checkout.sessions.create(sessionParams) and return the created session; keep the helper unexported and colocated in the same file so the exported createStripeSession remains a slim orchestrator.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@lib/stripe/createStripeSession.ts`:
- Around line 9-33: The createStripeSession function assembles the Stripe
payload inline making the exported function >20 lines; extract the payload
construction into a small local helper (e.g., buildStripeSessionParams or
createStripePayload) that accepts accountId and successUrl and returns a
Stripe.Checkout.SessionCreateParams object (containing line_items using
STRIPE_SUBSCRIPTION_PRICE_ID, client_reference_id: uuidV4(), metadata,
subscription_data with trial_period_days, and success_url), then reduce
createStripeSession to simply call that helper and pass its result to
stripeClient.checkout.sessions.create(sessionParams) and return the created
session; keep the helper unexported and colocated in the same file so the
exported createStripeSession remains a slim orchestrator.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 06455125-b5b7-453b-ab81-981ada4a5302
⛔ Files ignored due to path filters (4)
app/api/subscriptions/sessions/__tests__/route.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included byapp/**lib/stripe/__tests__/client.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included bylib/**lib/stripe/__tests__/createStripeSession.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included bylib/**lib/stripe/__tests__/createSubscriptionSessionSchemas.test.tsis excluded by!**/*.test.*,!**/__tests__/**and included bylib/**
📒 Files selected for processing (2)
lib/stripe/config.tslib/stripe/createStripeSession.ts
✅ Files skipped from review due to trivial changes (1)
- lib/stripe/config.ts
There was a problem hiding this comment.
KISS - please delete this file.
There was a problem hiding this comment.
Why are you making changes to our prettier rules in a pr for stripe endpoint implementation?
- Deleted the .gitattributes file to simplify project configuration. - Updated .prettierrc by removing the "endOfLine" setting for cleaner formatting consistency.
YAGNI: client_reference_id is a soft-convenience pointer string with no
readers in either api or chat — never round-trips, never queried via the
sessions.list API (it isn't a filterable param). The migration was
generating a throwaway random uuid that mapped to nothing internally.
Setting it to accountId instead:
- removes the uuid dep entirely (only callsite in this repo)
- makes the Stripe dashboard's "Client reference" column actually
useful for support/debugging
- matches metadata.accountId so reconciliation is consistent
Drop the uuid mock from createStripeSession.test.ts and flip the
not.toBe("acc-1") assertion to toBe("acc-1") — encoding the new
contract instead of the old "value must be a random UUID" stance.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ion-checkout-sessions
Manual verification on preview deploymentTested Setup
Test cases
Stripe session created (T7) — for review
You can validate via the Stripe Dashboard search for either the session id or What this confirms
|
* chore: update Prettier configuration and add new dependencies
- Added "endOfLine": "lf" to the Prettier configuration for consistent line endings.
- Introduced "stripe" and "uuid" packages in package.json for enhanced functionality.
- Updated pnpm-lock.yaml to reflect the addition of new dependencies.
* refactor(stripe): rename mapToSubscriptionSessionErrorResponse to mapToSubscriptionSessionError
- Updated the function name from `mapToSubscriptionSessionErrorResponse` to `mapToSubscriptionSessionError` for consistency and clarity.
- Adjusted import statements and references in related files to reflect the new function name.
* fix(tests): update ACCOUNT UUID in route.test.ts for consistency
- Changed the ACCOUNT constant in route.test.ts from "123e4567-e89b-12d3-a456-426614174000" to "123e4567-e89b-12d3-a456-426614174001" to ensure consistency in test cases.
* feat(stripe): add STRIPE_SK environment variable and improve error handling
- Introduced the STRIPE_SK environment variable in .env.example to ensure proper configuration for Stripe integration.
- Updated the Stripe client initialization to throw an error if STRIPE_SK is not set, enhancing robustness.
- Modified error handling in createSubscriptionSessionHandler to return a 500 status with a generic error message for internal server errors.
- Adjusted validation logic in validateCreateSubscriptionSessionRequest to remove the optional accountId field, simplifying the request structure.
- Enhanced mapToSubscriptionSessionError to mask upstream error messages for 5xx responses, improving security.
- Updated tests to reflect changes in error handling and validation logic.
* fix(connectors): unify action catalog and execution paths for consistent access
- Merged shared and artist toolkits into the actions catalog to ensure all executable actions are listed, reflecting the full access available to the chat agent.
- Updated the executeConnectorAction function to also utilize the merged toolkit, ensuring that all actions listed in the catalog are executable.
- Extracted ConnectorActionNotFoundError into a separate file for cleaner error handling and improved test organization.
- Rewritten tests to mock the new toolkit structure, ensuring comprehensive coverage of the updated functionality.
* feat(stripe): refactor createStripeSession to use configuration constants
- Updated createStripeSession to utilize STRIPE_SUBSCRIPTION_PRICE_ID and STRIPE_SUBSCRIPTION_TRIAL_PERIOD_DAYS constants for better maintainability and configuration management.
- Enhanced tests for createStripeSession to reflect these changes, ensuring consistency in the subscription parameters.
- Improved error handling in client tests by refining the management of the STRIPE_SK environment variable.
* chore: remove .gitattributes and update Prettier configuration
- Deleted the .gitattributes file to simplify project configuration.
- Updated .prettierrc by removing the "endOfLine" setting for cleaner formatting consistency.
* chore(stripe): drop uuid dep, use accountId as client_reference_id
YAGNI: client_reference_id is a soft-convenience pointer string with no
readers in either api or chat — never round-trips, never queried via the
sessions.list API (it isn't a filterable param). The migration was
generating a throwaway random uuid that mapped to nothing internally.
Setting it to accountId instead:
- removes the uuid dep entirely (only callsite in this repo)
- makes the Stripe dashboard's "Client reference" column actually
useful for support/debugging
- matches metadata.accountId so reconciliation is consistent
Drop the uuid mock from createStripeSession.test.ts and flip the
not.toBe("acc-1") assertion to toBe("acc-1") — encoding the new
contract instead of the old "value must be a random UUID" stance.
---------
Co-authored-by: ahmednahima0-beep <ahmednahima0@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Migrate stripe subscription checkout sessions (#493) * chore: update Prettier configuration and add new dependencies - Added "endOfLine": "lf" to the Prettier configuration for consistent line endings. - Introduced "stripe" and "uuid" packages in package.json for enhanced functionality. - Updated pnpm-lock.yaml to reflect the addition of new dependencies. * refactor(stripe): rename mapToSubscriptionSessionErrorResponse to mapToSubscriptionSessionError - Updated the function name from `mapToSubscriptionSessionErrorResponse` to `mapToSubscriptionSessionError` for consistency and clarity. - Adjusted import statements and references in related files to reflect the new function name. * fix(tests): update ACCOUNT UUID in route.test.ts for consistency - Changed the ACCOUNT constant in route.test.ts from "123e4567-e89b-12d3-a456-426614174000" to "123e4567-e89b-12d3-a456-426614174001" to ensure consistency in test cases. * feat(stripe): add STRIPE_SK environment variable and improve error handling - Introduced the STRIPE_SK environment variable in .env.example to ensure proper configuration for Stripe integration. - Updated the Stripe client initialization to throw an error if STRIPE_SK is not set, enhancing robustness. - Modified error handling in createSubscriptionSessionHandler to return a 500 status with a generic error message for internal server errors. - Adjusted validation logic in validateCreateSubscriptionSessionRequest to remove the optional accountId field, simplifying the request structure. - Enhanced mapToSubscriptionSessionError to mask upstream error messages for 5xx responses, improving security. - Updated tests to reflect changes in error handling and validation logic. * fix(connectors): unify action catalog and execution paths for consistent access - Merged shared and artist toolkits into the actions catalog to ensure all executable actions are listed, reflecting the full access available to the chat agent. - Updated the executeConnectorAction function to also utilize the merged toolkit, ensuring that all actions listed in the catalog are executable. - Extracted ConnectorActionNotFoundError into a separate file for cleaner error handling and improved test organization. - Rewritten tests to mock the new toolkit structure, ensuring comprehensive coverage of the updated functionality. * feat(stripe): refactor createStripeSession to use configuration constants - Updated createStripeSession to utilize STRIPE_SUBSCRIPTION_PRICE_ID and STRIPE_SUBSCRIPTION_TRIAL_PERIOD_DAYS constants for better maintainability and configuration management. - Enhanced tests for createStripeSession to reflect these changes, ensuring consistency in the subscription parameters. - Improved error handling in client tests by refining the management of the STRIPE_SK environment variable. * chore: remove .gitattributes and update Prettier configuration - Deleted the .gitattributes file to simplify project configuration. - Updated .prettierrc by removing the "endOfLine" setting for cleaner formatting consistency. * chore(stripe): drop uuid dep, use accountId as client_reference_id YAGNI: client_reference_id is a soft-convenience pointer string with no readers in either api or chat — never round-trips, never queried via the sessions.list API (it isn't a filterable param). The migration was generating a throwaway random uuid that mapped to nothing internally. Setting it to accountId instead: - removes the uuid dep entirely (only callsite in this repo) - makes the Stripe dashboard's "Client reference" column actually useful for support/debugging - matches metadata.accountId so reconciliation is consistent Drop the uuid mock from createStripeSession.test.ts and flip the not.toBe("acc-1") assertion to toBe("acc-1") — encoding the new contract instead of the old "value must be a random UUID" stance. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Sweets Sweetman <sweetmantech@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(api): migrate GET /api/ai/models (#491) * feat(api): migrate misc-reads endpoints (email, ai/models, youtube/channel-info) Adds api parity for chat's /api/email, /api/ai/models, /api/youtube/channel-info ahead of the chat-side cutover. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(api): address AI review on misc-reads PR - updateContact: switch to loops v6 object-arg signature (fixes Vercel build) - handlers: stop leaking raw exception text in 500 responses - loopsClient: convert singleton to factory function matching filename - youtubeErrors: align export name with filename - getYouTubeChannelHandler: explicit boolean discriminator narrowing for build Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(api): change /api/email from GET to POST Email tracking upserts a Loops contact — a write — so it should not be exposed as GET with email in query params (PII in URLs, cacheable responses). Switches to POST with `{ email }` JSON body. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * revert(api): drop /api/email endpoint Loops is no longer used; the email tracking endpoint is removed from this migration entirely. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(api): simplify youtube channel-info flow - Fold token validation into validateYouTubeChannelInfoRequest so the handler is a thin fetch+respond orchestrator - Rename validateYouTubeChannelQuery → validateYouTubeChannelInfoRequest (matches validateArtistRequest / validateChatRequest convention) - Drop buildYouTubeUtilityError + error code catalog: every token-level failure collapses to the same {success:false, tokenStatus:"invalid"} response, so the typed codes carried no information out Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(api): throw from youtube libs, single catch at validator - refreshStoredYouTubeToken now throws on every failure (no more catch-log-return-null); the deep invalid_grant type guard is gone since the general console.error already logs the full error - validateYouTubeTokens lets errors propagate; null is reserved for the legitimate "no tokens row / no refresh_token" cases - validateYouTubeChannelInfoRequest is the single catch boundary; thrown errors and null both collapse to the same tokenStatus:invalid response, matching chat parity Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(api): drop redundant explicit return types from misc-reads libs TS inference already produces the same types for these helpers; explicit annotations were noise. Kept annotations on discriminated-union returns (validators, route handlers) and anywhere inference would widen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(api): drop more redundant return types from misc-reads libs Continues 7f64027 — TS inference produces the same types for these helpers; explicit annotations were noise. Kept the discriminated-union annotation on validateYouTubeChannelInfoRequest where it documents an intentional contract. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(api): slim youtube channel-info response and validator - drop response fields that no chat consumer reads - validateYouTubeTokens throws on every unusable case; the request validator's catch is the single failure boundary (no more let/null) - drop now-unnecessary explicit return types Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(api): trim youtube tests, jsdocs, type duplication, response - Drop `success` from /api/youtube/channel-info; conform to the `{ status: "success", channels }` envelope used across the rest of the api (lib/networking/successResponse.ts, songs, posts, accounts, chats, artists). Clients infer "needs re-auth" from `channels === null`. - Keep only validator + handler tests; cover no-token, expired-no-refresh, refresh-failure, and db-update-failure cases at the validator boundary via it.each on the thrown-error contract. - Trim jsdocs to information-bearing lines only; drop stale "mirrors chat" notes and self-evident @param/@returns lines on lib helpers. - Dedupe YouTubeTokensRow: keep the export in validateYouTubeTokens.ts (the contract owner) and import it in refreshStoredYouTubeToken.ts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(api): return proper status codes for youtube channel-info errors - 401 + {status:"error",message} when stored YouTube tokens can't be validated/refreshed (re-auth needed) - 502 + {status:"error",message} for upstream YouTube API failures - Drops the inconsistent "200 status:success channels:null" shape Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(api): drop YouTubeTokensRow alias and return type validateYouTubeTokens lets TS infer the return type; the alias is gone (only refreshStoredYouTubeToken used it, now inlined as Tables<>). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(api): bypass @ai-sdk/gateway SDK for model catalog The SDK's getAvailableModels() rejects valid responses with a Zod error ("expected error.object"), which silenced 194 models behind an empty array. Match chat's direct fetch to /v1/ai/config — same approach, same payload, same parity. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(api): pin @ai-sdk/gateway to 2.x for catalog reads The 3.x SDK requires specificationVersion:"v3" via a strict Zod literal, but the live /v1/ai/config endpoint still emits "v2" descriptors. Pin to 2.0.83 (latest 2.x, expects "v2") so gateway.getAvailableModels() works as designed. This is a catalog-read-only consumer; api never uses gateway() as a model factory, so 2.x's LanguageModelV2 surface doesn't collide with the rest of the codebase on @ai-sdk/provider@3. Restore the SDK call site (no more direct fetch hack); a comment flags when to bump back to 3.x. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(api): drop redundant jsdoc on getAvailableModels Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(api): restore original jsdoc on getAvailableModels Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(api): gate /api/youtube/channel-info behind auth + artist access Layers in validateAuthContext + checkAccountArtistAccess so anonymous callers can no longer probe stored YouTube tokens by guessing UUIDs. Returns 401 on missing/invalid auth, 403 when the authed caller has no access to the requested artist. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * revert(api): drop /api/youtube/channel-info from this migration YouTube channel-info will migrate via Composio in a separate PR. Reduces Group 1 scope to just /api/ai/models. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Sweets Sweetman <sweetmantech@gmail.com> --------- Co-authored-by: ahmednahima0-beep <ahmednahima0@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Arpit Gupta <arpitgupta1214@gmail.com>
Summary by cubic
Adds
POST /api/subscriptions/sessionsto create Stripe subscription checkout sessions with CORS, strict input/auth validation, and safer error handling. Sessions use a fixed price with a 30‑day trial and return{ id, url }; usesstripeanduuidand requiresSTRIPE_SK.New Features
OPTIONSandPOST; dynamic and no-cache.successUrlURL only) and auth;accountIdcomes from auth, not the body.{ id, url }.Refactors
lib/stripe/clientandSTRIPE_SKenv; throws if missing; updated.env.example.mapToSubscriptionSessionErrorResponsetomapToSubscriptionSessionError; enforce LF line endings via Prettier and.gitattributes.Written for commit 9c25be8. Summary will update on new commits. Review in cubic
Summary by CodeRabbit