https://d226lax1qjow5r.cloudfront.net/blog/blogposts/send-sms-from-php-with-failover-dr/sms-cupcake.png

Envoi de SMS depuis PHP avec basculement : La boulangerie Cupcake

Publié le May 4, 2021

Temps de lecture : 12 minutes

Rester en contact avec les clients est essentiel pour toute organisation. Dans ce billet, nous allons explorer l Messages API de Vonage de Vonage et comment elle peut être utilisée pour s'assurer qu'une entreprise peut envoyer des messages à ses clients de la manière qui leur convient. L'étude de cas porte sur une entreprise imaginaire, la " Cupcake Bakery ", qui a besoin de pouvoir envoyer des messages à ses clients. Ce billet montre comment utiliser la pierre angulaire de la communication moderne, le SMS, dans votre application web PHP. Il montre également comment l'API Messages peut utiliser d'autres canaux de communication pour envoyer votre message si votre première tentative échoue, afin de vous donner les meilleures chances d'atteindre votre client.

Si vous êtes un développeur web prêt à mettre en œuvre des fonctionnalités de messagerie modernes dans votre application, alors ce tutoriel est fait pour vous !

Préparer les ingrédients

Avant de commencer, nous devons rassembler les ingrédients suivants :

  • PHP sur un serveur web accessible au public, ou PHP sur une plateforme de développement avec un outil tel que ngrok pour rendre votre site web accessible. Vonage envoie des réponses par webhook et doit donc être en mesure d'accéder à votre application.

  • CLI Vonage si vous ne l'avez pas déjà.

  • C'est plus amusant si vous avez deux numéros de téléphone auxquels vous pouvez envoyer des SMS :)

Vonage API Account

To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.

This tutorial also uses a virtual phone number. To purchase one, go to Numbers > Buy Numbers and search for one that meets your needs.

Voici l'API Messages

Vonage est depuis longtemps connu pour ses capacités SMS, mais l'API Messages est nouvelle (elle est encore en version bêta). Grâce à l'API Messages, vous pouvez envoyer des messages à un certain nombre de canaux différents à l'aide d'une interface unique. Il est souvent plus économique d'envoyer un message sur Facebook Messenger ou WhatsApp plutôt que par SMS, et l'API Messages vous permet de n'avoir à intégrer qu'un seul outil pour couvrir tous ces canaux. De plus, d'autres canaux de messages seront ajoutés au fil du temps. Il s'agit donc d'un investissement qui vous évite d'avoir à ajouter d'autres intégrations pour chaque nouvelle plateforme de messagerie en vogue.

En complément de l'API Messages, l'API Dispatch apporte une fiabilité supplémentaire à l'acheminement du message vers son destinataire. Avec l'API Dispatch, vous pouvez définir des règles sur ce qu'il convient de faire si un message n'est pas remis dans un délai donné - et indiquer également ce qu'il convient de faire ensuite. Ainsi, si vous disposez d'une autre méthode de contact pour cet utilisateur, un autre numéro de téléphone par exemple, ou si vous avez interagi avec lui sur Facebook, vous pouvez lui envoyer un second message via une autre méthode. Ce billet montre un exemple d'envoi à un autre numéro de téléphone, une fonction que je souhaite souvent pour moi-même lorsque j'utilise différents numéros dans différents pays lorsque je voyage !

Configurez vos points de terminaison Webhook

Visitez la page Messages et répartition sur le tableau de bord pour créer une application et configurer les webhooks que nous utiliserons pour ce projet :

  • L'URL d'état doit être [YOUR URL HERE]/status par exemple avec ngrok, ce serait quelque chose comme https://abcdef1.ngrok.io/status.

  • L'URL du message entrant doit être [YOUR URL HERE]/inbound par exemple avec ngrok, ce serait quelque chose comme https://abcdef1.ngrok.io/inbound.

Ces deux itinéraires, /status et /inboundpeuvent être appelées comme vous le souhaitez, mais les exemples ici correspondent à ce qui se trouve dans l'exemple de code que vous allez utiliser depuis GitHub dans un instant ...

