# Webhooks are sent by Stripe, but you can test with Stripe CLI
stripe trigger checkout.session.completed
# Or forward webhooks to your local environment
stripe listen --forward-to localhost:3000/api/webhook
{
"received": true
}
Handle Stripe webhook events for payment and subscription lifecycle management
# Webhooks are sent by Stripe, but you can test with Stripe CLI
stripe trigger checkout.session.completed
# Or forward webhooks to your local environment
stripe listen --forward-to localhost:3000/api/webhook
{
"received": true
}
userId from client_reference_idplanId from session metadata"weekly", endsAt timestamp, stripeCustomerId, and stripeSubscriptionId"one-time" and stripeCustomerIdstripeSubscriptionIdendsAt date to the new current_period_end"weekly"stripeSubscriptionId"free" tierstripeSubscriptionId from user recordtrue to acknowledge receipt of the webhook event.{
"error": "Webhook Error: No signatures found matching the expected signature for payload"
}
{
subscription: {
tier: "weekly" | "one-time" | "free",
endsAt: Date | null,
stripeCustomerId: string | null,
stripeSubscriptionId: string | null,
updatedAt: serverTimestamp()
}
}
# Webhooks are sent by Stripe, but you can test with Stripe CLI
stripe trigger checkout.session.completed
# Or forward webhooks to your local environment
stripe listen --forward-to localhost:3000/api/webhook
{
"received": true
}
stripe.webhooks.constructEvent() with the STRIPE_WEBHOOK_SECRETSTRIPE_WEBHOOK_SECRET from your Stripe DashboardSTRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
https://your-domain.com/api/webhookcheckout.session.completedinvoice.payment_succeededcustomer.subscription.deletedSTRIPE_WEBHOOK_SECRET2023-10-16stripeSubscriptionId to find associated usersserverTimestamp() for accurate updatedAt trackingcheckout.session.completed webhooktier: "weekly", expiration date, and Stripe IDsinvoice.payment_succeeded webhookcustomer.subscription.deleted webhooktier: "free"checkout.session.completed webhooktier: "one-time" and customer IDSTRIPE_WEBHOOK_SECRET matches the dashboard valuestripe-signature header is being forwarded correctlyuserId is correctly set as client_reference_id during checkout"users"