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
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.
Related
- Row Level Security (RLS) explained with examples
- Add Google and GitHub sign-in with supabase-js
- Passwordless magic link login
- Semantic search with pgvector
- Quickstart
- supabase-js client docs
Updated 2026-10-23.