Skip to content

refactor: Improve Stripe webhook code quality and type safety#1

Closed
DavidASix wants to merge 1 commit into
mainfrom
feature/stripe-payment-webhook
Closed

refactor: Improve Stripe webhook code quality and type safety#1
DavidASix wants to merge 1 commit into
mainfrom
feature/stripe-payment-webhook

Conversation

@DavidASix

Copy link
Copy Markdown
Owner

Refactored src/app/api/purchases/webhook/route.ts to enhance readability, type safety, and maintainability:

  • Introduced helper functions determineUserId and processPayment to encapsulate core logic and reduce nesting in the main POST handler.
  • Defined a CheckoutSessionMetadata interface for clearer typing of session.metadata.userId.
  • Improved type safety by:
    • Reducing the use of the as keyword.
    • Handling potential null values from request headers and Stripe API responses more explicitly.
    • Typing error objects in try-catch blocks.
  • Streamlined error handling for missing Stripe signature and webhook secret.
  • Removed unnecessary comments, focusing on self-documenting code.
  • Ensured consistent and safe access to Stripe customer and payment intent IDs, whether they are strings or expanded objects.

The core logic for user_id determination (checking metadata then prior payments) and payment record insertion remains the same, but is now organized into a cleaner structure.

Refactored `src/app/api/purchases/webhook/route.ts` to enhance
readability, type safety, and maintainability:

- Introduced helper functions `determineUserId` and `processPayment` to
  encapsulate core logic and reduce nesting in the main POST handler.
- Defined a `CheckoutSessionMetadata` interface for clearer typing of
  `session.metadata.userId`.
- Improved type safety by:
    - Reducing the use of the `as` keyword.
    - Handling potential null values from request headers and Stripe API
      responses more explicitly.
    - Typing error objects in `try-catch` blocks.
- Streamlined error handling for missing Stripe signature and webhook secret.
- Removed unnecessary comments, focusing on self-documenting code.
- Ensured consistent and safe access to Stripe customer and payment intent IDs,
  whether they are strings or expanded objects.

The core logic for `user_id` determination (checking metadata then prior
payments) and payment record insertion remains the same, but is now
organized into a cleaner structure.
@DavidASix
DavidASix requested a review from Copilot May 22, 2025 16:32

Copilot AI 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.

Pull Request Overview

Refactors the Stripe webhook handler for better readability, type safety, and adds a new payments schema to persist checkout sessions.

  • Introduces a payments table in the database schema.
  • Extracts core logic into determineUserId and processPayment helper functions.
  • Updates environment example to include the Stripe webhook secret.

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/schema/schema.ts Added a new payments table definition with relevant fields and constraints.
src/app/api/purchases/webhook/route.ts Refactored webhook route to use helper functions, improved typing and errors.
.env.example Added STRIPE_WEBHOOK_SECRET placeholder with instructions.
Comments suppressed due to low confidence (1)

src/app/api/purchases/webhook/route.ts:15

  • Unit tests for the new determineUserId and processPayment functions are missing. Adding tests will verify metadata lookups, error handling paths, and successful database inserts.
async function determineUserId(session: Stripe.Checkout.Session, dbInstance: typeof db): Promise<string | null> {

const paymentData = {
user_id: userId,
stripe_customer_id: typeof session.customer === 'string' ? session.customer : (session.customer?.id || null),
stripe_payment_intent_id: typeof session.payment_intent === 'string' ? session.payment_intent : null,

Copilot AI May 22, 2025

Copy link

Choose a reason for hiding this comment

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

When session.payment_intent is an object, the code sets stripe_payment_intent_id to null. It should extract the id property (e.g., typeof session.payment_intent === 'string' ? session.payment_intent : session.payment_intent?.id) to ensure the payment intent ID is always captured.

Suggested change
stripe_payment_intent_id: typeof session.payment_intent === 'string' ? session.payment_intent : null,
stripe_payment_intent_id: typeof session.payment_intent === 'string'
? session.payment_intent
: session.payment_intent?.id || null,

Copilot uses AI. Check for mistakes.
amount_total: session.amount_total,
currency: session.currency,
payment_status: session.payment_status,
line_items: lineItems ? JSON.parse(JSON.stringify(lineItems)) : null,

Copilot AI May 22, 2025

Copy link

Choose a reason for hiding this comment

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

This persists the entire line_items list object (including metadata like has_more) instead of just the items. Consider storing lineItems.data (e.g., line_items: lineItems?.data ?? null) to save only the actual line item array.

Suggested change
line_items: lineItems ? JSON.parse(JSON.stringify(lineItems)) : null,
line_items: lineItems?.data ?? null,

Copilot uses AI. Check for mistakes.
@DavidASix

Copy link
Copy Markdown
Owner Author

TLDR Jules is not very good and this PR sucked. Resolved this in #8

@DavidASix DavidASix closed this Jun 29, 2025
@DavidASix
DavidASix deleted the feature/stripe-payment-webhook branch July 27, 2025 21:17
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.

2 participants