ΣύνδεσηΞεκινήστε δωρεάν δοκιμή
Meta Tech Provider

Αποστολή και λήψη μηνυμάτων WhatsApp από PHP και Laravel

Η λήψη γίνεται με ένα route του Laravel που απαντά στο GET επαλήθευσης της Meta και δέχεται POST, διαβάζοντας τις παραμέτρους ως hub_mode, hub_verify_token και hub_challenge, επειδή η PHP μετατρέπει τις τελείες σε κάτω παύλες. Η αποστολή είναι ένα POST με Http::withToken() στο endpoint messages του Cloud API, με το phone number id και το token του αριθμού. Δεν χρειάζεται SDK.

hub_challenge

Η PHP μετατρέπει τις τελείες στα ονόματα των εισερχόμενων μεταβλητών σε κάτω παύλες, οπότε το hub.challenge διαβάζεται ως hub_challenge.

routes/api.php

Τα stateless API routes του Laravel, που δημιουργούνται με install:api, εξυπηρετούνται κάτω από το /api και εκτός της προστασίας CSRF του web group.

v25.0

Η έκδοση του Graph API στα τρέχοντα παραδείγματα αποστολής μηνύματος της Meta, στο POST /{phone-number-id}/messages.

Πώς λαμβάνετε μηνύματα WhatsApp στο Laravel;

Με ένα GET και ένα POST στην ίδια διαδρομή στο routes/api.php. Εκτελέστε πρώτα php artisan install:api, αν το αρχείο δεν υπάρχει. Τα API routes είναι stateless και βρίσκονται εκτός του web group, επομένως το POST της Meta δεν απορρίπτεται λόγω απουσίας token CSRF, όπως συμβαίνει σε route του routes/web.php.

Το GET απαντά στην επαλήθευση της Meta. Η Meta στέλνει hub.mode, hub.verify_token και hub.challenge, όμως η PHP μετατρέπει τις τελείες στα ονόματα των εισερχόμενων μεταβλητών σε κάτω παύλες, οπότε το Laravel βλέπει hub_mode, hub_verify_token και hub_challenge. Ο κώδικας που διαβάζει τα ονόματα με τελείες παίρνει null και δεν επαληθεύεται ποτέ. Επιστρέψτε το challenge ως απλό κείμενο, όχι μέσω response()->json().

Το POST παραδίδει το payload σε queued job και επιστρέφει αμέσως 200. Βάλτε ένα μακρύ τυχαίο κλειδί στη διαδρομή και ελέγξτε το και στα δύο routes: η Meta υπογράφει τις παραδόσεις με το app secret του Tech Provider, άρα η κεφαλίδα υπογραφής δεν μπορεί να επικυρωθεί από εσάς.

routes/api.php: verification and event intake
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);
});

Πώς στέλνετε μήνυμα WhatsApp από το Laravel;

Με τον HTTP client του Laravel: ένα POST στο endpoint messages για το phone number id, με το access token ως bearer token. Και οι δύο τιμές προέρχονται από τη σύνδεση, εμφανίζονται στο dashboard και επιστρέφονται από το get_api_credentials, και ανήκουν στο config/services.php με μεταβλητές περιβάλλοντος και όχι μέσα στον κώδικα.

Η μορφή του σώματος εξαρτάται από τον χρόνο. Μέσα σε 24 ώρες από το τελευταίο μήνυμα του πελάτη λειτουργεί ένα αντικείμενο text, όπως παρακάτω. Εκτός αυτού του παραθύρου η Meta δέχεται μόνο αντικείμενο template που ονομάζει ένα εγκεκριμένο template και τη γλώσσα του, ενώ το ελεύθερο κείμενο αποτυγχάνει με το σφάλμα 131047. Η απόκριση είναι το σημείο όπου θα το δείτε: μια αποτυχημένη κλήση επιστρέφει αντικείμενο error, του οποίου ο κωδικός δείχνει τι πήγε στραβά, γι' αυτό καταγράψτε το αντί να το πετάτε.

Τίποτα στο αίτημα δεν αφορά αποκλειστικά το EasyCoexistence. Είναι η κλήση της τεκμηρίωσης της ίδιας της Meta, οπότε κάθε HTTP client της PHP, το Guzzle ή το απλό cURL, λειτουργεί με τον ίδιο τρόπο.

Send a text reply inside the 24 hour window
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', []));
}

Τι χρειάζεται ένας handler Laravel παραγωγής πέρα από αυτά;

