Mag-log inMagsimula ng Libreng Trial
Meta Tech Provider

Tumanggap ng WhatsApp webhooks sa isang Supabase Edge Function

Maaaring direktang maging webhook destination ng numero ang isang Supabase Edge Function. Sinasagot nito ang verification GET ng Meta gamit ang halaga ng hub.challenge, ini-insert sa Postgres table ang bawat mensaheng na-POST, at nagre-reply sa pamamagitan ng Cloud API. I-deploy ito nang naka-off ang JWT verification, dahil default na humihingi ang Supabase ng valid na JWT at hindi makapagpapadala nito ang Meta.

verify_jwt = true

Default ng Supabase para sa Edge Functions: tinatanggihan nito ang anumang request na walang valid na JWT bago pa tumakbo ang inyong handler.

--no-verify-jwt

Ang flag ng supabase functions deploy na nagdi-disable ng JWT verification para sa isang function.

150 s

Idle timeout ng request para sa isang Edge Function; kapag walang sagot pagsapit nito, 504 ang ibabalik.

Ano ang kailangang gawin ng Edge Function?

Dalawang bagay, sa dalawang method.

Sa GET, sasagutin nito ang verification ng Meta. Basahin ang hub.mode, hub.verify_token at hub.challenge mula sa query string, ihambing ang token sa verify token na inilagay ninyo bilang destination, at ibalik ang challenge bilang plain text na body na may status 200. Magbalik ng 403 kapag hindi tugma ang token.

Sa POST, tatanggap ito ng mga event. I-parse ang body, kunin ang mga mensahe mula sa entry, changes at value, i-insert ang mga ito sa isang table, at magbalik ng 200. Panatilihing mabilis ang sagot: itinuturing ng Meta na bigong delivery ang mabagal o bigong sagot at nagre-retry ito, kaya dalawang beses darating ang parehong mensahe.

Itakda po ninyo ang URL ng function bilang webhook destination ng numero sa EasyCoexistence, gamit ang parehong verify token, at magsisimula nang mag-deliver ang Meta.

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");
});

Bakit nagbabalik ng 401 ang function sa Meta?

Dahil naka-on ang JWT verification. Bini-verify ng Supabase ang isang JWT sa Authorization header bago tumakbo ang handler ng Edge Function, at default iyon sa bawat function. Walang ganoong header na ipinapadala ang Meta, kaya tinatanggihan ng platform ang verification GET at ang bawat delivery, at hindi kailanman nakikita ng inyong code ang mga ito.

I-off ito para sa function na ito lamang. I-deploy gamit ang supabase functions deploy whatsapp --no-verify-jwt, o itakda ang verify_jwt = false sa seksyon ng function sa supabase/config.toml. Eksaktong ganitong kaso ang inilalarawan ng docs ng Supabase: isang public endpoint na tinatawagan ng panlabas na webhook provider.

Kapag naka-off ang check ng platform, kailangan ng function ng sarili nitong check. Hindi magagamit ang signature header, dahil pinipirmahan ng Meta gamit ang app secret ng app na may-ari ng subscription. Maglagay ng mahabang random na secret sa destination URL bilang query parameter at tanggihan ang mga request na wala nito, tulad ng ginagawa ng halimbawa sa POST.

Paano dapat mag-store at mag-reply ang function?

Mag-store muna, mag-reply pagkatapos. Mag-upsert gamit ang WhatsApp message id na may unique constraint, para mapunta sa iisang row ang mga retry ng Meta sa halip na gumawa ng duplicate. Itago ang tumanggap na phone_number_id sa bawat row kung higit sa isang numero ang tumuturo sa function.

Para sa anumang mabagal, tulad ng pagtawag sa isang model o ibang API bago mag-reply, ipasa ang trabaho sa EdgeRuntime.waitUntil at ibalik agad ang sagot. Patuloy na tatakbo ang function hanggang matapos ang promise na iyon, habang nasa Meta na ang 200 nito. Humihinto ang Supabase sa paghihintay pagkalipas ng 150 segundong idle at nagbabalik ng 504, kaya ang handler na tinatapos muna ang lahat ng trabaho bago sumagot ang pinakamalamang na magdulot ng mga retry.

