How-to guides

Handle Stripe webhooks with a Deno edge function

Webhooks need a public endpoint and a secret. Edge functions give you both, running on your project’s server.

Add secrets

In Functions → Secrets, add STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, PROJECT_URL and SERVICE_KEY. Names starting with SUPABASE_ are reserved.

Write the function

stripe-webhook/index.ts
import Stripe from 'npm:stripe'
import { createClient } from 'npm:@supabase/supabase-js@2'
const stripe = new Stripe(Deno.env.get('STRIPE_SECRET_KEY')!)
const admin = createClient(Deno.env.get('PROJECT_URL')!, Deno.env.get('SERVICE_KEY')!)

Deno.serve(async (req) => {
  const body = await req.text()
  const event = await stripe.webhooks.constructEventAsync(
    body, req.headers.get('stripe-signature')!, Deno.env.get('STRIPE_WEBHOOK_SECRET')!)
  if (event.type === 'checkout.session.completed') {
    await admin.from('orders').update({ paid: true })
      .eq('checkout_id', event.data.object.id)
  }
  return new Response('ok')
})

Make it public

Turn off Require a valid JWT for this function: Stripe doesn’t send your anon key. The signature check above is what authenticates the request.

Point Stripe at it

In Stripe’s dashboard, add the endpoint https://<your-project-url>/functions/v1/stripe-webhook.

Questions

Can I read a secret back later?

No. Secret values are write-only; set them again to change them.

Updated 2026-10-23.