Envio de SMS desde tu App: Tutorial con Codigo

Introduccion

Integrar el envio de SMS en tu aplicacion es una de las funcionalidades mas utiles que puedes agregar. Desde verificacion de usuarios hasta notificaciones de pedidos, los SMS siguen siendo el canal de comunicacion mas confiable y universal.

En este tutorial aprenderemos a enviar SMS desde cualquier aplicacion con ejemplos practicos de codigo.

Requisitos Previos

  • Conocimientos basicos de programacion
  • Una cuenta en un proveedor de SMS API
  • Tu lenguaje de programacion preferido (mostraremos ejemplos en varios)

Arquitectura Basica

Tu Aplicacion
      |
      v
   API REST  ---> Proveedor SMS ---> Operador ---> Usuario
      |
      v
  Webhook (estado de entrega)

Configuracion Inicial

Paso 1: Obtener Credenciales

Registrate en un proveedor de SMS para desarrolladores y obtén tu API key.

Paso 2: Instalar SDK

Node.js:

npm install @zavudev/sdk

Python:

pip install zavudev

Go:

go get github.com/zavudev/zavu-go

Enviando tu Primer SMS

JavaScript/Node.js

import Zavudev from '@zavudev/sdk';

const zavu = new Zavudev({
  apiKey: process.env.ZAVUDEV_API_KEY
});

async function enviarSMS() {
  try {
    const mensaje = await zavu.messages.send({
      to: '+573001234567',
      text: 'Hola desde mi aplicacion!'
    });

    console.log('SMS enviado:', mensaje.id);
    console.log('Estado:', mensaje.status);

    return mensaje;
  } catch (error) {
    console.error('Error:', error.message);
    throw error;
  }
}

enviarSMS();

Python

from zavudev import Zavudev
import os

zavu = Zavudev(api_key=os.environ['ZAVUDEV_API_KEY'])

def enviar_sms():
    try:
        mensaje = zavu.messages.send(
            to='+573001234567',
            text='Hola desde mi aplicacion!'
        )

        print(f'SMS enviado: {mensaje.id}')
        print(f'Estado: {mensaje.status}')

        return mensaje

    except Exception as e:
        print(f'Error: {e}')
        raise

enviar_sms()

Go

package main

import (
    "fmt"
    "os"
    "github.com/zavudev/zavu-go"
)

func main() {
    client := zavu.NewClient(os.Getenv("ZAVUDEV_API_KEY"))

    mensaje, err := client.Messages.Send(&zavu.MessageParams{
        To:   "+573001234567",
        Text: "Hola desde mi aplicacion!",
    })

    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }

    fmt.Printf("SMS enviado: %s\n", mensaje.ID)
    fmt.Printf("Estado: %s\n", mensaje.Status)
}

PHP

<?php
require 'vendor/autoload.php';

use Zavu\Client;

$client = new Client(getenv('ZAVUDEV_API_KEY'));

try {
    $mensaje = $client->messages->send([
        'to' => '+573001234567',
        'text' => 'Hola desde mi aplicacion!'
    ]);

    echo "SMS enviado: " . $mensaje->id . "\n";
    echo "Estado: " . $mensaje->status . "\n";

} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . "\n";
}

cURL (API REST Directa)

curl -X POST https://api.zavu.dev/v1/messages \
  -H "Authorization: Bearer $ZAVUDEV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+573001234567",
    "text": "Hola desde mi aplicacion!"
  }'

Casos de Uso Practicos

1. Verificacion de Usuario

const crypto = require('crypto');

class VerificacionSMS {
  constructor(client) {
    this.client = client;
    this.codigos = new Map(); // En produccion usar Redis
  }

  generarCodigo() {
    return crypto.randomBytes(3).toString('hex').toUpperCase().slice(0, 6);
  }

