
Compartilhar:
Lorna é engenheira de software e tem um vício incurável por escrever em blogs. Ela tenta domar as palavras e o código na mesma medida.
Envio de SMS a partir do PHP com failover: A Padaria Cupcake
Tempo de leitura: 12 minutos
Manter contato com os clientes é fundamental para qualquer organização. Nesta postagem, vamos explorar a Messages API da Vonage e como ela pode ser usada para garantir que uma empresa envie mensagens aos seus clientes da maneira que melhor lhes convier. O estudo de caso é uma empresa fictícia chamada “Cupcake Bakery”, que precisa enviar mensagens aos seus clientes. Este post mostra como usar o pilar da comunicação moderna, o SMS, em sua aplicação web em PHP. Ele também demonstra como a Messages API pode utilizar outros canais de comunicação para enviar sua mensagem caso a primeira tentativa falhe, a fim de oferecer a você a melhor chance de alcançar seu cliente.
Se você é um desenvolvedor web pronto para implementar recursos modernos de mensagens em seu aplicativo, então este tutorial é para você!
Prepare os ingredientes
Antes de começarmos, precisamos reunir os seguintes ingredientes:
PHP em um servidor web acessível ao público ou PHP em uma plataforma de desenvolvimento com uma ferramenta como o ngrok para disponibilizar seu site. A Vonage envia respostas por meio de webhook , portanto, ele precisa conseguir acessar seu aplicativo.
CLI da Vonage caso você ainda não o tenha.
É mais divertido se você tiver dois números de telefone para os quais possa enviar SMS :)
Conheça a Messages API
A Vonage é conhecida há muito tempo por seus recursos de SMS, mas a Messages API é nova (ainda está em fase beta). Com a Messages API, você pode enviar mensagens para diversos canais diferentes usando uma única interface. Muitas vezes, é mais barato enviar uma mensagem pelo Facebook Messenger ou pelo WhatsApp do que por SMS, e a Messages API significa que você só precisa integrar uma única ferramenta para cobrir todas essas opções. Além disso, mais canais de mensagens serão adicionados com o tempo, portanto, esse é um investimento que evita que você tenha que adicionar mais integrações a cada nova plataforma de mensagens que surgir.
Complementando a Messages API está a Dispatch API, que oferece maior confiabilidade à tarefa de entregar a mensagem ao destinatário pretendido. Com a Dispatch API, você pode definir regras sobre o que fazer caso uma mensagem não seja entregue dentro de um determinado intervalo de tempo — e também determinar o que fazer em seguida. Portanto, se você tiver outro meio de contato para esse usuário — um número de telefone alternativo, por exemplo — ou se já tiver interagido com ele no Facebook, poderá enviar uma segunda mensagem por outro meio. Esta postagem mostra um exemplo de envio para um número de telefone alternativo, um recurso que muitas vezes eu mesmo gostaria de ter quando uso números diferentes em países distintos durante minhas viagens!
Configure seus pontos de conexão de Webhook
Acesse a seção página “Mensagens e Despacho” no painel para criar um aplicativo e configurar os webhooks que usaremos neste projeto:
A URL de status deve ser
[YOUR URL HERE]/statuspor exemplo, com o ngrok, seria algo comohttps://abcdef1.ngrok.io/status.A URL da mensagem recebida deve ser
[YOUR URL HERE]/inboundpor exemplo, com o ngrok, seria algo comohttps://abcdef1.ngrok.io/inbound.
Essas duas rotas, /status e /inbound, podem receber qualquer nome que você quiser, mas os exemplos aqui correspondem ao que está no código de exemplo que você usará do GitHub daqui a pouco...
No painel, anote o ID do aplicativo que você criou e certifique-se de que também tenha o arquivo da chave privada (há uma função prática do tipo “clique para criar uma chave” que gera um par de chaves pública/privada, inserindo a chave pública nas configurações do seu aplicativo e baixando a chave privada para você usar com seu aplicativo)
Pré-aqueça o código
(Será que estamos levando essa metáfora culinária longe demais? Desculpem!)
O código-fonte de um aplicativo funcional está disponível no GitHub. Acesse https://github.com/nexmo-community/bakery-messaging-with-dispatch e clone o repositório ou baixe o código.
Depois de instalá-lo, há algumas dependências que precisamos instalar. Para manter as coisas o mais simples possível, este projeto utiliza o microframework Slim. Para fazer as chamadas de API (já que as APIs de Messaging e Dispatch API ainda estão em fase Beta, elas não são suportadas em nossa biblioteca PHP ), o projeto utiliza o GuzzleHTTP.
Para instalar as dependências, use o Composer:
composer installA Messages API e a Dispatch API utilizam JSON Web Tokens (JWTs) para autenticação. Pegue o ID do aplicativo que você criou no painel e use-o com a CLI da Vonage para executar um comando como este (supondo que sua chave privada se chame private.key):
vonage jwt --application_id=VONAGE_APPLICATION_IDO resultado desse comando é o seu JWT, que você usará para acessar este aplicativo; copie-o para a área de transferência agora. Lembre-se de que ele expira a cada 24 horas, portanto, talvez seja necessário repetir esse processo quando o seu aplicativo, que estava funcionando perfeitamente, de repente começar a exibir erros do tipo “Token inválido”.
O aplicativo precisa do JWT, do ID do aplicativo e também de alguns dados de contato para as mensagens que enviará. Há um modelo de configuração em config.php.sample; copie esse arquivo e nomeie-o config.php, depois edite os valores de acordo com a sua plataforma. Você precisará de:
O ID do aplicativo, mais uma vez.
O JWT que você acabou de gerar.
O número de telefone para enviar mensagens de.
Dois números de telefone para enviar mensagens para.
Depois disso, os ingredientes já estão prontos e podemos começar a preparar algo incrível!
Crie uma excelente comunicação com o cliente
Neste momento, os preparativos estão concluídos e o aplicativo está pronto para uso. Configure seu servidor web com o public/ diretório como raiz da web. Estou usando uma plataforma de desenvolvimento local com o ngrok, então meus comandos de configuração são php -S localhost:8080 public/index.php em um terminal e ngrok http 8080 em outro.
Se o Ngrok fornecer uma nova URL (não é possível reservar URLs em um Account gratuito), não se esqueça de voltar ao painel e atualizar essas URLs de webhook
Agora vamos carregar a página inicial do projeto. Você deverá ver um formulário bem simples para enviar uma mensagem. Antes disso, vamos dedicar um momento para entender como isso vai funcionar.
Entendendo o processo da Messages API
O processo de envio de uma mensagem com a Messages API funciona da seguinte maneira:
Nós escrevemos a mensagem! Ela vai para o formulário de envio que você pode ver na página inicial.
Enviamos os detalhes do número de telefone do qual a mensagem está de onde, o número para o qual ela está sendo destinado e a própria mensagem, no formato JSON. Confira a documentação da API para obter informações detalhadas sobre o que você pode enviar aqui.
A resposta para uma mensagem enviada com sucesso é um código de status 202, que significa “Aceito”, e um
message_uuidcampo no corpo JSON da resposta.Todas as comunicações futuras da Nexmo serão feitas por meio de
POSTwebhooks de solicitação para o/statusponto de extremidade do nosso aplicativo. Cada solicitação recebida incluirá o ID da mensagem à qual o status se refere e um carimbo de data/hora. Isso é feito no formato JSON.Um webhook de status indica que a mensagem foi enviada. Há mais informações sobre os status das mensagens na documentação da API.
Outro webhook de status indica se a mensagem foi entregue (caso tenha sido) e quanto custou.
É claro que é muito importante que possamos ler as atualizações de status; portanto, vamos dar uma olhada no código relacionado a isso primeiro.
Gerenciar atualizações de status
Sempre que ocorrer algo interessante em relação ao status da mensagem, um webhook é enviado para o webhook que você configurou no painel no início. Para este aplicativo, é /status. Aqui está o código para essa rota:
$app->post('/status', function (Request $request, Response $response) {
error_log($request->getBody());
print_r($request->getParsedBody());
});
Nesta aplicação de exemplo, ela não faz praticamente nada, mas captura todas as respostas e as registra nos logs do seu servidor. Ela também as exibe, o que pode ser útil mais tarde, ao depurar alguns recursos mais avançados.
Envie sua mensagem ao cliente
Cupcake Bakery Custom Message
Nesse momento, fique à vontade para digitar uma mensagem na caixa. A minha diz “Seus cupcakes estão prontos para retirada”, porque sempre fico feliz em receber essa mensagem. Aperte “enviar” e a mensagem deve chegar rapidamente no seu celular. Foi mágica? Não, vamos dar uma olhada no código.
$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;
});
Este trajeto está disponível tanto para GET e POST verbos, pois a forma é carregada inicialmente com GET e, em seguida, quando é enviada, usamos POST. Se houver POST dados, então os dados da mensagem são usados juntamente com a configuração que você definiu anteriormente para construir uma solicitação de API. O código de status da resposta e o corpo da resposta são capturados e exibidos na página também, já que isso pode ser útil ao trabalhar com este aplicativo de demonstração ou ao adaptá-lo para criar o seu próprio. Usando essa saída de depuração, você pode obter o message_uuid da mensagem que você acabou de enviar.
Este exemplo utiliza o Slim Framework, mas a maior parte do código não é específica do Slim e funcionaria em qualquer aplicação PHP. Para saber mais sobre o Slim, acesse https://www.slimframework.com/ - Recomendo especialmente o tutorial tutorial “Primeiro Aplicativo”.
Acompanhar o andamento da mensagem
Como o /status endpoint já está configurado para receber os webhooks, você pode acessá-lo para verificar o que está acontecendo. Se você tiver enviado mais de uma mensagem, o ID da mensagem se torna ainda mais importante, mas, nesta fase, provavelmente já está claro a qual mensagem as atualizações de status se referem.
Se a mensagem for enviada com sucesso, você verá duas atualizações de status. A primeira apenas confirma que a mensagem foi enviada:
{
"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"
}Depois que a mensagem chega no meu celular, recebo uma segunda atualização de status com informações sobre a entrega da mensagem:
{
"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"
}
}Outros canais de mensagens, como o Facebook Messenger, também podem indicar o status “lido” para informar que o usuário realmente viu a mensagem.
Você também receberá atualizações de status caso haja erros na sua mensagem. Nesse caso, haverá um errors campo no nível superior dos dados JSON e detalhes do seu erro , incluindo um código e o motivo do erro. Fique de olho no /status ponto de extremidade enquanto trabalha com a Messages API, pois há muitas informações importantes ali que o ajudarão a desenvolver seus próprios aplicativos.
Use os dados de contato alternativos caso a mensagem não seja recebida
Essa é uma forma avançada de comunicação com o cliente: se a mensagem não chegar ao usuário, detecte isso e tente outros meios de contato que você tenha para esse usuário. O melhor disso tudo é que, embora os usuários geralmente prefiram o WhatsApp ou o Facebook Messenger (e essas opções possam ser mais baratas para você enviar), o SMS chega às pessoas de forma mais confiável, mesmo que elas tenham ficado sem dados ou estejam em uma área com sinal fraco. Como desenvolvedores, não precisamos nos esforçar para detectar o status das mensagens, adicionar lógica de repetição de tentativa ou criar código capaz de lidar com diversas plataformas de mensagens. A Dispatch API faz tudo isso por nós, então é muito, muito fácil.
Confira a documentação da Dispatch API para obter mais informações além deste exemplo específico
O aplicativo de exemplo usa um número de SMS alternativo (algo que costumo achar útil quando estou viajando, já que um plano de celular funciona melhor do que outro em alguns locais), mas você pode configurar vários detalhes de “remetente” e oferecer suporte a qualquer número de métodos de contato diferentes para um usuário, exatamente da mesma forma que no exemplo “try-another-SMS” mostrado aqui.
Coloque a Dispatch API no controle
Para adicionar essa camada extra, você usa a Dispatch API em conjunto com a Messages API. No aplicativo de exemplo, é possível ver isso em ação na /message-with-dispatch ação que está por trás do link “Enviar mensagem com fallback”, que você observou na interface web do aplicativo de exemplo. O código não muda muito, mas a solicitação que enviamos apresenta algumas diferenças:
$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
]
]
]
]
]
]);
A estrutura mudou; agora especificamos um template no nível superior e, em seguida, um workflow que é uma matriz de métodos e detalhes de contato com alguns critérios para o modelo a ser usado. Este exemplo usa o failover modelo (atualmente a única opção) para garantir que, se o SMS não for entregue em 15 segundos (é apenas uma demonstração, quanto tempo você quer esperar?), enviemos outra mensagem para o outro número que temos para esse cliente.
Acompanhe as atualizações do status da remessa
Exatamente como a Messages API, a Dispatch API simplesmente retorna um código de status de 202 Accepted e um dispatch_uuid para que possamos encerrar os eventos relacionados a essa solicitação da API.
Assim como no exemplo anterior que utilizava a Messages API, você recebe atualizações quando a mensagem é enviada, bem como quando ela é entregue/lida ou quando o status de envio é alterado. Aqui está a sequência de dados que vejo quando a Dispatch API não consegue entregar a mensagem no primeiro número de telefone e recorre ao segundo.
Antes de mais nada: envie a primeira mensagem para o primeiro número.
{
"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"
}
}
}Quando não consegue enviar (eu testo colocando o “primeiro” celular no modo avião), aparece exatamente o mesmo status novamente, mas com o “segundo” número de telefone nos dados:
{
"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"
}
}
}A segunda mensagem foi enviada, uhu!
{
"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"
}
}
}Se a primeira mensagem também for entregue mais tarde, você receberá uma atualização de status bem parecida; não vou colar essa também. Normalmente recebo essa notificação uns dez minutos depois, quando percebo que meu celular ainda está no modo avião...
Em casos como este, em que o envio do SMS também marca a conclusão bem-sucedida da solicitação de despacho, você pode perceber que os webhooks recebidos chegam em qualquer uma das duas ordens; portanto, tome cuidado para não se basear exatamente em qual deles chega primeiro.
Como a entrega bem-sucedida da segunda mensagem indica que o envio foi bem-sucedido, há também uma atualização de status para “concluído” nesse caso:
{
"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"
}
]
}
}Como a entrega das mensagens pode demorar algum tempo, essas atualizações assíncronas são uma forma essencial de interagir com a API. Neste exemplo, você configura primeiro o /status ponto de extremidade primeiro, e isso é algo que eu recomendaria a qualquer desenvolvedor que trabalhe com essas APIs.
Mensagens multicanal e resilientes
Neste tutorial, você trabalhou com um aplicativo capaz de enviar uma mensagem a um cliente por meio de uma SMS API simples, que também oferece integração com outros tipos de mensagem utilizando o mesmo código e as mesmas chamadas de API (basta enviar diferentes from e to dados). Você também viu como podemos aumentar a resiliência, detectando quando uma mensagem não chegou ao cliente e utilizando um canal alternativo para entrar em contato com ele.
E agora, para onde vamos?
Se você quiser explorar mais essas APIs, aqui estão alguns sites que talvez queira visitar a seguir:
A documentação da Messages API e da Dispatch API no portal do desenvolvedor
Tutorial detalhado Envio de mensagens SMS com a Messages API que inclui exemplos de código em NodeJS e cURL
Elementos básicos são trechos de código na sua linguagem de programação para realizar diversas tarefas com a Messages API.
Se precisar de nós, acesse o canal da Comunidade Nexmo no Slack