Giriş yapınÜcretsiz denemeyi başlatın
Meta Tech Provider

Supabase Edge Function ile WhatsApp webhook'larını alma

Bir Supabase Edge Function, numaranın webhook hedefi olarak doğrudan kullanılabilir. Meta'nın doğrulama GET isteğini hub.challenge değeriyle yanıtlar, POST edilen her mesajı bir Postgres tablosuna ekler ve Cloud API üzerinden yanıt verir. Fonksiyonu JWT doğrulaması kapalı olarak dağıtın, çünkü Supabase varsayılan olarak geçerli bir JWT ister ve Meta bunu gönderemez.

verify_jwt = true

Supabase'in Edge Function'lar için varsayılanı: geçerli JWT içermeyen her isteği, handler'ınız çalışmadan önce reddeder.

--no-verify-jwt

Bir fonksiyon için JWT doğrulamasını kapatan supabase functions deploy bayrağı.

150 s

Edge Function için istek boşta kalma zaman aşımı; bu sürede yanıt dönmezse 504 verilir.

Edge Function'ın ne yapması gerekir?

İki yöntemde iki iş.

GET isteğinde Meta'nın doğrulamasını yanıtlar. Sorgu dizesinden hub.mode, hub.verify_token ve hub.challenge değerlerini okuyun, token'ı hedefin doğrulama token'ı olarak girdiğiniz değerle karşılaştırın ve challenge değerini düz metin gövdesi olarak 200 durum koduyla döndürün. Token eşleşmezse 403 döndürün.

POST isteğinde olayları alır. Gövdeyi ayrıştırın, mesajları entry, changes ve value içinden çıkarın, bir tabloya ekleyin ve 200 döndürün. Yanıtı hızlı tutun: Meta yavaş ya da başarısız bir yanıtı teslimat hatası sayar ve yeniden dener, bu da aynı mesajın iki kez gelmesi demektir.

Fonksiyonun URL'sini EasyCoexistence'ta numaranın webhook hedefi olarak, aynı doğrulama token'ıyla birlikte ayarlayın. Meta teslimata başlar.

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

Fonksiyon neden Meta'ya 401 döndürüyor?

Çünkü JWT doğrulaması açık. Supabase, bir Edge Function'ın handler'ı çalışmadan önce Authorization başlığındaki JWT'yi doğrular ve bu her fonksiyon için varsayılandır. Meta böyle bir başlık göndermez, bu yüzden platform hem doğrulama GET isteğini hem de her teslimatı reddeder ve kodunuz bunları hiç görmez.

Bunu yalnızca bu fonksiyon için kapatın. Ya supabase functions deploy whatsapp --no-verify-jwt ile dağıtın ya da supabase/config.toml içinde fonksiyonun bölümüne verify_jwt = false yazın. Supabase belgeleri tam olarak bu durumu anlatır: dış bir webhook sağlayıcısının çağırdığı herkese açık uç nokta.

Platform denetimi kapalıyken fonksiyonun kendi denetimi olmalı. İmza başlığını kullanamazsınız, çünkü Meta aboneliğin sahibi olan uygulamanın app secret'ıyla imzalar. Hedef URL'ye sorgu parametresi olarak uzun, rastgele bir secret koyun ve örnekteki POST'ta olduğu gibi onsuz gelen istekleri reddedin.

Fonksiyon nasıl kaydetmeli ve yanıtlamalı?

Önce kaydedin, sonra yanıtlayın. WhatsApp mesaj kimliği üzerinde benzersiz kısıtlamayla upsert yapın; böylece Meta'nın yeniden denemeleri kopya oluşturmak yerine aynı satıra düşer. Fonksiyona birden fazla numara bağlıysa her satırda alıcı phone_number_id değerini tutun.

Bir modeli ya da başka bir API'yi çağırmak gibi yavaş işleri EdgeRuntime.waitUntil'e devredin ve yanıtı hemen döndürün. Fonksiyon bu promise tamamlanana kadar çalışmaya devam eder, Meta ise 200 yanıtını çoktan almıştır. Supabase 150 saniyelik boşta kalma süresinde beklemeyi bırakır ve 504 döndürür; yani yanıtlamadan önce tüm işi yapan bir handler yeniden denemelere en çok yol açandır.

