ACG Docs
Pro & Payments

Webhooks

The Creem webhook handler in the buyer portal.

The portal's Creem webhook handler lives at app/api/payments/creem/webhook/route.ts. It is responsible for:

  1. Verifying the HMAC-SHA256 signature using CREEM_WEBHOOK_SECRET.
  2. Reading the event type (checkout.completed, subscription.paid, etc.).
  3. Routing by metadata.kind:
    • subscription → update the user's subscription and credit_ledger rows.
    • one_time (credit pack) → update the user's credits and credit_ledger rows.
    • pro → mark the pro_license row active and invite the user as a GitHub collaborator.

Signature verification

Creem signs each webhook with HMAC-SHA256 of the raw body using your CREEM_WEBHOOK_SECRET. The signature is in the creem-signature header. The handler uses a timing-safe comparison to prevent timing attacks.

Idempotency

The handler is idempotent on the event id and the provider_payment_id. Replaying the same webhook is safe.

Setting up a webhook in the Creem dashboard

  1. Sign in to Creem.
  2. Create a new product for AI Cost Gate Pro (or reuse the placeholder prod_pro_ai_cost_gate).
  3. Under "Webhooks", add an endpoint pointing to https://aicostgate.com/api/payments/creem/webhook.
  4. Copy the signing secret into CREEM_WEBHOOK_SECRET in your .env.local.

Local testing

You can replay a signed webhook with curl:

BODY='{"eventType":"checkout.completed","id":"evt_test","object":{...}}'
SIG=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$CREEM_WEBHOOK_SECRET" -hex | awk '{print $2}')
curl -X POST https://localhost:6080/api/payments/creem/webhook \
  -H "Content-Type: application/json" \
  -H "creem-signature: $SIG" \
  --data "$BODY"

On this page