Skip to content

feat: add Stripe support for Fastify template - #283

Merged
aagarwal1012 merged 4 commits into
dodopayments:mainfrom
yashranaway:feat/fastify-stripe-support
Nov 8, 2025
Merged

aagarwal1012 merged 4 commits into
dodopayments:mainfrom
yashranaway:feat/fastify-stripe-support

Conversation

@yashranaway

@yashranaway yashranaway commented Oct 4, 2025 •

Copy link
Copy Markdown
Contributor

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

  • Added complete Stripe integration for Fastify template
  • Created Stripe client library with environment validation (src/lib/stripe.ts)
  • Implemented all Stripe routes: checkout, customer, payments, products, subscriptions, webhook
  • Added secure webhook signature verification using Stripe's verification
  • Updated main route exports to include both DodoPayments and Stripe routes
  • Added fastify-stripe template to registry.json with proper dependencies
  • Follows exact Hono/Express implementation patterns for consistency

Screenshots/Recordings (if UI)

N/A - Backend template implementation only

How to Test

Steps to validate locally:

  1. npm ci
  2. npm run typecheck
  3. npm run build
  4. Test CLI template generation:
    BILLINGSDK_REGISTRY_BASE=http://localhost:3000/tr \
    node packages/cli/dist/index.js init --framework fastify --provider stripe --yes --cwd /tmp/test-stripe
  5. Verify Stripe routes are generated and functional

Checklist

  • Title is clear and descriptive
  • Related issue linked (if any) - closes Add CLI support for Fastify + Stripe #209
  • Tests or manual verification steps included
  • CI passes (typecheck & build)
  • Docs updated (if needed) - N/A, follows project standards (no template docs)

closes #209

Summary by CodeRabbit

  • New Features

    • Full Stripe integration for Fastify: browse products/prices, create/retrieve/update customers, manage subscriptions (list/update/cancel/resume), view/list payments and refunds, start checkout sessions (returns URL), and webhook event handling.
    • Centralized Stripe client with secret-key validation and consistent error responses.
  • Chores

    • Added a fastify-stripe template to the registry.
  • Removed

    • Removed legacy Stripe helper APIs from Express, Hono, and Next.js templates, narrowing their public Stripe surface.

@coderabbitai

coderabbitai Bot commented Oct 4, 2025 •

Copy link
Copy Markdown
Contributor

Walkthrough

Adds 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 fastify-stripe, and removals of Stripe wrapper functions from Express/Hono/Next.js templates.

Changes