Yanıtlar, numaranın phone number id değeri ve token'ı ile Cloud API'nin mesaj uç noktasına gider; ikisini de fonksiyon secret'ları olarak saklayın. Müşterinin son mesajından sonraki 24 saat içinde serbest metin iletilir; bu sürenin dışında yalnızca onaylı bir şablon iletilir.

Telefondan gönderilen mesajlar fonksiyona ulaşır mı?

Evet. Coexistence numarasında WhatsApp Business uygulaması çalışmaya devam eder ve biri oradan yanıt verdiğinde Meta, giden mesajın bir yankısını aynı hedefe iletir. Bu yankı messages yerine smb_message_echoes alanı ve message_echoes dizisi altında gelir.

Ayrıştırıcıyı ikisini de okuyacak şekilde genişletin ve bir direction sütunu saklayın. Tablo böylece müşterinin satırlarını ve işletmenin yanıtlarını içeren tüm konuşmayı tutar. Çoğu kişi sonradan bunu sorgulamak ister: yanıt süreleri, yanıtsız konuşmalar, kişi başına son gelen mesaj zamanı.

Bu son değer, her gönderim yolunun da ihtiyaç duyduğu girdidir; çünkü yanıtın serbest metin mi yoksa şablon mu olacağını belirler. Tablo üzerindeki bir görünüm (view) bunu uygulamanızın geri kalanına kişi başına sunabilir.

Yaygın hatalar

  • JWT doğrulaması açık bırakılarak dağıtmak. Supabase, Meta'nın isteklerini handler çalışmadan 401 ile reddeder ve hiçbir mesaj ulaşmaz.
  • Challenge değerini JSON içine sarıp döndürmek. Meta gövde olarak ham değeri bekler, aksi halde doğrulama sessizce başarısız olur.
  • Yanıt vermeden önce yavaş iş yapmak. Meta başarısız saydığı istekleri yeniden dener ve aynı mesaj iki kez işlenir.
  • JWT doğrulamasını kendi denetiminiz olmadan kapatmak. URL'yi bulan herkes tabloya sahte mesajlar yazabilir.
Bunu EasyCoexistence'la yapmak

Numarayı easycoexistence.com üzerinden bağlayın, webhook hedefini doğrulama token'ınızla birlikte Edge Function'ın URL'sine ayarlayın ve phone number id ile token'ı fonksiyon secret'ları olarak saklayın. Bağlı numara başına ayda US$ 9'dan başlar, numara ekledikçe US$ 1'e düşer; ilk 3 gün ücretsizdir.

Sık sorulan sorular

Bu, Supabase'in ücretsiz planında çalışır mı?

Edge Function'lar ücretsiz planda çalışır; duvar saati sınırı 150 saniyedir, ücretli planlarda ise 400 saniye. Hızlı yanıt veren bir webhook handler'ı ikisinin de çok altında kalır.

Fonksiyonda service role key kullanmalı mıyım?

Yalnızca sunucu tarafında, buradaki gibi fonksiyonun ortam değişkenlerinden okuyarak kullanın. Satır düzeyi güvenliği (RLS) atlar, bu yüzden tarayıcıya asla ulaşmamalıdır.

Tek fonksiyon birden fazla numaraya hizmet verebilir mi?

Evet. Her numaranın webhook hedefini bu fonksiyona yönlendirin ve her payload'daki phone_number_id değerine göre dallanın.

EasyCoexistence mesajları görür mü?

Hayır. Meta her webhook'u doğrudan fonksiyonunuza iletir ve EasyCoexistence konuşma içeriğini hiçbir zaman saklamaz.

Okumaya devam edin

Başlamaya hazır mısınız?

WhatsApp Coexistence'ı aylar değil, dakikalar içinde kurun. Uygulama telefonda çalışmaya devam eder.

Ücretsiz denemeyi başlatın

Doğrulama tarihi

Supabase Edge Functions'ta WhatsApp Webhook'ları