WhatsApp versturen en ontvangen vanuit PHP en Laravel
Ontvangen is een Laravel-route die de verificatie-GET van Meta beantwoordt en POST-verzoeken accepteert. Je leest de parameters als hub_mode, hub_verify_token en hub_challenge, omdat PHP punten omzet in underscores. Versturen is één Http::withToken()-POST naar het messages-endpoint van de Cloud API, met het phone number id en het token van het nummer. Een SDK is niet nodig.
PHP zet punten in namen van binnenkomende variabelen om in underscores, dus hub.challenge lees je als hub_challenge.
De stateless API-routes van Laravel, aangemaakt met install:api, draaien onder /api en buiten de CSRF-bescherming van de web-groep.
De Graph API-versie uit de huidige voorbeelden van Meta voor het versturen van berichten, op POST /{phone-number-id}/messages.
Hoe ontvang je WhatsApp-berichten in Laravel?
Met een GET en een POST op hetzelfde pad in routes/api.php. Voer eerst php artisan install:api uit als het bestand niet bestaat. API-routes zijn stateless en vallen buiten de web-groep, dus de POST van Meta wordt niet afgewezen vanwege een ontbrekend CSRF-token. Dat gebeurt wel bij een route in routes/web.php.
De GET beantwoordt de verificatie van Meta. Meta stuurt hub.mode, hub.verify_token en hub.challenge, maar PHP schrijft punten in namen van binnenkomende variabelen om naar underscores, dus Laravel ziet hub_mode, hub_verify_token en hub_challenge. Code die de namen met punt leest, krijgt null en verifieert nooit. Geef de challenge terug als platte tekst, niet via response()->json().
De POST geeft de payload door aan een job in de wachtrij en geeft meteen 200 terug. Zet een lange, willekeurige sleutel in het pad en controleer die op beide routes: Meta ondertekent leveringen met het app secret van de Tech Provider, dus de handtekening in de header kun je aan jouw kant niet valideren.
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);
});Hoe verstuur je een WhatsApp-bericht vanuit Laravel?
Met de HTTP-client van Laravel: één POST naar het messages-endpoint van het phone number id, met het access token als bearer token. Beide waarden komen uit de verbinding, staan in het dashboard en worden teruggegeven door get_api_credentials. Zet ze in config/services.php, gevoed door omgevingsvariabelen, en niet in de code.
De vorm van de body hangt af van het tijdstip. Binnen 24 uur na het laatste bericht van de klant werkt een text-object, zoals hieronder. Buiten dat venster accepteert Meta alleen een template-object met een goedgekeurd template en de taal ervan, en vrije tekst mislukt met foutcode 131047. Het antwoord laat het zien: een mislukte aanroep geeft een error-object terug waarvan de code zegt wat er misging. Log het dus, in plaats van het weg te gooien.
Niets in het verzoek is specifiek voor EasyCoexistence. Het is de aanroep uit de eigen documentatie van Meta, dus elke PHP HTTP-client, Guzzle of gewone cURL, werkt op dezelfde manier.
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', []));
}Wat heeft een Laravel-handler in productie daarnaast nodig?
Een echte wachtrij, ontdubbeling en een controle op het venster.
De wachtrij is belangrijk omdat Meta een traag antwoord opnieuw probeert. HandleWhatsAppEvent::dispatch() komt alleen snel terug als de queue-verbinding een echte is en er een worker draait. Bij de sync-driver draait de job binnen het verzoek, en een trage modelaanroep of databaseschrijfactie wordt een dubbele levering.
Ontdubbel op het bericht-id, de wamid in elk bericht, want nieuwe pogingen zijn normaal en geen uitzondering. Een unieke index op die kolom, of een cache-lock met die waarde als sleutel, is genoeg.
En sla op wanneer elk contact voor het laatst schreef. Met dat tijdstip kiest het verzendpad zelf tussen tekst en template, in plaats van het via foutcode 131047 te ontdekken terwijl de klant al zat te wachten. Antwoorden die op de telefoon in de WhatsApp Business-app worden getypt, komen als echo-events binnen op dezelfde route, dus de handler ziet ook wanneer een persoon een gesprek heeft overgenomen.
Veelgemaakte fouten
- hub.challenge lezen met een punt. PHP hernoemde het naar hub_challenge, dus de handler geeft een lege body terug en Meta verifieert nooit.
- De webhook in routes/web.php zetten. De POST van Meta heeft geen CSRF-token en wordt afgewezen met 419.
- Dispatchen naar de sync-wachtrij. De job draait vóór de 200, trage verwerking wordt opnieuw geprobeerd en berichten worden twee keer verwerkt.
- De challenge teruggeven met response()->json(). De aanhalingstekens rond de waarde laten de controle van Meta mislukken.
Verbind het nummer op easycoexistence.com, stel de webhook-bestemming in op je Laravel-route met de sleutel in het pad, en lees het phone number id en het token uit het dashboard. Vanaf US$ 9 per nummer per maand, dalend tot US$ 1 naarmate er nummers bijkomen, met de eerste 3 dagen gratis.
Veelgestelde vragen
Is er een PHP SDK?
Niet van ons, en je hebt er geen nodig. De API is van Meta, door Meta gedocumenteerd, en de HTTP-client van Laravel of Guzzle bereikt hem rechtstreeks.
Werkt dit ook zonder Laravel?
Ja. Lees in gewone PHP $_GET['hub_challenge'], geef die terug als text/plain en verstuur met cURL. De underscore-regel geldt voor elk PHP-framework.
Waarom heeft de URL een sleutel in het pad nodig?
Omdat dat de authenticatie is die werkt. De handtekening van Meta gebruikt het app secret van de Tech Provider, dat nooit wordt gedeeld, dus het geheim in de URL houdt vreemden ervan af om nepgebeurtenissen te posten.
Kan het team de telefoon nog gebruiken?
Ja. Coexistence houdt de WhatsApp Business-app op het nummer werkend, en berichten die vanaf de telefoon worden verstuurd, bereiken je route als echo-events.
Lees verder
Klaar om te beginnen?
Stel WhatsApp Coexistence in enkele minuten in, niet maanden. De app blijft op je telefoon werken.
Start je gratis proefperiodeGeverifieerd op