Repository navigation
feat: add Stripe support for Fastify template - #283
Conversation
WalkthroughAdds a Fastify Stripe template: a singleton Stripe client and exported types, multiple Fastify Stripe route modules (checkout, customer, payments, products, subscriptions, webhook) with routing aggregator, registry entry for Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Client
participant Fastify as Fastify /stripe/checkout
participant Stripe as Stripe API
Client->>Fastify: POST /stripe/checkout { productCart, customer?, success_url, cancel_url }
Fastify->>Fastify: Validate body (zod)
alt invalid
Fastify-->>Client: 400 Validation error
else valid
Fastify->>Stripe: create or retrieve Customer (optional)
Fastify->>Stripe: create Checkout Session (line_items, urls, metadata)
Stripe-->>Fastify: session { url }
Fastify-->>Client: 200 { checkout_url }
end
sequenceDiagram
autonumber
participant Stripe as Stripe (webhook sender)
participant Fastify as Fastify /stripe/webhook
Stripe->>Fastify: POST raw body + Stripe-Signature header
Fastify->>Fastify: check STRIPE_WEBHOOK_SECRET & header
alt missing/invalid secret/signature
Fastify-->>Stripe: 400 / 401
else verified
Fastify->>Fastify: constructEvent & switch on event.type
Fastify-->>Fastify: handle event (subscriptions/payments/checkout/invoice/charge)
Fastify-->>Stripe: 200
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (4)
💤 Files with no reviewable changes (3)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
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: 8
🧹 Nitpick comments (3)
packages/templates/fastify/src/routes/stripe/checkout.ts (2)
52-58: Handle nullable customer fields correctly.Lines 54-56 use the nullish coalescing operator to default
name, andphoneto empty strings when they're null/undefined. However:
nameis required by the schema (line 21), so it can't be null/undefinedphone_numberis explicitly.optional().nullable()(line 22), so defaulting to''when null may not be intendedConsider this refactor for clarity:
if (customer && 'email' in customer) { const stripeCustomer = await stripe.customers.create({ - email: customer.email ?? '', - name: customer.name ?? '', - phone: customer.phone_number ?? '', + email: customer.email, + name: customer.name, + ...(customer.phone_number && { phone: customer.phone_number }), })
59-61: Consider validating existing customer_id.When an existing
customer_idis provided, there's no verification that the customer exists in Stripe. If an invalid ID is provided, the checkout session creation will fail with a less helpful error.Optionally retrieve the customer first to provide better error messages:
} else if (customer && 'customer_id' in customer) { try { await stripe.customers.retrieve(customer.customer_id) customerId = customer.customer_id } catch (err) { return reply.status(400).send({ error: 'Invalid customer_id' }) } }packages/templates/fastify/src/routes/stripe/subscriptions.ts (1)
49-49: Validate status parameter before type assertion.Line 49 casts
statustoStripe.Subscription.Statuswithout validation. If an invalid status is provided, Stripe will reject it, but a client-side check would provide better UX.Add validation:
+const validStatuses = ['active', 'canceled', 'incomplete', 'incomplete_expired', 'past_due', 'trialing', 'unpaid', 'paused'] as const + if (typeof customer_id === 'string') params.customer = customer_id if (typeof limit === 'string') params.limit = parseInt(limit) if (typeof starting_after === 'string') params.starting_after = starting_after -if (typeof status === 'string') params.status = status as Stripe.Subscription.Status +if (typeof status === 'string' && validStatuses.includes(status as any)) { + params.status = status as Stripe.Subscription.Status +}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
packages/templates/fastify/src/lib/stripe.ts(1 hunks)packages/templates/fastify/src/routes/route.ts(1 hunks)packages/templates/fastify/src/routes/stripe/checkout.ts(1 hunks)packages/templates/fastify/src/routes/stripe/customer.ts(1 hunks)packages/templates/fastify/src/routes/stripe/payments.ts(1 hunks)packages/templates/fastify/src/routes/stripe/products.ts(1 hunks)packages/templates/fastify/src/routes/stripe/route.ts(1 hunks)packages/templates/fastify/src/routes/stripe/subscriptions.ts(1 hunks)packages/templates/fastify/src/routes/stripe/webhook.ts(1 hunks)packages/templates/registry.json(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
packages/templates/fastify/src/routes/stripe/webhook.ts (1)
packages/templates/fastify/src/lib/stripe.ts (3)
getStripe(5-23)Subscription(27-27)PaymentIntent(28-28)
packages/templates/fastify/src/routes/stripe/customer.ts (1)
packages/templates/fastify/src/lib/stripe.ts (1)
getStripe(5-23)
packages/templates/fastify/src/routes/stripe/route.ts (6)
packages/templates/fastify/src/routes/stripe/checkout.ts (1)
checkoutRoutes(7-87)packages/templates/fastify/src/routes/stripe/customer.ts (1)
customerRoutes(7-138)packages/templates/fastify/src/routes/stripe/payments.ts (1)
paymentsRoutes(7-115)packages/templates/fastify/src/routes/stripe/products.ts (1)
productsRoutes(6-82)packages/templates/fastify/src/routes/stripe/subscriptions.ts (1)
subscriptionsRoutes(7-130)packages/templates/fastify/src/routes/stripe/webhook.ts (1)
webhookRoutes(6-127)
packages/templates/fastify/src/routes/stripe/products.ts (1)
packages/templates/fastify/src/lib/stripe.ts (1)
getStripe(5-23)
packages/templates/fastify/src/routes/stripe/payments.ts (1)
packages/templates/fastify/src/lib/stripe.ts (1)
getStripe(5-23)
packages/templates/fastify/src/routes/stripe/subscriptions.ts (1)
packages/templates/fastify/src/lib/stripe.ts (2)
getStripe(5-23)Subscription(27-27)
packages/templates/fastify/src/routes/stripe/checkout.ts (1)
packages/templates/fastify/src/lib/stripe.ts (1)
getStripe(5-23)
🔇 Additional comments (13)
packages/templates/fastify/src/routes/route.ts (1)
1-4: LGTM! Clean aggregation pattern.The changes correctly extend the module exports to include Stripe routes alongside DodoPayments routes, following the established pattern.
packages/templates/fastify/src/routes/stripe/route.ts (1)
10-17: Route registration structure is correct.The route aggregator properly registers all six Stripe sub-route modules with appropriate prefixes.
packages/templates/registry.json (1)
318-384: Verify the dual route.ts entries are intentional.The template includes both:
- Line 330-333:
packages/templates/fastify/src/routes/stripe/route.ts(target:routes/stripe/route.ts)- Line 372-375:
packages/templates/fastify/src/routes/route.ts(target:routes/route.ts)Confirm this is correct. The first is the Stripe sub-routes aggregator, while the second is the main routes file that exports both
dodopaymentsRoutesandstripeRoutes. Both are needed for proper integration.packages/templates/fastify/src/routes/stripe/webhook.ts (4)
17-21: Webhook secret validation is secure.The handler correctly checks for
STRIPE_WEBHOOK_SECRETbefore processing any requests and returns an appropriate error. The 500 status is acceptable here as a missing secret is a configuration/deployment issue rather than a client error.
24-24: Verify rawBody fallback behavior.The fallback
JSON.stringify(request.body ?? {})may not produce the exact raw body that Stripe sent, which could cause signature verification failures. Thefastify-raw-bodyplugin should populaterawBodywhen properly configured, but this fallback could mask configuration issues.Ensure your documentation clearly states that
fastify-raw-bodymust be registered and configured correctly, and consider logging a warning when the fallback is used.Consider adding a warning:
- const rawBody: any = (request as any).rawBody ?? JSON.stringify(request.body ?? {}) + const rawBody: any = (request as any).rawBody + if (!rawBody) { + request.log.warn('rawBody not found; ensure fastify-raw-body is registered') + return reply.status(500).send({ error: 'Raw body required for webhook verification' }) + }
34-39: Signature verification is implemented correctly.The handler properly uses
stripe.webhooks.constructEventfor cryptographic verification and returns 400 on verification failure, preventing replay attacks and unauthorized webhook calls.
43-115: Event handling structure is sound.The switch statement covers key Stripe event types with appropriate logging. Unhandled events are logged but don't cause errors, which is correct for forward compatibility.
packages/templates/fastify/src/routes/stripe/checkout.ts (1)
10-34: Schema definitions are well-structured.The zod schemas properly validate checkout session data with appropriate constraints and error messages, including the union type for new vs. existing customers.
packages/templates/fastify/src/routes/stripe/subscriptions.ts (2)
10-22: Validation schemas are appropriate.The zod schemas correctly define optional fields for subscription updates and cancellations with proper enum constraints.
121-123: Resume implementation is correct.Setting
pause_collection: nullis the correct way to resume a paused Stripe subscription per Stripe's API documentation.packages/templates/fastify/src/routes/stripe/products.ts (1)
6-82: Product and price endpoints are well-implemented.All four endpoints correctly:
- Validate required parameters
- Build appropriate Stripe API parameters
- Handle errors with logging and appropriate status codes
- Parse query parameters with type checks
packages/templates/fastify/src/routes/stripe/payments.ts (2)
10-14: Refund schema is well-defined.The schema correctly validates refund parameters with appropriate constraints (positive integer for amount, enum for reason).
7-115: Payment endpoints are correctly implemented.All five endpoints properly:
- Validate required IDs from query parameters
- Use appropriate Stripe API calls
- Handle validation errors with structured responses
- Log errors and return 500 on failures
…e limit params, remove @ts-nocheck
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
packages/templates/fastify/src/lib/stripe.ts (1)
108-133: Previous critical issue resolved; consider safer handling of session.url.The conditional customer field assignment (lines 123-125) correctly fixes the previous issue where an empty string was sent to Stripe. Well done!
However, line 128 uses a non-null assertion (
session.url!) which could throw at runtime if Stripe returnsnull. Although rare in practice, consider a safer approach:- const session = await stripe.checkout.sessions.create(sessionParams); - return { checkout_url: session.url! }; + const session = await stripe.checkout.sessions.create(sessionParams); + if (!session.url) { + throw new Error('Stripe returned a checkout session without a URL'); + } + return { checkout_url: session.url };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
packages/templates/fastify/src/lib/stripe.ts(1 hunks)packages/templates/fastify/src/routes/stripe/checkout.ts(1 hunks)packages/templates/fastify/src/routes/stripe/customer.ts(1 hunks)packages/templates/fastify/src/routes/stripe/payments.ts(1 hunks)packages/templates/fastify/src/routes/stripe/products.ts(1 hunks)packages/templates/fastify/src/routes/stripe/route.ts(1 hunks)packages/templates/fastify/src/routes/stripe/subscriptions.ts(1 hunks)packages/templates/fastify/src/routes/stripe/webhook.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/templates/fastify/src/routes/stripe/checkout.ts
- packages/templates/fastify/src/routes/stripe/webhook.ts
🧰 Additional context used
🧬 Code graph analysis (5)
packages/templates/fastify/src/routes/stripe/payments.ts (1)
packages/templates/fastify/src/lib/stripe.ts (1)
getStripe(5-23)
packages/templates/fastify/src/routes/stripe/route.ts (7)
packages/templates/fastify/src/routes/route.ts (1)
stripeRoutes(4-4)packages/templates/fastify/src/routes/stripe/checkout.ts (1)
checkoutRoutes(6-86)packages/templates/fastify/src/routes/stripe/customer.ts (1)
customerRoutes(6-161)packages/templates/fastify/src/routes/stripe/payments.ts (1)
paymentsRoutes(6-114)packages/templates/fastify/src/routes/stripe/products.ts (1)
productsRoutes(5-81)packages/templates/fastify/src/routes/stripe/subscriptions.ts (1)
subscriptionsRoutes(6-129)packages/templates/fastify/src/routes/stripe/webhook.ts (1)
webhookRoutes(5-126)
packages/templates/fastify/src/routes/stripe/customer.ts (1)
packages/templates/fastify/src/lib/stripe.ts (1)
getStripe(5-23)
packages/templates/fastify/src/routes/stripe/products.ts (1)
packages/templates/fastify/src/lib/stripe.ts (1)
getStripe(5-23)
packages/templates/fastify/src/routes/stripe/subscriptions.ts (1)
packages/templates/fastify/src/lib/stripe.ts (2)
getStripe(5-23)Subscription(27-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Typecheck and Build
🔇 Additional comments (1)
packages/templates/fastify/src/lib/stripe.ts (1)
5-23: LGTM! Well-implemented singleton with clear error guidance.The lazy initialization pattern is efficient, and the detailed error message provides actionable troubleshooting steps for missing environment variables.
|
@thepushkaraj also this is done |
thepushkaraj
left a comment
There was a problem hiding this comment.
Looks like helper functions such as getProducts(), getCustomer(), etc. are not used anywhere in the template routes, can you please remove them from lib/stripe.ts so only getStripe() and type aliases are exposed?
The PR looks good, we can merge it after removing these unused helper functions.
|
@yashranaway is attempting to deploy a commit to the Dodo Payments Team on Vercel. A member of the Team first needs to authorize it. |
|
@thepushkaraj done |
|
Thanks @yashranaway |
|
Summary
Adds Stripe support for the Fastify template, enabling users to choose between DodoPayments and Stripe providers when using the Fastify framework. This implementation follows the exact patterns established by existing Hono and Express templates.
Changes
src/lib/stripe.ts)fastify-stripetemplate to registry.json with proper dependenciesScreenshots/Recordings (if UI)
N/A - Backend template implementation only
How to Test
Steps to validate locally:
Checklist
closes #209
Summary by CodeRabbit
New Features
Chores
Removed