Cómo enviar SMS con Laravel y Twilio en producción

5 de abril de 2024 • 6 min de lectura

Inicio / Blog / Cómo enviar SMS con Laravel y Twilio en producción

Sobre el autor

Author

Gonzalo Gomez

Especialista en IA y Automatización

Diseño sistemas de comunicación impulsados por IA. Mi trabajo se centra en agentes de voz, chatbots de WhatsApp, asistentes de IA y automatización de flujos, construidos principalmente sobre Twilio, n8n y LLM modernos como OpenAI y Claude. En los últimos 7 años he entregado más de 30 proyectos de automatización que manejan más de 250 000 interacciones mensuales.

Suscríbete a mi newsletter

Si te gusta el contenido que hago, puedes suscribirte y recibir información valiosa por correo. No se envía spam, solo novedades sobre publicaciones interesantes o contenido especializado del que hablo.

Cómo enviar SMS con Laravel y Twilio en producción | Aprendé a enviar SMS con Laravel y Twilio: instalación del SDK, un servicio desacoplado y la implementación real de una app que manda 3000 mensajes por día.

Introducción

En un capítulo anterior armé una guía con mis consejos para verificar tu número 10DLC en Twilio. Lo que nunca hice fue la guía de cómo enviar SMS con Laravel desde tu propia plataforma. Twilio ofrece implementaciones cortas en varios lenguajes para mandar el mensaje, y está bien como punto de partida. Yo quiero ir un paso más allá y mostrarte mi implementación en una app real que maneja alrededor de 3000 mensajes por día. Vamos a eso.

 

Nota: para enviar SMS desde un número 10DLC en producción, necesitás una campaña A2P 10DLC aprobada y activa. Mirá https://ggomez.dev/blog/twilio-create-an-a2p-10dlc-campaign para más información.

 

Además, si necesitás enviar SMS desde un número Toll-Free, vas a tener que pasar por la Toll-Free Message Verification dentro de Twilio. Mirá https://help.twilio.com/articles/5377174717595-Toll-Free-Message-Verification-for-US-Canada para más información.

 

Para empezar

Hay 2 formas de usar Twilio en nuestra app: a través de sus SDK o de su API. Esta guía usa el SDK, que ya se comunica con la API de Twilio por nosotros.

 

En tu aplicación de Laravel (o de PHP, para el caso), instalá el SDK de Twilio

composer require twilio/sdk

 

Después todo se reduce a usar el método send() del cliente de Twilio, que necesitamos instanciar dentro de nuestra app. A mí me gusta tener toda la lógica repartida en servicios y usar el patrón de Inyección de Dependencias cuando construyo en Laravel. Veamos entonces cómo usar el SDK de Twilio en nuestro servicio:

 

app\Services\TwilioMessageService.php

<?php

namespace App\Services;

use App\Models\TwilioSmsLog;
use Error;
use Exception;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Log;
use Twilio\Rest\Api\V2010\Account\MessageInstance;
use Twilio\Rest\Client;

class TwilioMessageService
{
    public function send(Model $model, string $message)
    {
        $client = new Client(config('services.twilio.account_sid'), config('services.twilio.auth_token'));

        $twilioMessage = $client->messages->create(
            $model->PhoneNumberE164,
            array(
                'from' => config('services.twilio.from'),
                'body' => $message
            )
        );

        $this->saveLogs($twilioMessage);
    }

    protected function saveLogs(mixed $message)
    {
        try {
            if (!$message instanceof MessageInstance) {
                throw new Error("The message is not an instance of MessageInstance: {$message}");
            }

            TwilioSmsLog::create([
                'sid' => $message->sid,
                'body' => $message->body,
                'direction' => $message->direction,
                'from' => $message->from,
                'to' => $message->to,
                'dateCreated' => $message->dateCreated,
                'dateUpdated' => $message->dateUpdated,
                'price' => $message->price,
                'accountSid' => $message->accountSid,
                'status' => $message->status,
                'priceUnit' => $message->priceUnit,
                'apiVersion' => $message->apiVersion
            ]);
        } catch (Exception $ex) {
            Log::error("Something went wrong storing the SMS logs: {$ex->getMessage()} at {$ex->getLine()}");
        }
    }
}

 