Sur le tableau de bord, notez l'identifiant de l'application que vous créez et assurez-vous que vous avez également le fichier de la clé privée (il y a un clic pratique pour créer une clé qui générera une paire de clés publique/privée, plaçant la clé publique dans les paramètres de votre application et téléchargeant la clé privée pour que vous puissiez l'utiliser avec votre application).

Préchauffer le code

(est-ce que nous allons trop loin dans la métaphore de la pâtisserie ? Désolé !)

Le code d'une application fonctionnelle est disponible sur GitHub. Visitez le site https://github.com/nexmo-community/bakery-messaging-with-dispatch et clonez le dépôt ou téléchargez le code.

Une fois que vous l'avez, il y a quelques dépendances que nous devons installer. Pour garder les choses aussi simples que possible, ce projet utilise le micro-cadre Slim microframework. Pour effectuer les appels API (puisque les API Messaging et Dispatch sont encore en Beta, elles ne sont pas supportées dans notre bibliothèque PHP pour l'instant), le projet utilise GuzzleHTTP.

Pour installer les dépendances, utilisez Composer:

composer install

L'API Messages et l'API Dispatch utilisent des jetons Web JSON (JWT) pour l'authentification. Prenez l'ID d'application que vous avez créé dans le tableau de bord et utilisez-le avec la CLI de Vonage pour exécuter une commande comme celle-ci (en supposant que votre clé privée s'appelle . private.key) :

vonage jwt --application_id=VONAGE_APPLICATION_ID

La sortie de cette commande est votre JWT que vous utiliserez pour l'accès avec cette application ; copiez-le dans votre presse-papiers dès maintenant. Notez qu'il expire toutes les 24 heures, vous devrez donc peut-être répéter ce processus lorsque votre application, qui fonctionne parfaitement, commencera soudainement à renvoyer des erreurs "Invalid token" (jeton invalide).

L'application a besoin du JWT, de l'identifiant de l'application, mais aussi de quelques coordonnées pour les messages qu'elle enverra. Il existe un modèle de configuration dans config.php.samplecopiez ce fichier et appelez-le config.phppuis modifiez les valeurs pour les adapter à votre plateforme. Vous aurez besoin de

  • L'ID de l'application à nouveau.

  • Le JWT que vous venez de générer.

  • Le numéro de téléphone pour l'envoi de messages de.

  • Deux numéros de téléphone pour envoyer des messages à.

Après cela, les ingrédients sont prêts et nous pouvons commencer à faire quelque chose de génial !

Préparer une excellente communication avec les clients

À ce stade, les préparatifs sont terminés et l'application est prête à être utilisée. Configurez votre serveur web avec le répertoire public/ comme racine du site. J'utilise une plateforme de développement locale avec ngrok, donc mes commandes d'installation sont php -S localhost:8080 public/index.php dans un terminal et ngrok http 8080 dans un autre.

