Terima webhook WhatsApp dalam Supabase Edge Function
Supabase Edge Function boleh menjadi destinasi webhook nombor anda secara terus. Ia menjawab GET pengesahan Meta dengan nilai hub.challenge, memasukkan setiap mesej POST ke dalam jadual Postgres, dan membalas melalui Cloud API. Deploy dengan pengesahan JWT dimatikan, kerana Supabase memerlukan JWT yang sah secara lalai dan Meta tidak dapat menghantarnya.
Tetapan lalai Supabase untuk Edge Function, yang menolak sebarang permintaan tanpa JWT yang sah sebelum pengendali anda berjalan.
Bendera supabase functions deploy yang mematikan pengesahan JWT bagi sesuatu fungsi.
Had masa melahu permintaan untuk Edge Function; jika tiada respons pada masa itu, 504 dikembalikan.
Apakah yang perlu dilakukan oleh Edge Function?
Dua perkara, pada dua kaedah.
Pada GET, ia menjawab pengesahan Meta. Baca hub.mode, hub.verify_token dan hub.challenge daripada query string, bandingkan token dengan verify token yang anda masukkan untuk destinasi itu, dan kembalikan challenge sebagai badan teks biasa dengan status 200. Kembalikan 403 jika token tidak sepadan.
Pada POST, ia menerima peristiwa. Hurai badan permintaan, ambil mesej daripada entry, changes dan value, masukkan ke dalam jadual, dan kembalikan 200. Pastikan respons pantas: Meta menganggap jawapan yang lambat atau gagal sebagai penghantaran yang gagal lalu mencuba semula, yang bermakna mesej yang sama tiba dua kali.
Tetapkan URL fungsi sebagai destinasi webhook nombor itu dalam EasyCoexistence, dengan verify token yang sama, dan Meta akan mula menghantar.
import { createClient } from "npm:@supabase/supabase-js@2";
const db = createClient(Deno.env.get("SUPABASE_URL")!, Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!);
Deno.serve(async (req) => {
const url = new URL(req.url);
if (req.method === "GET") {
const ok = url.searchParams.get("hub.verify_token") === Deno.env.get("VERIFY_TOKEN");
return new Response(ok ? url.searchParams.get("hub.challenge") : "forbidden", { status: ok ? 200 : 403 });
}
if (url.searchParams.get("key") !== Deno.env.get("URL_SECRET")) return new Response("no", { status: 401 });
const body = await req.json();
const rows = (body.entry ?? []).flatMap((e: any) => e.changes ?? [])
.flatMap((c: any) => (c.value?.messages ?? []).map((m: any) => ({
wamid: m.id, sender: m.from, kind: m.type, text: m.text?.body ?? null,
phone_number_id: c.value.metadata?.phone_number_id, raw: m,
})));
if (rows.length) await db.from("whatsapp_messages").upsert(rows, { onConflict: "wamid" });
return new Response("ok");
});Mengapa fungsi memulangkan 401 kepada Meta?
Kerana pengesahan JWT dihidupkan. Supabase mengesahkan JWT dalam pengepala Authorization sebelum pengendali Edge Function berjalan, dan itu ialah tetapan lalai bagi setiap fungsi. Meta tidak menghantar pengepala sedemikian, jadi platform menolak GET pengesahan dan setiap penghantaran, dan kod anda tidak pernah melihatnya.
Matikannya untuk fungsi ini sahaja. Sama ada deploy dengan supabase functions deploy whatsapp --no-verify-jwt, atau tetapkan verify_jwt = false di bawah bahagian fungsi itu dalam supabase/config.toml. Dokumentasi Supabase menerangkan kes ini dengan tepat, iaitu endpoint awam yang dipanggil oleh penyedia webhook luar.
Apabila semakan platform dimatikan, fungsi memerlukan semakan sendiri. Pengepala tandatangan tidak tersedia kepada anda, kerana Meta menandatangani dengan app secret bagi aplikasi yang memiliki langganan itu. Letakkan rahsia rawak yang panjang dalam URL destinasi sebagai parameter query dan tolak permintaan tanpanya, seperti yang dilakukan oleh contoh pada POST.
Bagaimanakah fungsi patut menyimpan dan membalas?
Simpan dahulu, balas kemudian. Lakukan upsert pada id mesej WhatsApp dengan kekangan unik, supaya percubaan semula Meta masuk ke baris yang sama dan tidak mencipta pendua. Simpan phone_number_id penerima dalam setiap baris jika lebih daripada satu nombor menuju ke fungsi itu.
Untuk kerja yang lambat, seperti memanggil model atau API lain sebelum membalas, serahkan kerja itu kepada EdgeRuntime.waitUntil dan kembalikan respons serta-merta. Fungsi terus berjalan sehingga promise itu selesai, sementara Meta sudah menerima 200 nya. Supabase berhenti menunggu pada 150 saat masa melahu dan memulangkan 504, jadi pengendali yang menyiapkan semua kerja sebelum memberi respons paling berkemungkinan menyebabkan percubaan semula.
Balasan dihantar ke endpoint mesej Cloud API dengan phone number id dan token nombor itu, yang disimpan sebagai rahsia fungsi. Dalam tempoh 24 jam selepas mesej terakhir pelanggan, teks bebas boleh dihantar; di luar tempoh itu hanya template yang diluluskan boleh digunakan.
Adakah mesej yang dihantar dari telefon sampai ke fungsi?
Ya. Pada nombor Coexistence, aplikasi WhatsApp Business terus berfungsi, dan apabila seseorang membalas daripadanya, Meta menghantar gema mesej keluar itu ke destinasi yang sama, di bawah medan smb_message_echoes dan tatasusunan message_echoes dan bukannya messages.
Panjangkan pengurai untuk membaca kedua-duanya dan simpan lajur direction. Jadual itu kemudian memegang perbualan penuh, baris pelanggan dan jawapan perniagaan, iaitu yang paling kerap ingin dikueri selepas itu: masa respons, perbualan yang belum dijawab, masa masuk terakhir bagi setiap kenalan.
Nilai terakhir itu juga ialah input yang diperlukan oleh setiap laluan penghantaran, kerana ia menentukan sama ada balasan boleh berupa teks bebas atau mesti menggunakan template. Sebuah view pada jadual boleh mendedahkannya bagi setiap kenalan kepada seluruh aplikasi anda.
Kesilapan biasa
- Deploy dengan pengesahan JWT dibiarkan hidup. Supabase menolak permintaan Meta dengan 401 sebelum pengendali berjalan, dan tiada mesej pernah tiba.
- Memulangkan challenge yang dibalut dalam JSON. Meta memerlukan nilai mentah sebagai badan, atau pengesahan gagal secara senyap.
- Melakukan kerja yang lambat sebelum memberi respons. Meta mencuba semula apa yang dilihatnya sebagai kegagalan, dan mesej yang sama diproses dua kali.
- Mematikan pengesahan JWT tanpa sebarang semakan sendiri. Sesiapa yang menjumpai URL itu boleh menghantar mesej palsu ke dalam jadual.
Sambungkan nombor anda di easycoexistence.com, tetapkan destinasi webhook kepada URL Edge Function dengan verify token anda, dan simpan phone number id serta token sebagai rahsia fungsi. Daripada US$ 9 sebulan bagi setiap nombor, jatuh kepada US$ 1 apabila nombor bertambah, dengan 3 hari pertama percuma.
Soalan lazim
Adakah ini berfungsi pada pelan percuma Supabase?
Edge Function berjalan pada pelan percuma, dengan had masa jam dinding 150 saat berbanding 400 saat pada pelan berbayar. Pengendali webhook yang memberi respons dengan pantas kekal jauh di dalam kedua-dua had itu.
Patutkah saya menggunakan service role key dalam fungsi?
Hanya di sebelah pelayan, seperti di sini, dibaca daripada persekitaran fungsi. Ia memintas row level security, jadi ia tidak boleh sekali-kali sampai ke pelayar.
Bolehkah satu fungsi melayani beberapa nombor?
Boleh. Halakan destinasi webhook setiap nombor kepadanya dan cabangkan berdasarkan phone_number_id dalam setiap payload.
Adakah EasyCoexistence melihat mesej itu?
Tidak. Meta menghantar setiap webhook terus ke fungsi anda, dan EasyCoexistence tidak pernah menyimpan kandungan perbualan.
Teruskan membaca
Sedia untuk bermula?
Sediakan WhatsApp Coexistence dalam beberapa minit, bukan berbulan-bulan. Aplikasi terus berfungsi pada telefon.
Mulakan Percubaan PercumaDisahkan pada