  async enviarCodigo(telefono) {
    const codigo = this.generarCodigo();

    // Guardar codigo (expira en 5 min)
    this.codigos.set(telefono, {
      codigo,
      expira: Date.now() + 5 * 60 * 1000
    });

    // Enviar SMS
    await this.zavu.messages.send({
      to: telefono,
      text: `Tu codigo de verificacion es: ${codigo}. Valido por 5 minutos.`
    });

    return { enviado: true };
  }

  verificar(telefono, codigoIngresado) {
    const datos = this.codigos.get(telefono);

    if (!datos) {
      return { valido: false, error: 'Codigo no encontrado' };
    }

    if (Date.now() > datos.expira) {
      this.codigos.delete(telefono);
      return { valido: false, error: 'Codigo expirado' };
    }

    if (datos.codigo !== codigoIngresado) {
      return { valido: false, error: 'Codigo incorrecto' };
    }

    this.codigos.delete(telefono);
    return { valido: true };
  }
}

// Uso
const verificacion = new VerificacionSMS(client);
await verificacion.enviarCodigo('+573001234567');
const resultado = verificacion.verificar('+573001234567', 'ABC123');

2. Notificaciones de Pedidos

class NotificacionesPedidos {
  constructor(client) {
    this.client = client;
  }

  async notificarEstado(pedido) {
    const mensajes = {
      confirmado: `Pedido #${pedido.id} confirmado! Total: ${pedido.total}. Preparando tu orden.`,
      enviado: `Tu pedido #${pedido.id} ha sido enviado! Seguimiento: ${pedido.tracking}`,
      entregado: `Pedido #${pedido.id} entregado! Gracias por tu compra.`
    };

    const texto = mensajes[pedido.estado];
    if (!texto) return;

    await this.zavu.messages.send({
      to: pedido.cliente.telefono,
      text: texto
    });
  }

  async recordatorioPago(pedido) {
    await this.zavu.messages.send({
      to: pedido.cliente.telefono,
      text: `Recordatorio: Tu pedido #${pedido.id} espera pago de ${pedido.total}. Completa tu compra en: ${pedido.urlPago}`
    });
  }
}

3. Alertas y Monitoreo

class AlertasSistema {
  constructor(client, numeroAdmin) {
    this.client = client;
    this.numeroAdmin = numeroAdmin;
  }

  async alertaCritica(servicio, mensaje) {
    await this.zavu.messages.send({
      to: this.numeroAdmin,
      text: `[ALERTA CRITICA] ${servicio}: ${mensaje}`
    });
  }

  async reporteDiario(stats) {
    const resumen = `
Reporte Diario:
- Usuarios activos: ${stats.usuarios}
- Transacciones: ${stats.transacciones}
- Errores: ${stats.errores}
    `.trim();

    await this.zavu.messages.send({
      to: this.numeroAdmin,
      text: resumen
    });
  }
}

// Uso con monitoreo
const alertas = new AlertasSistema(client, '+573001234567');

// En tu sistema de monitoreo
if (cpuUsage > 90) {
  await alertas.alertaCritica('Servidor', 'CPU al 90%');
}

4. Recordatorios Automaticos

const cron = require('node-cron');

class Recordatorios {
  constructor(client, database) {
    this.client = client;
    this.db = database;
  }

  iniciar() {
    // Recordatorios de citas cada hora
    cron.schedule('0 * * * *', () => this.enviarRecordatoriosCitas());

    // Recordatorios de renovacion cada dia a las 9 AM
    cron.schedule('0 9 * * *', () => this.enviarRecordatoriosRenovacion());
  }

  async enviarRecordatoriosCitas() {
    const enUnaHora = new Date(Date.now() + 60 * 60 * 1000);
    const citas = await this.db.citas.find({
      fecha: { $lte: enUnaHora, $gt: new Date() },
      recordatorioEnviado: false
    });

    for (const cita of citas) {
      await this.zavu.messages.send({
        to: cita.paciente.telefono,
        text: `Recordatorio: Tu cita es en 1 hora con ${cita.doctor}. Direccion: ${cita.direccion}`
      });

      await this.db.citas.update({ _id: cita._id }, { recordatorioEnviado: true });
    }
  }

