Skip to content

Stripe And Webhook Cookbook

Use this page when moving from Stripe adapter wiring to production webhook behavior.

  • Stripe Provider is complete.
  • STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET are configured in the host environment.
  • The Stripe provider is registered through createMikaProviderRegistry([stripeProvider]).
  • The webhook route renders on demand with export const prerender = false.
  1. Install Stripe and create the SDK client in a server-only module.
  2. Pass that client and STRIPE_WEBHOOK_SECRET to createMikaStripeProvider().
  3. Register the provider through createMikaProviderRegistry([stripeProvider]).
  4. Copy or implement src/pages/api/mika-webhook/[provider].ts.
  5. Preserve the original Request so Mika can read the raw body for provider verification.
  6. Confirm checkout success by calling Mika checkout status; do not trust the success URL alone.
  7. Monitor webhook replay, failure, and deduplication records through host ops/admin surfaces.
src/pages/api/mika-webhook/[provider].ts
import { createMika } from "@bnomei/emdash-mika/astro";
import { createProviderName } from "@bnomei/emdash-mika/types";
import type { APIRoute } from "astro";
export const prerender = false;
export const POST: APIRoute = async ({ params, request, url }) => {
const provider = params.provider;
if (!provider) return new Response("Missing provider.", { status: 400 });
const Mika = createMika({ request, url }, { includeWebhook: true });
const result = await Mika.webhook.receive({
provider: createProviderName(provider),
});
return Response.json(result, { status: result.status });
};

The copied template endpoint adds diagnostics such as payload hash and signature-header presence, but verification still happens inside the provider adapter using the raw body from ctx.request.

createMikaStripeProvider() verifies webhooks when the host supplied:

  • a Stripe-shaped client with stripe.webhooks.constructEvent;
  • webhookSecret;
  • the original raw request body;
  • the incoming Stripe-Signature header.

The adapter returns a payloadHash as sha256:<hex> plus parsed provider event data. Mika’s backend uses provider evidence for deduplication and workflow handling. Stripe payment failures, expired checkout sessions, refunds, partial refunds, and uncollectible invoices are normalized into Mika payment events instead of being treated as unknown webhooks.

Checkout start should receive a host idempotency key when the caller may retry the same request. Mika stores checkout idempotency metadata and rejects same-key different-input replay.

The success URL is not proof of payment. The copied success page reads checkoutId and optional token, then calls Mika.checkout.status(...) and renders the provider-backed status.

The cancel page can call Mika checkout cancel for abandonment UX, but it is not proof that payment failed or never completed. Release expired stock reservations from maintenance.

  • Stripe sends Stripe-Signature to the webhook endpoint.
  • The endpoint passes the original Request to createMika({ request, url }, { includeWebhook: true }).
  • A valid webhook returns a Mika result envelope with the provider event normalized or queued.
  • Checkout success renders status from Mika.checkout.status(...), not from the return URL alone.
  • ../emdash-mika/src/stripe.ts
  • ../emdash-mika/src/provider.ts
  • ../emdash-mika/src/api/backend.ts
  • ../emdash-mika/src/templates/astro/pages/api/mika-webhook/[provider].ts
  • ../emdash-mika/src/templates/astro/pages/checkout/success.astro
  • ../emdash-mika/src/templates/astro/pages/checkout/cancel.astro