Wysyłanie i odbieranie WhatsApp z PHP i Laravela
Odbieranie to trasa Laravela, która odpowiada na weryfikacyjne żądanie GET od Meta i przyjmuje żądania POST. Parametry odczytujesz jako hub_mode, hub_verify_token i hub_challenge, ponieważ PHP zamienia kropki na podkreślniki. Wysyłanie to jedno żądanie POST przez Http::withToken() do endpointu `messages` w Cloud API, z identyfikatorem numeru telefonu i tokenem. SDK nie jest potrzebne.
PHP zamienia kropki w nazwach przychodzących zmiennych na podkreślniki, więc hub.challenge jest odczytywane jako hub_challenge.
Bezstanowe trasy API w Laravelu, tworzone przez install:api, są serwowane pod /api i znajdują się poza ochroną CSRF grupy web.
Wersja Graph API z aktualnych przykładów wysyłania wiadomości w dokumentacji Meta, pod adresem POST /{phone-number-id}/messages.
Jak odbierać wiadomości WhatsApp w Laravelu?
Za pomocą GET i POST na tej samej ścieżce w routes/api.php. Jeśli plik nie istnieje, najpierw uruchom php artisan install:api. Trasy API są bezstanowe i leżą poza grupą web, więc POST od Meta nie zostanie odrzucony z powodu braku tokenu CSRF, co dzieje się na trasie w routes/web.php.
GET odpowiada na weryfikację Meta. Meta wysyła hub.mode, hub.verify_token i hub.challenge, ale PHP zamienia kropki w nazwach przychodzących zmiennych na podkreślniki, więc Laravel widzi hub_mode, hub_verify_token i hub_challenge. Kod odczytujący nazwy z kropkami dostaje null i nigdy nie przejdzie weryfikacji. Zwróć wartość challenge jako zwykły tekst, a nie przez response()->json().
POST przekazuje payload do zadania w kolejce i od razu zwraca 200. Umieść w ścieżce długi losowy klucz i sprawdzaj go na obu trasach: Meta podpisuje dostawy kluczem aplikacji Tech Providera, więc nagłówka z podpisem nie da się zweryfikować po Twojej stronie.
use App\Jobs\HandleWhatsAppEvent;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::get('/whatsapp/{key}', function (Request $request, string $key) {
abort_unless(hash_equals(config('services.whatsapp.url_key'), $key), 404);
// PHP turns hub.mode into hub_mode: read the underscored names
if ($request->query('hub_mode') === 'subscribe'
&& hash_equals(config('services.whatsapp.verify_token'),
(string) $request->query('hub_verify_token'))) {
return response((string) $request->query('hub_challenge'), 200)
->header('Content-Type', 'text/plain');
}
abort(403);
});
Route::post('/whatsapp/{key}', function (Request $request, string $key) {
abort_unless(hash_equals(config('services.whatsapp.url_key'), $key), 404);
HandleWhatsAppEvent::dispatch($request->json()->all());
return response('', 200);
});Jak wysłać wiadomość WhatsApp z Laravela?
Za pomocą klienta HTTP Laravela: jedno żądanie POST do endpointu `messages` dla danego identyfikatora numeru telefonu, z tokenem dostępu przekazanym jako bearer token. Obie wartości pochodzą z połączenia, są widoczne w panelu i zwracane przez get_api_credentials. Powinny trafić do config/services.php, oparte na zmiennych środowiskowych, a nie do kodu.
Kształt treści zależy od czasu. W ciągu 24 godzin od ostatniej wiadomości klienta działa obiekt tekstowy `text`, jak poniżej. Poza tym oknem Meta przyjmuje wyłącznie obiekt szablonu `template` ze wskazaniem zatwierdzonego szablonu i jego języka, a dowolny tekst kończy się błędem 131047. Najlepiej zobaczyć to w odpowiedzi: nieudane wywołanie zwraca obiekt `error`, którego kod mówi, co poszło nie tak, więc zapisuj go w logach zamiast go odrzucać.
Nic w tym żądaniu nie jest specyficzne dla EasyCoexistence. To wywołanie z dokumentacji Meta, więc każdy klient HTTP w PHP, Guzzle czy zwykły cURL, działa tak samo.
use Illuminate\Support\Facades\Http;
$response = Http::withToken(config('services.whatsapp.token'))
->post('https://graph.facebook.com/v25.0/'
. config('services.whatsapp.phone_number_id') . '/messages', [
'messaging_product' => 'whatsapp',
'to' => '14155550188', // digits only, with country code
'type' => 'text',
'text' => ['body' => 'Thanks, we are on it.'],
]);
if ($response->failed()) {
// error.code 131047: outside the window, send a template instead
logger()->warning('WhatsApp send failed', $response->json('error', []));
}Czego produkcyjny handler w Laravelu potrzebuje poza tym?
Prawdziwej kolejki, deduplikacji i sprawdzania okna.
Kolejka ma znaczenie, bo Meta ponawia dostawę, gdy odpowiedź jest wolna. HandleWhatsAppEvent::dispatch() wraca szybko tylko wtedy, gdy połączenie kolejki jest prawdziwe i działa worker. Na sterowniku sync zadanie wykonuje się w trakcie żądania, a wolne wywołanie modelu lub zapis do bazy kończy się zdublowaną dostawą.
Deduplikuj po identyfikatorze wiadomości, czyli wamid w każdej wiadomości, bo ponowienia są normą, a nie wyjątkiem. Wystarczy unikalny indeks na tej kolumnie albo blokada w pamięci podręcznej z tym kluczem.
Zapisuj też, kiedy każdy kontakt napisał ostatnio. Dzięki temu znacznikowi czasu ścieżka wysyłania sama wybiera tekst lub szablon, zamiast dowiadywać się o tym z błędu 131047, gdy klient już czeka. Odpowiedzi wpisane na telefonie w aplikacji WhatsApp Business docierają jako zdarzenia `echo` na tę samą trasę, więc handler widzi także, kiedy rozmowę przejęła osoba.
Typowe błędy
- Odczytywanie hub.challenge z kropką. PHP zmienił nazwę na hub_challenge, więc handler zwraca pustą treść, a Meta nigdy nie przeprowadzi weryfikacji.
- Umieszczenie webhooka w routes/web.php. POST od Meta nie ma tokenu CSRF i jest odrzucany z kodem 419.
- Wysyłanie zadań do kolejki sync. Zadanie wykonuje się przed odpowiedzią 200, wolna obsługa jest ponawiana, a wiadomości przetwarzane dwa razy.
- Zwracanie wartości challenge przez response()->json(). Cudzysłowy wokół wartości powodują, że sprawdzenie Meta kończy się niepowodzeniem.
Połącz numer na easycoexistence.com, ustaw adres docelowy webhooka na swoją trasę Laravela z kluczem w ścieżce, a identyfikator numeru telefonu i token odczytaj w panelu. Od US$ 9 za numer miesięcznie, spadając do US$ 1 w miarę dodawania numerów, a pierwsze 3 dni są bezpłatne.
Najczęściej zadawane pytania
Czy istnieje SDK dla PHP?
Nie od nas i nie jest potrzebne. API należy do Meta i jest opisane przez Meta, a klient HTTP Laravela lub Guzzle łączy się z nim bezpośrednio.
Czy to działa bez Laravela?
Tak. W czystym PHP odczytaj $_GET['hub_challenge'], zwróć je jako text/plain, a wiadomości wysyłaj przez cURL. Reguła podkreślników obowiązuje w każdym frameworku PHP.
Dlaczego adres URL wymaga klucza w ścieżce?
Bo to jedyne działające uwierzytelnienie. Podpis Meta używa klucza aplikacji Tech Providera, który nigdy nie jest udostępniany, więc to sekret w adresie URL chroni przed fałszywymi zdarzeniami od obcych.
Czy zespół nadal może korzystać z telefonu?
Tak. Coexistence sprawia, że aplikacja WhatsApp Business nadal działa na tym numerze, a wiadomości wysłane z telefonu docierają na Twoją trasę jako zdarzenia `echo`.
Czytaj dalej
Gotowy, aby zacząć?
Skonfiguruj WhatsApp Coexistence w kilka minut, nie miesięcy. Aplikacja nadal działa na telefonie.
Rozpocznij bezpłatny okres próbnyZweryfikowano w