Ese es nuestro servicio básico, y se puede extender para aprovechar más funcionalidades del SDK. Ahora podemos crear un servicio de SMS que va a interactuar con nuestro servicio de Twilio:

 

app\Services\SmsService.php

<?php

namespace App\Services;

use App\Models\AppointmentMessage;
use App\Services\TwilioMessageService;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Blade;

class SmsService
{
    public TwilioMessageService $twilioMessageService;
    public VonageService $vonageService;

    public function __construct(TwilioMessageService $twilioMessageService, VonageService $vonageService)
    {
        $this->twilioMessageService = $twilioMessageService;
        $this->vonageService = $vonageService;
    }

    /**
     * Send an SMS to either a customer or a lead model.
     * The type of message to send is determined by the type parameter.
     * 
     * @return void
     */
    public function send(string $type, Model $model)
    {
        $appointmentMessage = AppointmentMessage::where('type', $type)->first();

        if ($appointmentMessage) {
            $data = $this->mapMessageVariables($type, $model);
            $message = Blade::render($appointmentMessage->message, $data, true);

            $this->twilioMessageService->send($model, $message);
        }
    }

    /**
     * Send a raw message to a customer or a lead model.
     * 
     * @return void
     */
    public function sendRaw(Model $model, string $message)
    {
        $this->twilioMessageService->send($model, $message);
    }

    /**
     * Map the message variables to send in the SMS.
     * 
     * @return array
     */
    protected function mapMessageVariables(string $type, Model $model): array
    {
        switch ($type) {
            case 'created':
                return [
                    'clientName' => "{$model->Name} {$model->Surname}",
                    'date' => date('m/d/Y', strtotime($model->AppointmentDate)),
                    'time' => Carbon::createFromFormat('H:i:s', $model->AppointmentTime)->format('g:iA'),
                    'timezone' => $model->office->availabilityProfile->Timezone,
                    'officeAddress' => $model->office->address,
                    'officePhoneNumber' => $model->office->PhoneNumber
                ];
            break;

            case 'updated':
                return [
                    'clientName' => "{$model->Name} {$model->Surname}",
                    'date' => date('m/d/Y', strtotime($model->AppointmentDate)),
                    'time' => Carbon::createFromFormat('H:i:s', $model->AppointmentTime)->format('g:iA'),
                    'timezone' => $model->office->availabilityProfile->Timezone,
                    'officeAddress' => $model->office->address,
                    'officePhoneNumber' => $model->office->PhoneNumber
                ];
            break;

            case 'cancelled':
                return [
                    'clientName' => "{$model->Name} {$model->Surname}",
                    'date' => date('m/d/Y', strtotime($model->AppointmentDate)),
                    'timezone' => $model->office->availabilityProfile->Timezone,
                    'officePhoneNumber' => $model->office->PhoneNumber,
                    'time' => Carbon::createFromFormat('H:i:s', $model->AppointmentTime)->format('g:iA'),
                ];
            break;

            case 'existing_client':
                return [
                    'officeName' => $model->office->name,
                    'officePhoneNumber' => $model->office->PhoneNumber
                ];
            break;

            default:
                return [];
            break;
        }
    }
}

 

La idea acá es desacoplar el SmsService del TwilioService. En algún momento quizás quieras cambiar de proveedor de comunicaciones en la nube, y te conviene minimizar el impacto en el código. En este ejemplo el SmsService es el único punto de contacto de la app. Podemos alternar entre Twilio y Vonage sin que el lado del cliente se entere de esos cambios.

 

Conclusión

Según tu arquitectura, quizás te convenga encolar los mensajes antes de mandárselos a Twilio, por ejemplo en batches. Yo no usé ese enfoque: prefiero que Twilio me encole los mensajes de su lado, porque no necesito que se entreguen al instante. Igual, el throughput (mensajes procesados por segundo) se define según tu campaña A2P 10DLC.

 

Eso es todo por ahora, si te sirvió, compartilo.

-Gonza

1509
Laravel,  Twilio
Publicado el 5 de abril de 2024

Descubre cuánto te está costando tu sistema de comunicaciones.

Obtén la auditoría de comunicaciones

Posts relacionados

No se encontraron resultados