Webhooks
How Creem webhooks are processed.
Endpoint
POST /api/payments/creem/webhookSecurity
Signature Verification
Every webhook is verified using HMAC-SHA256:
const isValid = verifyWebhookSignature(body, signature, CREEM_WEBHOOK_SECRET);The signature is in the creem-signature header. Timing-safe comparison is used to prevent timing attacks.
Idempotency
Duplicate webhooks are handled by checking providerPaymentId in the payment table. If a payment with the same ID already exists, the webhook is acknowledged without re-processing.
Supported Events
| Event | Action |
|---|---|
checkout.completed | Create payment, grant credits, send email |
subscription.paid | Grant monthly credits, update subscription |
subscription.active | Activate subscription record |
Setup
In your Creem Dashboard, set the webhook URL to:
https://your-domain.com/api/payments/creem/webhookAnd configure the webhook secret in .env:
CREEM_WEBHOOK_SECRET="whsec_your_secret"Debugging
If webhooks aren't working:
- Check Creem Dashboard webhook logs for delivery status
- Verify
CREEM_WEBHOOK_SECRETmatches your Creem settings - Ensure the endpoint is publicly accessible (not behind auth)
- Check server logs for signature verification errors