Si Ngrok vous donne une nouvelle URL (vous ne pouvez pas réserver d'URL sur un Account gratuit), n'oubliez pas de retourner dans le tableau de bord et de mettre à jour ces URL de webhook.

Chargeons maintenant la page d'accueil du projet. Vous devriez voir un formulaire très simple pour envoyer un message. Avant de le faire, prenons un moment pour voir comment cela va fonctionner.

Comprendre le processus de l'API Messages

Le processus d'envoi d'un message avec l'API Messages se déroule comme suit :

  1. Nous rédigeons le message ! Il est inséré dans le formulaire de saisie que vous pouvez voir sur la page d'accueil.

  2. Nous envoyons les coordonnées du numéro de téléphone dont provient le message dele numéro dont il s'agit à et le message lui-même, au format JSON. Consultez la documentation de l'API pour obtenir des informations détaillées sur ce que vous pouvez envoyer ici.

  3. La réponse à un message réussi est un code d'état 202, qui signifie "Accepté", et un champ message_uuid dans le corps JSON de la réponse.

  4. Toutes les communications ultérieures de Nexmo se font via POST par l'intermédiaire de webhooks de demande vers le point de terminaison de notre application /status de notre application. Chaque demande entrante comprendra l'ID du message auquel le statut se rapporte, ainsi qu'un horodatage. Ces informations sont au format JSON.

  5. Un webhook d'état indique que le message a été envoyé. Il y a plus d'informations informations sur les statuts des messages dans la documentation de l'API.

  6. Un autre webhook d'état indique que le message a été livré (s'il l'a été) et combien il a coûté.

Il est clair qu'il est très important que nous puissions lire les mises à jour de statut, alors regardons d'abord le code pour cela.

Traiter les mises à jour de statut

Lorsque quelque chose d'intéressant se produit concernant le statut du message, un webhook est envoyé au webhook que vous avez configuré dans le tableau de bord au début. Pour cette application, il s'agit de /status. Voici le code pour cette route :

$app->post('/status', function (Request $request, Response $response) {
    error_log($request->getBody());
    print_r($request->getParsedBody());
});

Dans cet exemple d'application, il ne fait pas grand-chose, mais il capture toutes les réponses et les place dans les journaux de votre serveur. Elle les affiche également en sortie, ce qui peut s'avérer utile ultérieurement lors du débogage de certaines fonctionnalités plus avancées.

Envoyez votre message au client

Screenshot showing a custom message to be sentCupcake Bakery Custom Message

À ce stade, n'hésitez pas à taper un message dans la case. Le mien est "Vos cupcakes sont prêts à être collectés", car je suis toujours heureuse de recevoir ce message. Appuyez sur Envoyer et le message devrait rapidement arriver sur votre téléphone. Était-ce de la magie ? Non, regardons le code.

$app->map(['GET', 'POST'], '/', function (Request $request, Response $response, array $args) {
    $config = $this->get('config');
    $information = [];
    $title = "Cupcake Bakery Customer Messaging";

    if($data = $request->getParsedBody()) {
        $message = $data['message'];
        $client = new \GuzzleHttp\Client(['base_uri' => "https://api.nexmo.com/v0.1/messages"]);

        try {
            $apiResponse = $client->request('POST', '/v0.1/messages', [
                'headers' => [
                    'Authorization' => 'Bearer ' . $config['jwt'],
                    'Content-Type' => 'application/json',
                    'Accept' => 'application/json'
                ],
                'json' => [
                    'from' => $config['from'],
                    'to' => $config['customer1'][0],
                    'message' => [
                        'content' => [
                            'type' => 'text',
                            'text' => $message
                        ]
                    ]
                ]
            ]);

            $information['statusCode'] = $apiResponse->getStatusCode();
            $information['body'] = $apiResponse->getBody();
        } catch (Exception $e) {
            $response = $e->getResponse();
            $responseBodyAsString = $response->getBody()->getContents();
            echo $responseBodyAsString;
            error_log($responseBodyAsString);
        }

    }

    $response = $this->view->render($response, 'index.html', ['information' => $information, 'title' => $title]);
    return $response;
});

Cet itinéraire est disponible à la fois pour les GET et POST car le formulaire est d'abord chargé avec GET puis, lorsqu'il est soumis, nous utilisons POST. S'il y a des POST les données du message sont utilisées avec la configuration que vous avez établie plus tôt pour construire une requête API. Le code d'état et le corps de la réponse sont capturés et transmis à la page, ce qui peut s'avérer utile lorsque vous travaillez avec cette application de démonstration ou que vous l'adaptez pour créer votre propre application. En utilisant cette sortie de débogage, vous pouvez saisir le code d'état et le corps de la réponse. message_uuid du message que vous venez d'envoyer.

Cet exemple utilise le cadre Slim, mais la plupart du code n'est pas spécifique à Slim et fonctionnerait dans n'importe quelle application PHP. Pour en savoir plus sur Slim, visitez le site https://www.slimframework.com/ - Je recommande tout particulièrement le tutoriel "tutoriel "Première application.

Suivi de la progression des messages

Puisque le point de terminaison /status est déjà configuré pour recevoir les webhooks, vous pouvez vous y rendre pour vérifier ce qui se passe. Si vous avez envoyé plusieurs messages, l'identifiant du message devient encore plus important, mais à ce stade, il est probablement évident de savoir à quel message se rapportent les mises à jour d'état.

Si le message est accepté, vous verrez deux mises à jour de l'état. La première confirme simplement que le message a été envoyé :

{
    "message_uuid": "a5587e33-c304-4bf9-85a3-823e379e8a68",
    "to": {
        "number": "447700900001",
        "type": "sms"
    },
    "from": {
        "number": "	447700900000",
        "type": "sms"
    },
    "timestamp": "2018-10-17T10:17:02.889Z",
    "status": "submitted"
}

Une fois le message arrivé sur mon téléphone, j'obtiens une deuxième mise à jour de l'état indiquant des informations sur la livraison du message :

{
    "message_uuid": "a5587e33-c304-4bf9-85a3-823e379e8a68",
    "to": {
        "number": "447700900001",
        "type": "sms"
    },
    "from": {
        "number": "	447700900000",
        "type": "sms"
    },
    "timestamp": "2018-10-17T10:17:05.480Z",
    "status": "delivered",
    "usage": {
        "price": "0.0333",
        "currency": "EUR"
    }
}

D'autres canaux de messagerie, tels que Facebook Messenger, peuvent également renvoyer un statut "lu" pour vous indiquer que l'utilisateur a effectivement vu le message.

Vous recevrez également des mises à jour de l'état d'avancement si votre message comporte des erreurs. Dans ce cas, il y aura un champ errors au niveau supérieur des données JSON et des les détails de l'erreur y compris un code et la raison de l'erreur. Gardez un œil sur le point de terminaison /status lorsque vous travaillez avec l'API Messages, car il contient de nombreuses informations clés qui vous aideront à développer vos propres applications.

Utiliser d'autres coordonnées si le message n'est pas reçu

Il s'agit d'une communication client de haut niveau : si le message n'atteint pas l'utilisateur, détectez-le et essayez d'autres coordonnées que vous possédez pour cet utilisateur. L'avantage est que, même si les utilisateurs préfèrent souvent WhatsApp ou Facebook Messenger (qui peuvent être moins chers à envoyer), les SMS atteignent les utilisateurs de manière plus fiable, même s'ils n'ont plus de données ou se trouvent dans une zone où le signal est faible. En tant que développeurs, nous n'avons pas besoin de travailler dur pour détecter l'état des messages, ajouter une logique de relance ou construire un code capable de gérer plusieurs plateformes de messagerie différentes. Dispatch API fait tout cela pour nous, c'est donc très, très facile.

Consultez la Dispatch API pour toute information dépassant le cadre de cet exemple spécifique

L'exemple d'application utilise un autre numéro de SMS (ce que je trouve souvent utile lorsque je voyage et qu'un forfait téléphonique fonctionne mieux qu'un autre dans certains endroits), mais vous pourriez configurer plusieurs détails "de" et prendre en charge n'importe quel nombre de méthodes de contact différentes pour un utilisateur, exactement de la même manière que l'exemple "essayer un autre SMS" présenté ici.

Mettre l'API Dispatch sous contrôle

Pour ajouter ce niveau supplémentaire, vous utilisez l'API Dispatch en plus de l'API Messages. Dans l'exemple d'application, vous pouvez voir cela en action dans l'action /message-with-dispatch qui se trouve derrière le lien "Send message with fallback" que vous avez remarqué dans l'interface web de l'exemple d'application. Le code ne change pas beaucoup, mais la requête que nous envoyons présente quelques différences :

    $apiResponse = $client->request('POST', '/v0.1/dispatch', [
        'headers' => [
            'Authorization' => 'Bearer ' . $config['jwt'],
            'Content-Type' => 'application/json',
            'Accept' => 'application/json'
        ],
        'json' => [
            'template' => 'failover',
            'workflow' => [
                [
                    'from' => $config['from'],
                    'to' => $config['customer1'][0],
                    'message' => [
                        'content' => [
                            'type' => 'text',
                            'text' => $message
                        ]
                    ],
                    'failover' => [
                        'expiry_time' => 15, // in seconds, 15 is the minimum
                        'condition_status' => 'delivered'
                    ]
                ],
                [
                    'from' => $config['from'],
                    'to' => $config['customer1'][1],
                    'message' => [
                        'content' => [
                            'type' => 'text',
                            'text' => 'Message retry. ' . $message
                        ]
                    ]
                ]
            ]
        ]
    ]);

La structure a changé, nous spécifions maintenant un template au niveau supérieur, puis un workflow qui est un tableau de méthodes de contact et de détails avec certains critères à utiliser par le modèle. Cet exemple utilise le modèle failover (actuellement la seule option) pour s'assurer que si le SMS n'est pas délivré dans les 15 secondes (ce n'est qu'une démo, combien de temps voulez-vous attendre ?), nous envoyons un autre message à l'autre numéro que nous avons pour ce client.

Suivre les mises à jour de l'état de la répartition

Tout comme l'API Messages, l'API Dispatch renvoie simplement un code d'état de 202 Accepted et un dispatch_uuid afin que nous puissions lier les événements relatifs à cette demande d'API.

Comme dans l'exemple précédent utilisant l'API Messages, vous recevez des mises à jour lorsque le message est soumis ainsi que lorsqu'il est délivré/lu ou lorsque l'état de Dispatch change. Voici la séquence de données que je vois lorsque Dispatch API ne peut pas livrer le premier numéro de téléphone et se rabat sur le second.

Tout d'abord : soumettre le premier message au premier numéro.

{
    "message_uuid": "afb5f546-97e7-44ba-97cd-bb7706a93f4e",
    "to": {
        "number": "447700900001",
        "type": "sms"
    },
    "from": {
        "number": "	447700900000",
        "type": "sms"
    },
    "timestamp": "2018-10-19T11:33:07.118Z",
    "status": "submitted",
    "_links": {
        "dispatch": {
            "href": "v0.1/dispatch/de8d9eaf-8d10-407f-840b-53473f26c173",
            "dispatch_uuid": "de8d9eaf-8d10-407f-840b-53473f26c173"
        }
    }
}

Lorsqu'il ne peut pas le livrer (je teste en mettant le "premier" téléphone en mode avion), le même statut arrive à nouveau, mais avec le "deuxième" numéro de téléphone dans les données :

{
    "message_uuid": "e083fc2e-dffc-42c9-a7b3-446ee5fe67ba",
    "to": {
        "number": "447700900002",
        "type": "sms"
    },
    "from": {
        "number": "	447700900000",
        "type": "sms"
    },
    "timestamp": "2018-10-19T11:33:25.071Z",
    "status": "submitted",
    "_links": {
        "dispatch": {
            "href": "v0.1/dispatch/de8d9eaf-8d10-407f-840b-53473f26c173",
            "dispatch_uuid": "de8d9eaf-8d10-407f-840b-53473f26c173"
        }
    }
}

Le deuxième message a été délivré.

{
    "message_uuid": "e083fc2e-dffc-42c9-a7b3-446ee5fe67ba",
    "to": {
        "number": "447700900002",
        "type": "sms"
    },
    "from": {
        "number": "	447700900000",
        "type": "sms"
    },
    "timestamp": "2018-10-19T11:33:27.385Z",
    "status": "delivered",
    "usage": {
        "price": "0.0333",
        "currency": "EUR"
    },
    "_links": {
        "dispatch": {
            "href": "v0.1/dispatch/de8d9eaf-8d10-407f-840b-53473f26c173",
            "dispatch_uuid": "de8d9eaf-8d10-407f-840b-53473f26c173"
        }
    }
}

Si le premier message est également délivré plus tard, vous recevrez une mise à jour d'état très similaire, que je ne collerai pas non plus. En général, je le reçois environ dix minutes plus tard, lorsque je me rends compte que mon téléphone est toujours en mode avion...

Pour des statuts comme celui-ci, où la livraison du SMS marque également l'aboutissement de la demande d'envoi, vous pouvez constater que les webhooks entrants arrivent dans l'un ou l'autre ordre ; veillez donc à ne pas vous fier à celui qui arrive en premier.

Étant donné que la livraison du second message marque la réussite de l'envoi, il existe également une mise à jour de l'état "terminé" pour ce message :

{
    "template": "failover",
    "status": "completed",
    "timestamp": "2018-10-19T11:33:27.450Z",
    "usage": {
        "price": "0.0353",
        "currency": "EUR"
    },
    "dispatch_uuid": "de8d9eaf-8d10-407f-840b-53473f26c173",
    "_links": {
        "messages": [
            {
                "message_uuid": "afb5f546-97e7-44ba-97cd-bb7706a93f4e",
                "href": "v0.1/messages/afb5f546-97e7-44ba-97cd-bb7706a93f4e",
                "channel": "sms",
                "status": "submitted"
            },
            {
                "message_uuid": "e083fc2e-dffc-42c9-a7b3-446ee5fe67ba",
                "href": "v0.1/messages/e083fc2e-dffc-42c9-a7b3-446ee5fe67ba",
                "channel": "sms",
                "usage": {
                    "price": "0.0333",
                    "currency": "EUR"
                },
                "status": "delivered"
            }
        ]
    }
}

Étant donné que la livraison des messages peut prendre un certain temps, ces mises à jour asynchrones constituent un moyen essentiel d'interagir avec l'API. Dans cet exemple, vous configurez d'abord le point de terminaison /status et c'est quelque chose que je recommande à tout développeur travaillant avec ces API.

Messagerie multicanal et résiliente

Dans ce tutoriel, vous avez travaillé avec une application qui peut envoyer un message à un client à l'aide d'une API SMS simple qui offre également une intégration avec d'autres types de messages en utilisant le même code et les mêmes appels API (vous envoyez simplement des messages différents de from et to différentes). Vous avez également vu comment nous pouvons ajouter de la résilience en détectant si un message n'a pas atteint un client et en utilisant un autre canal pour le contacter.

Quelle est la prochaine étape ?

Si vous souhaitez en savoir plus sur ces API, voici quelques sites à visiter :

Partager:

https://a.storyblok.com/f/270183/250x250/e3d3b71060/lornajane.png
Lorna MitchellAnciens de Vonage

Lorna est une ingénieure en informatique qui a la manie incurable de bloguer. Elle tente d'apprivoiser les mots et le code à parts égales.