  async enviarRecordatoriosRenovacion() {
    const enUnaSemana = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
    const suscripciones = await this.db.suscripciones.find({
      fechaRenovacion: { $lte: enUnaSemana },
      recordatorioEnviado: false
    });

    for (const sub of suscripciones) {
      await this.zavu.messages.send({
        to: sub.usuario.telefono,
        text: `Tu suscripcion vence el ${sub.fechaRenovacion.toLocaleDateString()}. Renueva ahora para mantener acceso.`
      });

      await this.db.suscripciones.update({ _id: sub._id }, { recordatorioEnviado: true });
    }
  }
}

Manejo de Webhooks

Configurar Endpoint

const express = require('express');
const crypto = require('crypto');

const app = express();

// Verificar firma del webhook
function verificarFirma(req, secret) {
  const firma = req.headers['x-webhook-signature'];
  const esperada = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(req.body))
    .digest('hex');

  return firma === esperada;
}

app.post('/webhooks/sms', express.json(), async (req, res) => {
  // Verificar autenticidad
  if (!verificarFirma(req, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Firma invalida');
  }

  const { event, data } = req.body;

  switch (event) {
    case 'message.delivered':
      console.log(`Mensaje ${data.messageId} entregado`);
      await actualizarEstadoMensaje(data.messageId, 'entregado');
      break;

    case 'message.failed':
      console.log(`Mensaje ${data.messageId} fallo: ${data.errorMessage}`);
      await manejarFallo(data);
      break;

    case 'message.inbound':
      console.log(`Mensaje recibido de ${data.from}: ${data.text}`);
      await procesarMensajeEntrante(data);
      break;
  }

  res.sendStatus(200);
});

async function procesarMensajeEntrante(data) {
  // Manejar respuestas automaticas
  const texto = data.text.toLowerCase();

  if (texto === 'stop' || texto === 'baja') {
    await darDeBaja(data.from);
  } else if (texto === 'ayuda') {
    await zavu.messages.send({
      to: data.from,
      text: 'Comandos: STOP para darte de baja, AYUDA para ver opciones.'
    });
  }
}

Mejores Practicas

1. Validacion de Numeros

const { parsePhoneNumber, isValidPhoneNumber } = require('libphonenumber-js');

function validarYFormatear(numero, paisDefault = 'CO') {
  try {
    const parsed = parsePhoneNumber(numero, paisDefault);

    if (!parsed.isValid()) {
      return { valido: false, error: 'Numero invalido' };
    }

    return {
      valido: true,
      formateado: parsed.format('E.164'),
      pais: parsed.country
    };
  } catch (e) {
    return { valido: false, error: 'No se pudo parsear' };
  }
}

2. Rate Limiting

const rateLimit = require('express-rate-limit');

const smsLimiter = rateLimit({
  windowMs: 60 * 1000, // 1 minuto
  max: 10, // 10 SMS por minuto
  message: { error: 'Demasiadas solicitudes' }
});

app.post('/api/sms', smsLimiter, enviarSMSHandler);

3. Retry con Backoff

async function enviarConRetry(params, maxIntentos = 3) {
  for (let intento = 1; intento <= maxIntentos; intento++) {
    try {
      return await zavu.messages.send(params);
    } catch (error) {
      if (error.code === 'RATE_LIMITED' && intento < maxIntentos) {
        await sleep(Math.pow(2, intento) * 1000);
        continue;
      }
      throw error;
    }
  }
}

Conclusion

Enviar SMS desde tu aplicacion es mas facil que nunca con las APIs modernas para desarrolladores. Ya sea para verificacion de usuarios, notificaciones o alertas, los SMS siguen siendo el canal mas confiable para comunicacion critica.

Recuerda siempre validar numeros, implementar rate limiting y manejar errores apropiadamente para crear una experiencia robusta.

Recursos

sebastianosorio6

Blog sobre actualidad.

También te podría gustar...