Skip to main content

Webhook API Endpoints

The template supports payment webhook handlers for four providers: Stripe, LemonSqueezy, Polar, and Solidgate. Each webhook endpoint processes incoming events from its respective payment provider, handling subscription lifecycle management, payment notifications, and email delivery. All endpoints verify request signatures for security.

Overview

EndpointProviderSignature HeaderDescription
/api/stripe/webhookStripestripe-signatureProcess Stripe payment and subscription events
/api/lemonsqueezy/webhookLemonSqueezyx-signatureProcess LemonSqueezy payment events
/api/polar/webhookPolarwebhook-signatureProcess Polar payment events
/api/solidgate/webhookSolidgatex-signatureProcess Solidgate payment events

All webhook endpoints accept only POST requests and return {"received": true} on success.

Shared Architecture

All four webhook handlers follow the same general pattern:

  1. Read the raw request body as text (needed for signature verification)
  2. Extract the signature from provider-specific headers
  3. Pass the body and signature to the provider's handleWebhook() method for verification and parsing
  4. Route the parsed event to the appropriate handler based on WebhookEventType
  5. Execute business logic (database updates, email notifications)
  6. Return {"received": true} to acknowledge the webhook

Common Event Types

The WebhookEventType enum from lib/payment/types/payment-types standardizes events across providers:

Event TypeDescription
SUBSCRIPTION_CREATEDNew subscription activated
SUBSCRIPTION_UPDATEDSubscription plan or details changed
SUBSCRIPTION_CANCELLEDSubscription cancelled
PAYMENT_SUCCEEDEDOne-time payment completed
PAYMENT_FAILEDPayment attempt failed
SUBSCRIPTION_PAYMENT_SUCCEEDEDRecurring subscription payment completed
SUBSCRIPTION_PAYMENT_FAILEDRecurring subscription payment failed
SUBSCRIPTION_TRIAL_ENDINGTrial period about to expire
REFUND_SUCCEEDEDRefund processed
BILLING_PORTAL_SESSION_UPDATEDBilling portal session changed (Stripe only)

Stripe Webhook

POST /api/stripe/webhook

Processes Stripe webhook events with signature verification via the stripe-signature header. This is the most feature-complete webhook handler, including email notifications for all event types and sponsor ad subscription handling.

Required Header:

HeaderDescription
stripe-signatureStripe webhook signature (t=...,v1=... format)

Supported Events:

Stripe EventMapped TypeActions
customer.subscription.createdSUBSCRIPTION_CREATEDDatabase update, welcome email
customer.subscription.updatedSUBSCRIPTION_UPDATEDDatabase update, update email
customer.subscription.deletedSUBSCRIPTION_CANCELLEDDatabase update, cancellation email
invoice.payment_succeededSUBSCRIPTION_PAYMENT_SUCCEEDEDDatabase update, receipt email
invoice.payment_failedSUBSCRIPTION_PAYMENT_FAILEDDatabase update, retry email
payment_intent.succeededPAYMENT_SUCCEEDEDConfirmation email
payment_intent.payment_failedPAYMENT_FAILEDFailure notification email
customer.subscription.trial_will_endSUBSCRIPTION_TRIAL_ENDINGTrial ending email
billing_portal.session.updatedBILLING_PORTAL_SESSION_UPDATEDLogging only

Sponsor Ad Handling:

Stripe webhooks detect sponsor ad subscriptions via metadata.type === "sponsor_ad" in the subscription data. When detected, dedicated handlers activate, cancel, or renew sponsor ads instead of processing regular subscriptions.

Error Responses:

StatusCondition
400Missing stripe-signature header
400Webhook not processed (invalid signature)
400Webhook processing failed

Source: template/app/api/stripe/webhook/route.ts

LemonSqueezy Webhook

POST /api/lemonsqueezy/webhook

Processes LemonSqueezy webhook events with signature verification via the x-signature header. Uses an event mapping function to translate LemonSqueezy-specific event names to the generic WebhookEventType.

Required Header:

HeaderDescription
x-signatureLemonSqueezy webhook signature

Event Mapping:

