Implement backend payment and pricing model#60
Conversation
Co-authored-by: dogesman098 <dogesman098@gmail.com>
|
Cursor Agent can help with this pull request. Just |
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
WalkthroughAdds Autumn billing integration: new serverless API routes (checkout, attach, check, customer, track) with Clerk-auth and CORS; backend message creation checks/records usage via Autumn; frontend checkout adds an Autumn fallback; introduces Autumn config (features/products); updates README and environment templates/scripts. Changes
Sequence Diagram(s)sequenceDiagram
participant FE as Frontend (PricingTable)
participant API as /api/autumn-checkout
participant Clerk as Clerk Verify
participant Autumn as Autumn API
FE->>API: POST /api/autumn-checkout {product_id}
API->>Clerk: Verify token (issuer,audience)
alt Auth OK
API->>Autumn: POST /v1/checkout {customer_id, product_id}
alt Autumn OK
Autumn-->>API: {data|payload}
API-->>FE: 200 {url or data}
FE->>FE: Redirect if url present
else Autumn Error
Autumn-->>API: error
API-->>FE: Upstream status + error
end
else Auth Fail
API-->>FE: 401 Unauthorized
end
sequenceDiagram
participant Client as Client (App)
participant Convex as convex/messages.createMessage
participant Autumn as Autumn API
Client->>Convex: createMessage(role, content)
alt role == 'user'
Convex->>Autumn: POST /v1/check {customer_id, feature_id:'messages'}
alt Allowed == true
Convex->>Convex: Insert message, update chat
Convex->>Autumn: POST /v1/track {customer_id, feature_id:'messages'}
Convex-->>Client: success
else Disallowed/Null
Convex-->>Client: error "No more messages..."
end
else non-user
Convex->>Convex: Insert message
Convex-->>Client: success
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~40 minutes Poem
✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
|
Claude finished @otdoges's task —— View job PR Review: Backend Payment and Pricing Model ImplementationReviewing the Autumn payment integration implementation for code quality, security, performance, and best practices. Review Tasks:
🔍 Code Review SummaryThis PR implements a comprehensive Autumn billing integration with solid security practices and good error handling. The implementation follows the project's patterns well and includes proper authentication and validation. ✅ StrengthsSecurity Implementation
Code Quality
Integration Quality
|
There was a problem hiding this comment.
Actionable comments posted: 28
🔭 Outside diff range comments (3)
src/components/pricing/CustomPricingTable.tsx (3)
93-96: Auth check contradicts the comment. Fix condition or comment.Comment says “authenticated if signed in even if Convex sync is in progress,” but code requires !authLoading. Align to intent.
Apply this diff to match the comment:
-// Better authentication check: user is considered authenticated if signed in to Clerk -// even if Convex sync is still in progress -const isUserAuthenticated = isSignedIn && !authLoading; +// Consider the user authenticated if signed in to Clerk (ignore Convex sync state) +const isUserAuthenticated = !!isSignedIn;
171-181: Remove theanytype and add minimal typing for error payload parsing.Violates strict TS guideline to never use
any. Useunknownwith a narrow cast.Apply this diff:
- let errorData: any = null; + let errorData: unknown = null; @@ - errorData = await response.json(); - errorMessage = errorData.message || errorMessage; + errorData = await response.json(); + const maybe = errorData as { message?: string }; + if (typeof maybe?.message === 'string') { + errorMessage = maybe.message; + }Add this helper type near the top of the file (outside the changed range):
type ApiErrorPayload = { message?: string; [key: string]: unknown };
23-86: UI copy conflicts with Autumn config (Free: 10 vs 5; Pro: Unlimited vs 100; Price: $29 vs $20).Presently, the UI advertises:
- Free: 10 AI conversations/month (Autumn config: 5)
- Pro: “Unlimited AI conversations” (Autumn config: 100)
- Pro price: $29 (Autumn config: $20)
Aligning these is critical to avoid user-facing inconsistency and billing disputes.
I can either: (a) update UI copy to match current config, or (b) adjust Autumn config and restrict fallback to supported products. Which direction should we take?
🧹 Nitpick comments (8)
scripts/create-env-local.js (1)
67-70: Mirror env template guidance and add a helpful placeholder for AUTUMN_SECRET_KEYTo keep developer onboarding consistent with env-template.txt and reduce setup friction, include a placeholder and a link to Autumn’s dashboard.
Apply this diff:
-# Autumn Billing Integration (optional) -AUTUMN_SECRET_KEY= -# AUTUMN_API_BASE=https://api.useautumn.com +# Autumn Billing Integration (optional) +# Get from: https://dashboard.useautumn.com +AUTUMN_SECRET_KEY=am_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +# AUTUMN_API_BASE=https://api.useautumn.comapi/autumn-attach.ts (2)
60-73: Add a timeout to upstream fetch to avoid hanging function invocationsNetwork hiccups can stall the function. Use AbortController to bound latency.
Apply this diff:
- const apiBase = process.env.AUTUMN_API_BASE || 'https://api.useautumn.com'; - - const upstream = await fetch(`${apiBase}/v1/attach`, { + const apiBase = process.env.AUTUMN_API_BASE || 'https://api.useautumn.com'; + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 10_000); + const upstream = await fetch(`${apiBase}/v1/attach`, { method: 'POST', headers: { 'Authorization': `Bearer ${autumnSecret}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ customer_id: authenticatedUserId, product_id: productId, - }), - }); + }), + signal: controller.signal, + }).finally(() => clearTimeout(timeoutId));
76-79: Avoid leaking upstream error details to clientsReturn a generic message and omit raw payload details. Keep specifics in server logs.
Apply this diff:
- if (!upstream.ok) { - console.error('Autumn attach error:', upstream.status, payload); - return withCors(res, allowedOrigin).status(upstream.status).json({ error: 'Attach Error', message: payload?.message || 'Failed to attach Autumn product', details: payload }); - } + if (!upstream.ok) { + console.error('Autumn attach error:', upstream.status, payload); + const safeMessage = typeof payload?.message === 'string' ? payload.message : 'Failed to attach Autumn product'; + return withCors(res, allowedOrigin).status(upstream.status).json({ error: 'Attach Error', message: safeMessage }); + }README.md (1)
155-160: Document all new Autumn endpoints for completenessList check and track endpoints alongside checkout/attach, so integrators understand the full flow.
Apply this diff:
## Autumn (backend-only) billing -- Add `AUTUMN_SECRET_KEY` to your environment (see `env-template.txt` or `scripts/create-env-local.js`). -- Use `/api/autumn-checkout` to initiate checkout and `/api/autumn-attach` to attach if payment method exists. -- Server enforces feature access for chat messages via Autumn and records usage automatically. +- Add `AUTUMN_SECRET_KEY` to your environment (see `env-template.txt` or `scripts/create-env-local.js`). +- Endpoints: + - `POST /api/autumn-checkout` — initiate checkout + - `POST /api/autumn-attach` — attach if a payment method exists + - `POST /api/autumn-check` — server-side feature gate check + - `POST /api/autumn-track` — record feature usage (e.g., messages) +- Server enforces feature access for chat messages via Autumn and records usage automatically.src/components/pricing/CustomPricingTable.tsx (2)
166-169: Avoid logging full response headers in the browser.This can inadvertently expose sensitive info in devtools. Log only in development or remove.
Apply this diff to guard logs:
-console.log('Response headers:', Object.fromEntries(response.headers.entries())); +if (import.meta.env.DEV) { + console.log('Response headers:', Object.fromEntries(response.headers.entries())); +}
88-91: Prefer React.FC typing for components per guidelines.Add proper component typings for clarity and consistency.
Example:
-const PricingCard = ({ plan, index }: { plan: PricingPlan; index: number }) => { +type PricingCardProps = { plan: PricingPlan; index: number }; +const PricingCard: React.FC<PricingCardProps> = ({ plan, index }) => {And:
-export const CustomPricingTable = () => { +export const CustomPricingTable: React.FC = () => {api/autumn-customer.ts (2)
27-29: Set Allow header on 405 responsesReturn the supported methods to be explicit and spec-compliant.
- if (req.method !== 'GET') { - return withCors(res, allowedOrigin).status(405).json({ error: 'Method Not Allowed', message: 'Only GET requests are allowed' }); - } + if (req.method !== 'GET') { + res.setHeader('Allow', 'GET, OPTIONS'); + return withCors(res, allowedOrigin).status(405).json({ error: 'Method Not Allowed', message: 'Only GET requests are allowed' }); + }
14-14: Explicit return type for handlerAdd an explicit return type for this public API handler to conform to strict TS guidelines.
-export default async function handler(req: VercelRequest, res: VercelResponse) { +export default async function handler(req: VercelRequest, res: VercelResponse): Promise<void> {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
README.md(1 hunks)api/autumn-attach.ts(1 hunks)api/autumn-check.ts(1 hunks)api/autumn-checkout.ts(1 hunks)api/autumn-customer.ts(1 hunks)api/autumn-track.ts(1 hunks)autumn.config.ts(1 hunks)convex/messages.ts(4 hunks)env-template.txt(1 hunks)scripts/create-env-local.js(1 hunks)src/components/pricing/CustomPricingTable.tsx(1 hunks)src/lib/ai.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/convex-security.mdc)
**/*.{ts,tsx}: All Convex queries and mutations MUST use proper authentication. Never accept user IDs from client parameters.
Always verify user owns the data before allowing access.
Use the authenticated user's identity.subject for user references.
Implement proper error messages that don't leak information.
Authentication verification in every function.
Authorization checks for data ownership.
Input validation and sanitization.
Error handling without information leakage.
**/*.{ts,tsx}: Use Sonner for toast notifications to provide consistent user feedback, including success, error, and loading states.
Always handle errors gracefully using try-catch blocks in asynchronous functions, providing user feedback and logging errors.
Provide specific, actionable error messages for form validation errors using toast notifications.
Handle common network error scenarios in catch blocks, providing appropriate toast messages for network errors, authentication errors, and unexpected errors.If using TypeScript, use an enum to store flag names.
Strict TypeScript must be used with no 'any' types allowed
**/*.{ts,tsx}: NEVER useanytype - use proper TypeScript types
Useunknownfor truly unknown data types
Implement proper interface definitions
Do not use empty interfaces; use a type alias instead (e.g.,type InputProps = ...instead ofinterface InputProps {})
All function parameters must be typed
All return types should be explicit for public APIs
Use proper generic constraints
Implement discriminated unions for state management
Use proper interface definitions for error handling types (e.g.,interface ValidationResult { isValid: boolean; error?: string; })
**/*.{ts,tsx}: Always sanitize user input before storing or displaying using a sanitization function likesanitizeText.
Implement comprehensive input validation, including length checks and detection of malicious patterns, as shown in thevalidateInputfunction.
Define and use security constants suc...
Files:
src/lib/ai.tsapi/autumn-track.tsapi/autumn-attach.tsautumn.config.tsapi/autumn-checkout.tsapi/autumn-check.tsapi/autumn-customer.tsconvex/messages.tssrc/components/pricing/CustomPricingTable.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/posthog-integration.mdc)
**/*.{js,jsx,ts,tsx}: Use a consistent naming convention for this storage. enum/const object members should be written UPPERCASE_WITH_UNDERSCORE.
If a custom property for a person or event is at any point referenced in two or more files or two or more callsites in the same file, use an enum or const object, as above in feature flags.
Files:
src/lib/ai.tsapi/autumn-track.tsapi/autumn-attach.tsautumn.config.tsscripts/create-env-local.jsapi/autumn-checkout.tsapi/autumn-check.tsapi/autumn-customer.tsconvex/messages.tssrc/components/pricing/CustomPricingTable.tsx
**/*.{js,jsx}
📄 CodeRabbit Inference Engine (.cursor/rules/posthog-integration.mdc)
If using JavaScript, store flag names as strings to an object declared as a constant, to simulate an enum.
Files:
scripts/create-env-local.js
convex/**/*.ts
📄 CodeRabbit Inference Engine (.cursor/rules/project-overview.mdc)
convex/**/*.ts: All Convex functions must use proper authentication verification
User data access must be controlled with ownership verification for authorization
convex/**/*.ts: All Convex functions must verify authentication
Enforce ownership-based access control (userId checks) for user data in Convex functions
Validate all inputs to Convex functions using Zod schemas
Files:
convex/messages.ts
**/{pages,components}/**/*.tsx
📄 CodeRabbit Inference Engine (.cursor/rules/authentication-patterns.mdc)
Handle all authentication states in components by showing a loading spinner when loading, a sign-in prompt when unauthenticated, and protected content when authenticated.
Files:
src/components/pricing/CustomPricingTable.tsx
**/*.tsx
📄 CodeRabbit Inference Engine (.cursor/rules/error-handling.mdc)
**/*.tsx: Always provide loading feedback to users during asynchronous operations.
Use proper error boundaries to handle component crashes and display user-friendly error UI.Use proper React component typing (e.g.,
const MyComponent: React.FC<Props> = ...)
**/*.tsx: Use theSafeTextReact component for all user-generated content to ensure safe text display.
NEVER usedangerouslySetInnerHTMLwith user content.
NEVER use direct string interpolation in HTML with user content.
Files:
src/components/pricing/CustomPricingTable.tsx
src/components/**/*.tsx
📄 CodeRabbit Inference Engine (.cursor/rules/project-overview.mdc)
Input sanitization and validation must be implemented throughout the codebase to provide XSS protection
Files:
src/components/pricing/CustomPricingTable.tsx
src/**/*.tsx
📄 CodeRabbit Inference Engine (CLAUDE.md)
src/**/*.tsx: Use React Hook Form with Zod validation for client-side forms
Prevent XSS by sanitizing any user-generated content before rendering (avoid unsafe HTML, or sanitize it)
Implement proper error handling with typed error boundaries in React
Files:
src/components/pricing/CustomPricingTable.tsx
🧠 Learnings (1)
📚 Learning: 2025-08-09T23:03:47.108Z
Learnt from: CR
PR: otdoges/zapdev#0
File: .cursor/rules/xss-protection.mdc:0-0
Timestamp: 2025-08-09T23:03:47.108Z
Learning: Applies to **/*.{ts,tsx} : Define and use security constants such as `MAX_MESSAGE_LENGTH`, `MAX_TITLE_LENGTH`, and `MAX_CODE_BLOCK_LENGTH` to enforce input size limits.
Applied to files:
convex/messages.ts
🧬 Code Graph Analysis (5)
api/autumn-track.ts (1)
api/_utils/auth.ts (2)
getBearerOrSessionToken(6-33)verifyClerkToken(35-45)
api/autumn-attach.ts (1)
api/_utils/auth.ts (2)
getBearerOrSessionToken(6-33)verifyClerkToken(35-45)
api/autumn-checkout.ts (1)
api/_utils/auth.ts (2)
getBearerOrSessionToken(6-33)verifyClerkToken(35-45)
api/autumn-check.ts (5)
api/autumn-attach.ts (1)
handler(14-86)api/autumn-checkout.ts (1)
handler(14-86)api/autumn-customer.ts (1)
handler(14-79)api/autumn-track.ts (1)
handler(14-87)api/_utils/auth.ts (2)
getBearerOrSessionToken(6-33)verifyClerkToken(35-45)
api/autumn-customer.ts (2)
api/autumn-attach.ts (1)
handler(14-86)api/_utils/auth.ts (2)
getBearerOrSessionToken(6-33)verifyClerkToken(35-45)
🔇 Additional comments (3)
env-template.txt (1)
41-45: LGTM: Server-only Autumn secrets are introduced correctlyUsing AUTUMN_SECRET_KEY (without a VITE_ prefix) and documenting an optional AUTUMN_API_BASE override is the right approach for backend-only billing. Placement and comments look good.
convex/messages.ts (2)
9-9: Non-functional whitespace changeNo action needed.
996-996: Non-functional whitespace changeNo action needed.
Integrate Autumn for backend-only payments and pricing, including server-side feature checks and usage tracking for chat messages.
Summary by CodeRabbit