This document outlines the refactoring of the Stripe webhook handler to improve maintainability, testability, and error handling. The refactoring follows a modular approach, separating concerns into distinct components.
The refactored webhook handler follows a layered architecture:
-
Main Webhook Handler (
webhook.ts.new
): Entry point that receives webhook events, verifies signatures, and routes to appropriate handlers. -
Event Handlers: Specialized modules for handling specific event types:
subscription-events.ts
: Handles subscription created, updated, and deleted eventsinvoice-events.ts
: Handles invoice payment succeeded and failed eventscheckout-events.ts
: Handles checkout session completed eventscharge-events.ts
: Handles charge succeeded eventscustomer-events.ts
: Handles customer deleted events
-
Services: Business logic for different domains:
subscription-service.ts
: Manages subscription operationsgift-service.ts
: Handles gift subscription operationscustomer-service.ts
: Manages customer operations
-
Utilities:
transaction.ts
: Provides transaction management with retry logicidempotency.ts
: Ensures events are processed only onceerror-handling.ts
: Consistent error handling across the application
-
Modularity: Code is organized into focused, single-responsibility modules.
-
Error Handling: Consistent error handling with proper logging.
-
Transaction Management: All database operations run in transactions with retry logic.
-
Idempotency: Events are processed exactly once, preventing duplicate operations.
-
Testability: Modular design makes unit testing easier.
-
Type Safety: Improved TypeScript types throughout the codebase.
Two testing resources have been created:
-
Stripe CLI Commands (
stripe-test-commands.md
): A collection of Stripe CLI commands to test different webhook events. -
Testing Script (
test-webhook.sh
): An interactive shell script to run common test scenarios.
The testing tools use environment variables for Stripe price IDs with fallbacks to default values:
PRICE_ID_MONTHLY="${NEXT_PUBLIC_STRIPE_PRO_MONTHLY_PRICE_ID:-price_monthly123}"
PRICE_ID_ANNUAL="${NEXT_PUBLIC_STRIPE_PRO_ANNUAL_PRICE_ID:-price_annual123}"
PRICE_ID_LIFETIME="${NEXT_PUBLIC_STRIPE_PRO_LIFETIME_PRICE_ID:-price_lifetime123}"
If your environment has these variables set, the tests will use the actual price IDs. Otherwise, they'll use the default values.
-
Start the webhook forwarding:
stripe listen --forward-to http://localhost:3000/api/stripe/webhook
-
Run the test script:
chmod +x test-webhook.sh ./test-webhook.sh
-
Or run individual commands from
stripe-test-commands.md
.
The main webhook handler:
- Verifies the webhook signature
- Extracts the event type
- Routes to the appropriate handler based on event type
- Ensures idempotent processing
- Wraps operations in database transactions
- Provides consistent error responses
Each event handler:
- Processes a specific type of Stripe event
- Extracts relevant data from the event
- Calls appropriate service methods
- Returns a success/failure indicator
Services contain the business logic for:
- Creating and updating subscriptions
- Managing gift subscriptions
- Handling customer operations
- Updating user pro expiration dates
To migrate to the new webhook handler:
- Deploy the new handler alongside the existing one
- Run tests to ensure all functionality works correctly
- Switch the webhook endpoint in Stripe from the old to the new handler
- Monitor for any issues
- Remove the old handler once the new one is stable
Potential future improvements:
- Add more comprehensive logging
- Implement monitoring and alerting
- Add more test coverage
- Consider adding a queue for processing webhook events asynchronously