Conectați-văÎncepeți perioada de probă gratuită
Meta Tech Provider

Primiți webhook-uri WhatsApp într-o funcție Edge Supabase

O funcție Edge Supabase poate fi direct destinația webhook-ului numărului. Ea răspunde la GET-ul de verificare al Meta cu valoarea hub.challenge, inserează fiecare mesaj primit prin POST într-un tabel Postgres și răspunde prin Cloud API. Implementați-o cu verificarea JWT dezactivată, deoarece Supabase cere implicit un JWT valid, iar Meta nu poate trimite unul.

verify_jwt = true

Setarea implicită Supabase pentru Edge Functions, care respinge orice cerere fără un JWT valid înainte ca handlerul dumneavoastră să ruleze.

--no-verify-jwt

Opțiunea supabase functions deploy care dezactivează verificarea JWT pentru o funcție.

150 s

Timpul de inactivitate al cererii pentru o Edge Function; dacă nu există răspuns până atunci, se returnează 504.

Ce trebuie să facă funcția Edge?

Două lucruri, pe două metode.

Pe GET, răspunde la verificarea Meta. Citiți hub.mode, hub.verify_token și hub.challenge din query string, comparați token-ul cu cel introdus ca verify token al destinației și returnați challenge-ul ca text simplu în corp, cu statusul 200. Returnați 403 dacă token-ul nu se potrivește.

Pe POST, primește evenimentele. Parsați corpul, extrageți mesajele din entry, changes și value, inserați-le într-un tabel și returnați 200. Păstrați răspunsul rapid: Meta consideră un răspuns lent sau eșuat drept o livrare eșuată și reîncearcă, ceea ce înseamnă același mesaj de două ori.

Setați URL-ul funcției ca destinație webhook a numărului în EasyCoexistence, cu același verify token, iar Meta începe să livreze.

supabase/functions/whatsapp/index.ts
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");
});

De ce returnează funcția 401 către Meta?

Pentru că verificarea JWT este activă. Supabase validează un JWT din antetul Authorization înainte ca handlerul unei funcții Edge să ruleze, iar aceasta este setarea implicită pentru orice funcție. Meta nu trimite un astfel de antet, așa că platforma respinge atât GET-ul de verificare, cât și fiecare livrare, iar codul dumneavoastră nu le vede niciodată.

Dezactivați verificarea doar pentru această funcție. Fie implementați cu supabase functions deploy whatsapp --no-verify-jwt, fie setați verify_jwt = false în secțiunea funcției din supabase/config.toml. Documentația Supabase descrie exact acest caz: un endpoint public apelat de un furnizor extern de webhook-uri.

Cu verificarea platformei oprită, funcția are nevoie de propria verificare. Antetul de semnătură nu vă este disponibil, deoarece Meta semnează cu secretul aplicației care deține abonamentul. Puneți un secret lung și aleatoriu în URL-ul destinației ca parametru de query și respingeți cererile fără el, așa cum face exemplul la POST.

Cum ar trebui să stocheze și să răspundă funcția?

Stocați întâi, răspundeți după. Faceți upsert pe id-ul mesajului WhatsApp, cu o constrângere de unicitate, astfel încât reîncercările Meta să ajungă pe același rând, nu să creeze un duplicat. Păstrați phone_number_id-ul care a primit mesajul în fiecare rând, dacă mai multe numere trimit către funcție.

Pentru orice este lent, de exemplu apelarea unui model sau a altui API înainte de răspuns, predați lucrarea lui EdgeRuntime.waitUntil și returnați răspunsul imediat. Funcția continuă să ruleze până când promisiunea se încheie, în timp ce Meta are deja 200. Supabase nu mai așteaptă după 150 de secunde de inactivitate și returnează 504, așa că un handler care își face toată munca înainte de a răspunde este cel mai probabil să provoace reîncercări.

Răspunsurile merg către endpoint-ul de mesaje din Cloud API, cu phone number id-ul și token-ul numărului, păstrate ca secrete ale funcției. În 24 de ore de la ultimul mesaj al clientului, textul liber se livrează; în afara intervalului, doar un template aprobat.

Ajung în funcție mesajele trimise de pe telefon?

Da. Pe un număr Coexistence, aplicația WhatsApp Business continuă să funcționeze, iar când cineva răspunde din ea, Meta livrează un echo al mesajului trimis către aceeași destinație, în câmpul smb_message_echoes și într-un array message_echoes, nu în messages.

Extindeți parserul să le citească pe ambele și stocați o coloană direction. Tabelul conține astfel conversația completă, replicile clientului și răspunsurile firmei, ceea ce vor să interogheze cei mai mulți ulterior: timpii de răspuns, conversațiile rămase fără răspuns, momentul ultimului mesaj primit pentru fiecare contact.

Această ultimă valoare este și datul de care are nevoie orice cale de trimitere, fiindcă decide dacă un răspuns poate fi text liber sau trebuie să fie un template. O vizualizare peste tabel o poate expune pentru fiecare contact în restul aplicației dumneavoastră.

Greșeli frecvente

  • Implementarea cu verificarea JWT lăsată activă. Supabase respinge cererile Meta cu 401 înainte ca handlerul să ruleze, iar niciun mesaj nu ajunge vreodată.
  • Returnarea challenge-ului încadrat în JSON. Meta are nevoie de valoarea brută caTRUP al răspunsului, altfel verificarea eșuează fără niciun semnal.
  • Efectuarea lucrărilor lente înainte de răspuns. Meta reîncearcă ce vede ca eșec, iar același mesaj este procesat de două ori.
  • Dezactivarea verificării JWT fără nicio verificare proprie. Oricine găsește URL-ul poate trimite mesaje false în tabel.
Faceți acest lucru cu EasyCoexistence

Conectați numărul la easycoexistence.com, setați destinația webhook către URL-ul funcției Edge cu verify token-ul dumneavoastră și păstrați phone number id-ul și token-ul ca secrete ale funcției. De la US$ 9 pe număr pe lună, scăzând la US$ 1 pe măsură ce adăugați numere, cu primele 3 zile gratuite.

Întrebări frecvente

Funcționează pe planul gratuit Supabase?

Edge Functions rulează pe planul gratuit, cu o limită de 150 de secunde de timp real, față de 400 de secunde pe planurile plătite. Un handler de webhook care răspunde rapid rămâne cu mult sub oricare dintre ele.

Să folosesc service role key în funcție?

Doar pe partea de server, ca aici, citită din mediul funcției. Ocolește row level security, deci nu trebuie să ajungă niciodată într-un browser.

Poate o singură funcție să deservească mai multe numere?

Da. Îndreptați destinația webhook a fiecărui număr către ea și ramificați după phone_number_id din fiecare payload.

Vede EasyCoexistence mesajele?

Nu. Meta livrează fiecare webhook direct către funcția dumneavoastră, iar EasyCoexistence nu stochează niciodată conținutul conversațiilor.

Continuați să citiți

Gata să începeți?

Configurați WhatsApp Coexistence în câteva minute, nu luni. Aplicația continuă să funcționeze pe telefon.

Începeți perioada de probă gratuită

Verificat la

Webhook-uri WhatsApp în Supabase Edge Functions