Skip to main content
Watch: Claude Code Just Killed After Effects

Billing

Autumn Credit Billing

Use when adding Autumn, a billing layer in front of Stripe, to sell a credit balance shared across apps. Covers using the Clerk userId as the Autumn customer id, hosted checkout, verifying Svix-signed webhooks, and granting credits only after re-verifying the purchase. Also use when a webhook grants credits it should not, or a purchase completes without the balance moving. If money buys credits, use this skill.

autumnstripecreditswebhooksbilling

1 file

Description

Use when adding Autumn, a billing layer in front of Stripe, to sell a credit balance shared across apps. Covers using the Clerk userId as the Autumn customer id, hosted checkout, verifying Svix-signed webhooks, and granting credits only after re-verifying the purchase. Also use when a webhook grants credits it should not, or a purchase completes without the balance moving. If money buys credits, use this skill.

Install
SKILL.md

name: autumn-credit-billing description: Use when adding Autumn (a billing layer in front of Stripe) to sell a universal credit balance shared across apps. Covers using the Clerk userId as the Autumn customer id, hosted checkout, verifying Svix-signed webhooks, and granting credits only after re-verifying the purchase.

Autumn Credit Billing

When to trigger

Selling credits (not per-app subscriptions) where one purchase should spend across every app you run. Autumn owns the purchase side; your own store owns the spendable balance.

Split the responsibilities

  • Autumn + Stripe: checkout, payment, the record that a customer bought a product.
  • Your database (for example Neon): the spendable balance. Autumn never mutates it.
  • The webhook route composes the two: verify the event, then grant credits locally.

Use the Clerk userId as the customer id

Set the Autumn customer id to the Clerk userId. Because that id is stable across every app, a customer buys once and spends the same balance everywhere. Do not mint per-app customer records.

// createCheckout: Clerk userId + product id -> hosted Stripe URL
const res = await autumnFetch("/checkout", {
  method: "POST",
  body: JSON.stringify({ customer_id: clerkUserId, product_id: productId, success_url, cancel_url }),
});

Verify the webhook, then re-confirm before granting

Autumn signs deliveries with Svix (svix-id, svix-timestamp, svix-signature). Verify the signature first. Then re-fetch the customer from Autumn to confirm they actually own the product before you grant credits. Never grant on the webhook payload alone; a forged or replayed body must not mint credits.

if (!verifyAutumnWebhook(headers, rawBody)) return new Response("bad sig", { status: 400 });
if (await customerHasProduct(customerId, productId)) await grant(customerId, credits);

Pitfalls

  • Granting straight from the webhook body without re-verifying against Autumn trusts an unauthenticated payload. Confirm against the source of truth.
  • Verifying the signature over a parsed/re-serialized body fails; Svix signs the raw bytes. Read the raw request body for verification.
  • Creating a separate customer per app breaks the "buy once, spend everywhere" model. Key every app on the same Clerk userId.
  • Reading AUTUMN_SECRET_KEY at module load can crash a build without the env. Read it at call time and never log it.
AI Development Stack

Get Smarter About AI Dev

New tutorials, open-source projects, and deep dives on coding agents - delivered weekly.

One email per weekReal code, not theoryFree forever