Skip to main content
The Payment Webhooks endpoint processes events from payment providers (Stripe, Polar, Lemon Squeezy) to keep your database synchronized with payment status changes.

Webhook Endpoint

POST /api/webhooks/payments Receives and processes webhook events from payment providers.

Implementation Overview

From src/app/api/webhooks/payments/route.ts:10-225, the webhook handler:
  1. Verifies webhook signature based on active provider
  2. Parses the event into a standardized format
  3. Processes the event via payment adapter
  4. Updates database with customer, subscription, and payment data

Webhook Flow

Signature Verification

All webhook requests are verified using provider-specific signatures to ensure authenticity.

Signature Headers by Provider

From src/app/api/webhooks/payments/route.ts:22-42.

Supported Events

Customer Events

event
New customer createdUpdates: Creates new record in customer table
event
Customer information updatedUpdates: Updates email and metadata in customer table
event
Customer deletedUpdates: Can cascade delete related records

Subscription Events

event
New subscription createdUpdates: Creates record in subscription table with status active or trialing
event
Subscription modified (plan change, status change, etc.)Updates: Updates subscription status, plan, billing period, amounts
event
Subscription canceledUpdates: Sets status to canceled, records canceledAt timestamp
event
Subscription permanently deletedUpdates: Marks subscription as deleted

Payment Events

event
Payment successfulUpdates: Creates payment record with status succeeded
event
Payment failedUpdates: Creates or updates payment record with status failed, may update subscription to past_due
event
Checkout session completedUpdates: Creates customer, subscription, and initial payment records
event
One-time payment completed (Lemon Squeezy)Updates: Creates payment record for one-time purchase
From src/lib/payments/types.ts:109-120.

Database Updates

Customer Table

From src/app/api/webhooks/payments/route.ts:96-119:

Subscription Table

From src/app/api/webhooks/payments/route.ts:122-181:

Payment Table

From src/app/api/webhooks/payments/route.ts:185-217:

Webhook Event Structure

From src/lib/payments/types.ts:125-130:

Provider-Specific Parsing

From src/app/api/webhooks/payments/route.ts:56-80:

Setup Instructions

1

Configure Webhook URL

Add webhook endpoint to your payment provider:
2

Set Webhook Secret

Add the webhook signing secret to your environment variables:
3

Select Events

Configure which events to receive (recommended):
  • customer.created
  • customer.updated
  • subscription.created
  • subscription.updated
  • subscription.deleted
  • invoice.payment_succeeded
  • invoice.payment_failed
  • checkout.session.completed
4

Test Webhook

Use provider’s test mode to verify webhook is working:

Response Format

Success Response

Status: 200 OK

Error Responses

Bad Request
Missing or invalid signature
Internal Server Error
Webhook processing error

Idempotency

The webhook handler uses providerCustomerId, providerSubscriptionId, and providerPaymentId to ensure idempotency. Duplicate events are handled gracefully by updating existing records.
From src/app/api/webhooks/payments/route.ts:97-99, 123-125, 186-188:

Debugging

Webhook errors are logged to console:
From src/app/api/webhooks/payments/route.ts:89, 222.
Enable detailed logging in development to troubleshoot webhook issues. Check your payment provider’s dashboard for webhook delivery status and retry attempts.

Database Schema

View customer, subscription, and payment tables

Checkout API

Create checkout sessions

Payment Types

TypeScript type definitions