Primanje WhatsApp webhooka u funkciji Supabase Edge Function
Supabase Edge Function može izravno biti odredište webhooka za broj. Na Metin GET zahtjev za provjeru odgovara vrijednošću hub.challenge, svaku primljenu POST poruku upisuje u Postgres tablicu, a odgovore šalje putem Cloud API-ja. Postavite je s isključenom JWT provjerom, jer Supabase prema zadanim postavkama traži valjan JWT, a Meta ga ne može poslati.
Zadana postavka za Supabase Edge Functions: odbija svaki zahtjev bez valjanog JWT-a prije nego što se pokrene vaš kôd.
Oznaka naredbe supabase functions deploy koja isključuje JWT provjeru za pojedinu funkciju.
Ograničenje mirovanja zahtjeva za Edge Function; ako do tada nema odgovora, vraća se 504.
Što Edge Function mora napraviti?
Dvije stvari, na dvije metode.
Na GET odgovara na Metinu provjeru. Iz query stringa pročitajte hub.mode, hub.verify_token i hub.challenge, usporedite token s onim koji ste unijeli kao verify token odredišta i vratite challenge kao običan tekst sa statusom 200. Ako se token ne podudara, vratite 403.
Na POST prima događaje. Parsirajte tijelo zahtjeva, izvucite poruke iz entry, changes i value, upišite ih u tablicu i vratite 200. Odgovor neka bude brz: Meta sporu ili neuspjelu isporuku smatra neuspjelom i pokušava ponovno, što znači da ista poruka stigne dvaput.
URL funkcije postavite u EasyCoexistence kao odredište webhooka broja, s istim verify tokenom, i Meta počinje isporučivati.
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");
});Zašto funkcija vraća 401 Meti?
Zato što je JWT provjera uključena. Supabase provjerava JWT u zaglavlju Authorization prije nego što se pokrene kôd Edge Function, a to je zadano za svaku funkciju. Meta takvo zaglavlje ne šalje, pa platforma odbija i GET za provjeru i svaku isporuku, a vaš kôd ih nikad ne vidi.
Isključite je samo za ovu funkciju. Postavite je naredbom supabase functions deploy whatsapp --no-verify-jwt ili upišite verify_jwt = false u odjeljak funkcije u supabase/config.toml. Supabaseova dokumentacija opisuje upravo ovaj slučaj: javni endpoint koji poziva vanjski pružatelj webhooka.
Kad je provjera platforme isključena, funkcija treba vlastitu. Zaglavlje s potpisom vam nije dostupno, jer Meta potpisuje tajnom aplikacije koja posjeduje pretplatu. Stavite dugu nasumičnu tajnu u URL odredišta kao query parametar i odbijte zahtjeve bez nje, kao što primjer radi na POST.
Kako funkcija treba spremati i odgovarati?
Prvo spremite, odgovorite poslije. Radite upsert po ID-ju WhatsApp poruke uz unique constraint, tako da Metini ponovni pokušaji završe u istom retku umjesto da stvore duplikat. U svaki redak spremite phone_number_id primatelja ako na funkciju pokazuje više brojeva.
Za sve što je sporo, primjerice poziv modela ili drugog API-ja prije odgovora, prepustite posao funkciji EdgeRuntime.waitUntil i odmah vratite odgovor. Funkcija radi dok se taj promise ne razriješi, a Meta već ima svoj 200. Supabase prestaje čekati nakon 150 sekundi mirovanja i vraća 504, pa je kôd koji sav posao obavi prije odgovora najčešći uzrok ponovnih pokušaja.
Odgovori idu na messages endpoint Cloud API-ja s ID-jem telefonskog broja i tokenom, pohranjenima kao tajne funkcije. Unutar 24 sata od posljednje poruke kupca isporučuje se slobodan tekst; izvan tog razdoblja samo odobren predložak.
Stižu li poruke poslane s telefona do funkcije?
Da. Na Coexistence broju aplikacija WhatsApp Business i dalje radi, a kad netko odgovori iz nje, Meta na isto odredište isporučuje odjek te odlazne poruke, u polju smb_message_echoes i nizu message_echoes umjesto messages.
Proširite parser da čita oboje i spremajte stupac direction. Tablica tada sadrži cijeli razgovor, retke kupca i odgovore tvrtke, što većina ljudi poslije želi upitima dobiti: vremena odgovora, neodgovorene razgovore, vrijeme zadnje dolazne poruke po kontaktu.
Ta posljednja vrijednost je i ulaz koji treba svaki put slanja, jer odlučuje može li odgovor biti slobodan tekst ili mora biti predložak. View nad tablicom može je izložiti po kontaktu ostatku vaše aplikacije.
Uobičajene pogreške
- Postavljanje s uključenom JWT provjerom. Supabase odbija Metine zahtjeve s 401 prije nego što se pokrene kôd i nijedna poruka ne stiže.
- Vraćanje challengea umotanog u JSON. Meta traži sirovu vrijednost kao tijelo odgovora, inače provjera tiho ne uspije.
- Obavljanje sporog posla prije odgovora. Meta ponavlja ono što vidi kao neuspjeh, pa se ista poruka obradi dvaput.
- Isključivanje JWT provjere bez ikakve vlastite provjere. Svatko tko pronađe URL može upisati lažne poruke u tablicu.
Povežite broj na easycoexistence.com, postavite URL Edge Function kao odredište webhooka s vašim verify tokenom, a ID telefonskog broja i token pohranite kao tajne funkcije. Od US$ 9 po povezanom broju mjesečno, do US$ 1 kako dodajete brojeve, a prva 3 dana su besplatna.
Često postavljana pitanja
Radi li ovo na besplatnom Supabase paketu?
Edge Functions rade na besplatnom paketu, uz ograničenje od 150 sekundi stvarnog vremena, naspram 400 sekundi na plaćenim paketima. Webhook funkcija koja brzo odgovara ostaje daleko unutar oba.
Trebam li u funkciji koristiti ključ service role?
Samo na poslužitelju, kao ovdje, pročitan iz okruženja funkcije. On zaobilazi sigurnost na razini redaka, pa nikad ne smije dospjeti u preglednik.
Može li jedna funkcija služiti više brojeva?
Da. Usmjerite odredište webhooka svakog broja na nju i grananje radite po phone_number_id u svakom payloadu.
Vidi li EasyCoexistence poruke?
Ne. Meta svaki webhook isporučuje izravno vašoj funkciji, a EasyCoexistence nikad ne sprema sadržaj razgovora.
Nastavite čitati
Spremni za početak?
Postavite WhatsApp Coexistence u nekoliko minuta, a ne mjeseci. Aplikacija i dalje radi na telefonu.
Započnite besplatno probno razdobljeProvjereno na