Cohort / File(s) Summary
Fastify Stripe client
packages/templates/fastify/src/lib/stripe.ts
New singleton accessor getStripe() that lazily initializes Stripe from STRIPE_SECRET_KEY; exports type aliases Product, Customer, Subscription, PaymentIntent.
Fastify Stripe routes
packages/templates/fastify/src/routes/stripe/checkout.ts, .../customer.ts, .../payments.ts, .../products.ts, .../subscriptions.ts, .../webhook.ts
New Fastify route modules registering endpoints for checkout sessions, customer CRUD/listing, payments & refunds, products & prices, subscriptions management (list/update/cancel/resume), and webhook signature verification + event handling. Use zod validation, Stripe SDK, structured errors and logging.
Fastify routes aggregator & export
packages/templates/fastify/src/routes/stripe/route.ts, packages/templates/fastify/src/routes/route.ts
New stripeRoutes aggregator registering sub-routes under /stripe/*; main routes/route.ts export updated to include stripeRoutes.
Templates registry
packages/templates/registry.json
Added fastify-stripe template entry with files for Stripe client and route modules and dependencies: stripe, zod, fastify, fastify-raw-body.
Express / Hono / Next.js templates
packages/templates/express/src/lib/stripe.ts, packages/templates/hono/src/lib/stripe.ts, packages/templates/nextjs/lib/stripe.ts
Removed many previously exported Stripe wrapper functions (product/customer/subscription/payment helpers and checkout) from these templates; retained getStripe and type exports in places. Public API surface reduced.

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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Pay attention to: environment-variable validation and error messages in getStripe.
  • Verify zod schemas and request validation/response shapes across all Fastify route files.
  • Review webhook raw body handling and signature verification correctness.
  • Check removal impacts in Express/Hono/Next.js stripe lib files for callers and template consistency.

Suggested labels

cli

Suggested reviewers

  • tsahil01

Poem

I thump my paw — new routes in line,
Checkout beeps and webhooks chime,
Customers hop, products gleam,
Subscriptions dream and payments stream,
A happy rabbit, code refined 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR includes changes to packages/templates/express/hono/nextjs stripe.ts files that remove exported functions, which are out of scope for the Fastify Stripe support objective. Remove or isolate the function removals from Express, Hono, and Next.js templates into a separate refactoring PR, keeping this PR focused solely on adding Fastify Stripe support.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add Stripe support for Fastify template' is clear, concise, and accurately describes the main change of adding Stripe integration to the Fastify template.
Description check ✅ Passed The pull request description follows the template structure with comprehensive Summary, Changes, testing instructions, and a completed checklist addressing all required sections.
Linked Issues check ✅ Passed The PR fully implements the objectives from issue #209: adds Stripe support as an additional provider for Fastify, mirrors Hono and Express patterns, includes client library with environment validation, implements all required routes, and adds webhook signature verification.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 167674a and 966fc4f.

📒 Files selected for processing (4)
  • packages/templates/express/src/lib/stripe.ts (0 hunks)
  • packages/templates/fastify/src/lib/stripe.ts (1 hunks)
  • packages/templates/hono/src/lib/stripe.ts (0 hunks)
  • packages/templates/nextjs/lib/stripe.ts (0 hunks)
💤 Files with no reviewable changes (3)
  • packages/templates/hono/src/lib/stripe.ts
  • packages/templates/express/src/lib/stripe.ts
  • packages/templates/nextjs/lib/stripe.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/templates/fastify/src/lib/stripe.ts
⏰ 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

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 email, name, and phone to empty strings when they're null/undefined. However:

  • email is required by the schema (line 20), so it can't be null/undefined
  • name is required by the schema (line 21), so it can't be null/undefined
  • phone_number is explicitly .optional().nullable() (line 22), so defaulting to '' when null may not be intended

Consider 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_id is 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 status to Stripe.Subscription.Status without 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

📥 Commits

Reviewing files that changed from the base of the PR and between eb9c814 and 820ff9c.

📒 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 dodopaymentsRoutes and stripeRoutes. 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_SECRET before 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. The fastify-raw-body plugin should populate rawBody when properly configured, but this fallback could mask configuration issues.

Ensure your documentation clearly states that fastify-raw-body must 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.constructEvent for 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: null is 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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 returns null. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 820ff9c and e1ff61d.

📒 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.

@yashranaway

Copy link
Copy Markdown
Contributor Author

@thepushkaraj also this is done

@thepushkaraj thepushkaraj left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@vercel

vercel Bot commented Nov 7, 2025

Copy link
Copy Markdown

@yashranaway is attempting to deploy a commit to the Dodo Payments Team on Vercel.

A member of the Team first needs to authorize it.

@yashranaway

Copy link
Copy Markdown
Contributor Author

@thepushkaraj done

@thepushkaraj

Copy link
Copy Markdown
Contributor

Thanks @yashranaway

@thepushkaraj thepushkaraj left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@yashranaway

Copy link
Copy Markdown
Contributor Author

LGTM
🙏🏼

@aagarwal1012
aagarwal1012 merged commit 66dbfb9 into dodopayments:main Nov 8, 2025
1 of 2 checks passed
@yashranaway
yashranaway deleted the feat/fastify-stripe-support branch November 8, 2025 09:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add CLI support for Fastify + Stripe

3 participants