Πραγματική ουρά, αποσυσσώρευση διπλότυπων και έλεγχο του παραθύρου.

Η ουρά έχει σημασία επειδή η Meta ξαναστέλνει όταν η απόκριση αργεί. Το HandleWhatsAppEvent::dispatch() επιστρέφει γρήγορα μόνο όταν η σύνδεση ουράς είναι πραγματική και τρέχει worker. Με τον driver sync η εργασία εκτελείται μέσα στο αίτημα, και μια αργή κλήση μοντέλου ή εγγραφή στη βάση καταλήγει σε διπλή παράδοση.

Εντοπίστε τα διπλότυπα με βάση το id του μηνύματος, το wamid κάθε μηνύματος, επειδή οι επαναλήψεις είναι φυσιολογικές και όχι εξαίρεση. Αρκεί ένας μοναδικός δείκτης σε αυτή τη στήλη ή ένα cache lock με αυτό το κλειδί.

Τέλος, αποθηκεύετε πότε έγραψε τελευταία φορά κάθε επαφή. Με αυτή την ημερομηνία η διαδρομή αποστολής επιλέγει μόνη της text ή template, αντί να το μαθαίνει από το σφάλμα 131047 όταν ο πελάτης περιμένει ήδη. Οι απαντήσεις που πληκτρολογούνται στο κινητό, στην εφαρμογή WhatsApp Business, φτάνουν ως echo events στο ίδιο route, ώστε ο handler να βλέπει και πότε ένας άνθρωπος ανέλαβε τη συνομιλία.

Συνηθισμένα λάθη

  • Ανάγνωση του hub.challenge με τελεία. Η PHP το μετονόμασε σε hub_challenge, οπότε ο handler επιστρέφει κενό σώμα και η Meta δεν επαληθεύει ποτέ.
  • Τοποθέτηση του webhook στο routes/web.php. Το POST της Meta δεν έχει token CSRF και απορρίπτεται με 419.
  • Αποστολή στην ουρά sync. Η εργασία εκτελείται πριν από το 200, η αργή επεξεργασία ξαναστέλνεται και τα μηνύματα επεξεργάζονται δύο φορές.
  • Επιστροφή του challenge με response()->json(). Τα εισαγωγικά γύρω από την τιμή αποτυγχάνουν στον έλεγχο της Meta.
Κάντε το με EasyCoexistence

Συνδέστε τον αριθμό στο easycoexistence.com, ορίστε ως προορισμό του webhook το route του Laravel με το κλειδί του στη διαδρομή και διαβάστε το phone number id και το token από το dashboard. Από US$ 9 ανά αριθμό τον μήνα, με πτώση έως τα US$ 1 καθώς προσθέτετε αριθμούς, και τις πρώτες 3 ημέρες δωρεάν.

Συχνές ερωτήσεις

Υπάρχει SDK για PHP;

Όχι από εμάς, και δεν χρειάζεται. Το API είναι της Meta, τεκμηριωμένο από τη Meta, και ο HTTP client του Laravel ή το Guzzle το προσεγγίζουν απευθείας.

Λειτουργεί και χωρίς Laravel;

Ναι. Σε απλή PHP διαβάστε το $_GET['hub_challenge'], επιστρέψτε το ως text/plain και στείλτε με cURL. Ο κανόνας της κάτω παύλας ισχύει για κάθε framework της PHP.

Γιατί το URL χρειάζεται κλειδί στη διαδρομή;

Επειδή είναι ο μόνος τρόπος ταυτοποίησης που λειτουργεί. Η υπογραφή της Meta χρησιμοποιεί το app secret του Tech Provider, το οποίο δεν μοιράζεται ποτέ, οπότε το μυστικό στο URL εμποδίζει τρίτους να στέλνουν πλαστά events.

Μπορεί η ομάδα να συνεχίσει να χρησιμοποιεί το κινητό;

Ναι. Το Coexistence διατηρεί την εφαρμογή WhatsApp Business ενεργή στον αριθμό, και τα μηνύματα που στέλνονται από το κινητό φτάνουν στο route σας ως echo events.

Συνεχίστε την ανάγνωση

Έτοιμοι να ξεκινήσετε;

Ρυθμίστε το WhatsApp Coexistence σε λίγα λεπτά, όχι σε μήνες. Η εφαρμογή συνεχίζει να λειτουργεί στο τηλέφωνο.

Ξεκινήστε δωρεάν δοκιμή

Επαληθεύτηκε στις

Αποστολή WhatsApp από PHP και Laravel