Papunta ang mga reply sa messages endpoint ng Cloud API, gamit ang phone number id at token ng numero, na nakatago bilang function secrets. Sa loob ng 24 oras mula sa huling mensahe ng customer, naide-deliver ang free-form text; sa labas nito, approved template lamang ang maaari.

Nakararating ba sa function ang mga mensaheng ipinadala mula sa telepono?

Oo. Sa Coexistence na numero, patuloy na gumagana ang WhatsApp Business app, at kapag may sumagot mula rito, nagde-deliver ang Meta ng echo ng palabas na mensaheng iyon sa parehong destination, sa ilalim ng field na smb_message_echoes at ng array na message_echoes sa halip na messages.

Palawigin ang parser para mabasa ang pareho at mag-store ng direction column. Hawak na ng table ang buong usapan, ang mga linya ng customer at ang mga sagot ng negosyo, na siyang gustong i-query ng karamihan pagkatapos: oras ng pagsagot, mga usapang walang sagot, at ang huling oras ng inbound kada contact.

Ang huling halagang iyon din ang kailangan ng bawat paraan ng pagpapadala, dahil dito nakasalalay kung free-form text ang reply o kailangang template. Maaaring ilantad ng isang view sa ibabaw ng table ang halagang ito kada contact para sa iba pang bahagi ng inyong app.

Mga karaniwang pagkakamali

  • Pag-deploy nang naka-on ang JWT verification. Tinatanggihan ng Supabase ang mga request ng Meta nang may 401 bago tumakbo ang handler, at walang mensaheng makararating.
  • Pagbabalik ng challenge na nakabalot sa JSON. Kailangan ng Meta ang raw na halaga bilang body, kung hindi ay tahimik na mabibigo ang verification.
  • Paggawa ng mabagal na trabaho bago sumagot. Nagre-retry ang Meta sa itinuturing nitong pagkabigo, at dalawang beses napoproseso ang parehong mensahe.
  • Pag-off ng JWT verification nang walang sariling check. Sinumang makakita ng URL ay maaaring mag-post ng pekeng mensahe sa table.
Ginagawa ito gamit ang EasyCoexistence

I-connect ang numero sa easycoexistence.com, itakda ang webhook destination nito sa URL ng Edge Function kasama ang inyong verify token, at itago ang phone number id at token bilang function secrets. Mula US$ 9 kada naka-connect na numero kada buwan, bumababa hanggang US$ 1 habang nagdadagdag kayo ng numero, at libre ang unang 3 araw.

Mga madalas itanong

Gumagana ba ito sa free plan ng Supabase?

Tumatakbo ang Edge Functions sa free plan, na may 150 segundong wall clock limit kumpara sa 400 segundo sa mga paid plan. Malayong mas mababa sa alinman sa dalawa ang oras ng webhook handler na mabilis sumagot.

Dapat ko bang gamitin ang service role key sa function?

Sa server-side lamang, tulad dito, na binabasa mula sa environment ng function. Nilalampasan nito ang row level security, kaya hindi ito dapat makarating sa browser.

Maaari bang pagsilbihan ng isang function ang ilang numero?

Oo. Ituro ang webhook destination ng bawat numero rito at mag-branch batay sa phone_number_id sa bawat payload.

Nakikita ba ng EasyCoexistence ang mga mensahe?

Hindi. Diretso sa inyong function ang bawat webhook mula sa Meta, at hindi kailanman nag-iimbak ng nilalaman ng usapan ang EasyCoexistence.

Magpatuloy sa pagbabasa

Handa na po ba kayong magsimula?

I-set up ang WhatsApp Coexistence sa loob ng ilang minuto, hindi buwan. Patuloy na gumagana ang app sa telepono.

Magsimula ng Libreng Trial

Na-verify noong

WhatsApp Webhooks sa Supabase Edge Functions