LemonSqueezy EventMapped Type
subscription_createdSUBSCRIPTION_CREATED
subscription_updatedSUBSCRIPTION_UPDATED
subscription_cancelledSUBSCRIPTION_CANCELLED
subscription_payment_successSUBSCRIPTION_PAYMENT_SUCCEEDED
subscription_payment_failedSUBSCRIPTION_PAYMENT_FAILED
subscription_trial_will_endSUBSCRIPTION_TRIAL_ENDING
order_createdPAYMENT_SUCCEEDED
order_refundedREFUND_SUCCEEDED

Sponsor Ad Handling:

LemonSqueezy uses custom_data.type === "sponsor_ad" or meta.custom_data.type === "sponsor_ad" to identify sponsor ad subscriptions.

Source: template/app/api/lemonsqueezy/webhook/route.ts

Polar Webhook

POST /api/polar/webhook

Processes Polar webhook events with multi-header signature verification. Polar uses three headers for security verification and delegates event routing to a separate router module.

Required Headers:

HeaderDescription
webhook-signatureHMAC SHA256 signature (v1,<hex_signature> format)
webhook-timestampUnix timestamp of the webhook event
webhook-idUnique identifier for the webhook delivery

Supported Events:

Polar EventDescription
checkout.succeededCheckout completed
checkout.failedCheckout failed
subscription.createdSubscription created
subscription.updatedSubscription updated
subscription.canceledSubscription cancelled
invoice.paidInvoice payment completed
invoice.payment_failedInvoice payment failed

Processing:

Unlike the other providers, Polar's webhook handler uses a separate routeWebhookEvent() function from a router module and a validateWebhookPayload() utility for payload structure validation before signature verification.

Source: template/app/api/polar/webhook/route.ts

Solidgate Webhook

POST /api/solidgate/webhook

Processes Solidgate webhook events with signature verification. Includes in-memory idempotency protection to prevent duplicate processing of the same webhook event.

Required Header:

HeaderDescription
x-signature or solidgate-signatureSolidgate webhook signature

Idempotency:

The handler maintains an in-memory Set of processed webhook IDs. Duplicate webhooks return {"received": true} without reprocessing. Webhook IDs expire from the cache after 24 hours.

Note: The in-memory idempotency cache does not persist across serverless function invocations. In production serverless environments, this should be replaced with Redis or a database-backed solution.

Supported Events:

The handler accepts both the generic WebhookEventType constants and string-based event names (e.g., both WebhookEventType.PAYMENT_SUCCEEDED and "payment_succeeded").

EventActions
payment_succeededRecord payment
payment_failedRecord failure
subscription_createdCreate subscription
subscription_updatedUpdate subscription
subscription_cancelledCancel subscription
subscription_payment_succeededRecord subscription payment
subscription_payment_failedRecord subscription payment failure
subscription_trial_endingHandle trial ending
refund_processedLog refund

GET Endpoint:

Solidgate also exposes a GET handler that returns an informational message about the webhook endpoint:

{
"message": "Solidgate webhook endpoint",
"instructions": "This endpoint accepts POST requests from Solidgate webhooks",
"method": "POST"
}

Source: template/app/api/solidgate/webhook/route.ts

Email Notifications

The Stripe webhook handler sends the most comprehensive email notifications. All providers delegate to the WebhookSubscriptionService for database operations, but email templates vary by provider.

Email TypeTrigger
Welcome / New SubscriptionSubscription created
Subscription UpdateSubscription plan changed
Cancellation ConfirmationSubscription cancelled
Payment ReceiptSubscription or one-time payment succeeded
Payment Failed / RetryPayment attempt failed
Trial EndingTrial period about to expire

Email configuration is loaded from lib/config/server-config via getEmailConfig() and includes company name, company URL, and support email address.

Key Implementation Details

  • Signature Verification: All providers verify webhook signatures before processing events. Invalid signatures result in a 400 response.
  • Raw Body Parsing: Webhooks read the request body as text using request.text() rather than request.json() because signature verification requires the raw, unmodified payload.
  • WebhookSubscriptionService: The shared WebhookSubscriptionService class handles database operations for subscription lifecycle events across all providers.
  • Sponsor Ad Detection: Stripe and LemonSqueezy webhooks detect sponsor ad subscriptions via metadata and route them to separate handlers for ad activation, cancellation, and renewal.
  • Graceful Error Handling: Email send failures are caught and logged but do not cause the webhook to return an error. The webhook always acknowledges receipt to prevent provider retries.