¿Quieres integrar WhatsApp a tu sistema de facturación (CFDI 4.0) para enviar facturas, comprobantes y recordatorios automáticos? Con WhatzMeAPI lo implementas rápido.
Escríbeme y te comparto demo + precios 👉 CONTÁCTANOS

Implementar, paso a paso, el envío y recepción de mensajes de WhatsApp desde un sistema de facturación/ERP (por ejemplo, CFDI 4.0 en México) para notificar a clientes sobre facturas emitidas, pagos registrados, cancelaciones y recordatorios de vencimiento. El ejemplo usa Laravel + MySQL del lado del sistema de facturación y un API REST de WhatsApp (p.ej., WhatzMeAPI) como canal de mensajería.

Nota: Las rutas y el payload del API son genéricos. Adáptalos a la especificación real de tu cuenta de WhatzMeAPI (nombres de endpoints, parámetros, headers, etc.).


Arquitectura de referencia

  1. ERP/Facturación (Laravel, PHP 8.3, MySQL 8) genera CFDI (XML/PDF) y dispara eventos de negocio.
  2. Conector WhatsApp (servicio interno en Laravel) publica mensajes al API REST de WhatzMeAPI.
  3. Webhooks: WhatzMeAPI envía entregas/lecturas/respuestas al Webhook del ERP.
  4. Almacenamiento: se guardan logs de mensajería y el estado de cada notificación.
[ERP/Facturación] --(HTTP POST)--> [WhatzMeAPI /messages]
          ^                                  |
          |                                  v
   [Webhook /whatsapp] <---(HTTP POST)--- [WhatzMeAPI status/inbound]

Casos de uso (gatillos de negocio)

Evento de facturaciónMomentoMensaje sugeridoTipo
CFDI emitidoInmediato“Hola {{nombre}}, tu factura {{folio}} por {{importe}} está lista.” + link PDF/XMLTexto + link / Documento
Pago recibidoInmediato“¡Gracias! Registramos tu pago de {{importe}} de la factura {{folio}}.”Texto
Recordatorio de pagoX días antes del vencimiento“Tu factura {{folio}} vence el {{fecha}}. ¿Necesitas apoyo para pagar?”Texto + Botones
CancelaciónInmediato“Se canceló la factura {{folio}}. Si no lo solicitaste, responde a este mensaje.”Texto

Sugerencia: registra en BD qué notificaciones ya fueron enviadas por factura para asegurar idempotencia.


Requisitos previos

  • Cuenta activa en WhatzMeAPI (credenciales, token/bearer, números aprobados y, si aplica, plantillas)
  • URL pública (HTTPS) para recibir webhooks
  • Laravel 10+ / PHP 8.2+ / MySQL 8
  • Librería HTTP (Laravel HTTP Client o Guzzle)

Diseño técnico

1) Mensajería saliente (ERP → WhatzMeAPI)

  • Endpoint genérico: POST {WHATZMEAPI_BASE_URL}/messages
  • Headers: Authorization: Bearer <TOKEN> / Content-Type: application/json
  • Body (ejemplo texto):
{
  "to": "+52XXXXXXXXXX",
  "type": "text",
  "text": { "body": "Hola Ana, tu factura F-123 por $1,250.00 MXN está lista: https://tudominio.com/f/F-123.pdf" },
  "client_ref": "F-123"
}
  • Body (ejemplo documento PDF por URL):
{
  "to": "+52XXXXXXXXXX",
  "type": "document",
  "document": {
    "link": "https://tudominio.com/f/F-123.pdf",
    "filename": "F-123.pdf"
  },
  "caption": "Factura F-123",
  "client_ref": "F-123"
}

client_ref te ayuda a correlacionar el mensaje con la factura en tu BD.

Obtén descuentos exclusivos de nuestros cursos en vivo en línea

Capacítate con los expertos

2) Webhooks entrantes (WhatzMeAPI → ERP)

  • URL: POST https://erp.tu-dominio.com/webhooks/whatsapp
  • Eventos típicos: message_status (sent, delivered, read, failed), message_inbound (respuesta del cliente)
  • Seguridad: valida firma/HMAC y origen, guarda solo campos necesarios (cumplimiento de privacidad).

3) Idempotencia y reintentos

  • Usa un message_id externo + client_ref para no duplicar registros.
  • Implementa reintento exponencial para fallas 5xx y corta en 3–5 intentos.

Implementación de ejemplo en Laravel

1) Variables de entorno

# .env
WHATZMEAPI_BASE_URL=https://api.whatzmeapi.com/v1
WHATZMEAPI_TOKEN=xxxxxx
WHATSAPP_DEFAULT_SENDER=+52XXXXXXXXXX

2) Configuración de servicio (opcional)

// config/services.php
return [
    'whatzmeapi' => [
        'base_url' => env('WHATZMEAPI_BASE_URL'),
        'token'    => env('WHATZMEAPI_TOKEN'),
        'sender'   => env('WHATSAPP_DEFAULT_SENDER'),
    ],
];

3) Servicio de integración

// app/Services/WhatsAppApiService.php

namespace App\Services;

use Illuminate\Support\Facades\Http;

class WhatsAppApiService
{
    private string $baseUrl;
    private string $token;

    public function __construct()
    {
        $this->baseUrl = config('services.whatzmeapi.base_url');
        $this->token   = config('services.whatzmeapi.token');
    }

    private function client()
    {
        return Http::withToken($this->token)
            ->acceptJson()
            ->asJson();
    }

    public function sendText(string $to, string $body, ?string $clientRef = null): array
    {
        $payload = [
            'to'   => $to,
            'type' => 'text',
            'text' => ['body' => $body],
        ];
        if ($clientRef) { $payload['client_ref'] = $clientRef; }

        $res = $this->client()->post($this->baseUrl.'/messages', $payload);
        return [$res->status(), $res->json()];
    }

    public function sendDocument(string $to, string $url, string $filename, ?string $caption = null, ?string $clientRef = null): array
    {
        $payload = [
            'to'       => $to,
            'type'     => 'document',
            'document' => [ 'link' => $url, 'filename' => $filename ],
        ];
        if ($caption)   { $payload['caption']    = $caption; }
        if ($clientRef) { $payload['client_ref'] = $clientRef; }

        $res = $this->client()->post($this->baseUrl.'/messages', $payload);
        return [$res->status(), $res->json()];
    }
}

4) Tabla de logs

// database/migrations/2025_10_13_000000_create_whatsapp_logs_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
    public function up(): void
    {
        Schema::create('whatsapp_logs', function (Blueprint $table) {
            $table->id();
            $table->string('client_ref')->index();  // Folio de factura
            $table->string('to', 20)->index();
            $table->string('type', 20);
            $table->json('payload');
            $table->string('provider_message_id')->nullable()->index();
            $table->string('status', 30)->default('created'); // created|sent|delivered|read|failed
            $table->json('provider_response')->nullable();
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('whatsapp_logs');
    }
};

5) Envío al emitir CFDI

// app/Listeners/InvoiceIssuedListener.php

namespace App\Listeners;

use App\Events\InvoiceIssued; // Evento propio cuando se emite la factura
use App\Services\WhatsAppApiService;
use App\Models\WhatsAppLog;

class InvoiceIssuedListener
{
    public function __construct(private WhatsAppApiService $whats) {}

    public function handle(InvoiceIssued $event): void
    {
        $invoice = $event->invoice; // contiene folio, pdf_url, total, customer
        $to      = $invoice->customer->whatsapp_e164; // "+52..."
        $folio   = $invoice->folio;
        $body    = "Hola {$invoice->customer->first_name}, tu factura {$folio} por $".
                   number_format($invoice->total, 2)." MXN está lista: {$invoice->pdf_url}";

        [$status, $json] = $this->whats->sendText($to, $body, clientRef: $folio);

        WhatsAppLog::create([
            'client_ref'        => $folio,
            'to'                => $to,
            'type'              => 'text',
            'payload'           => json_encode(['body' => $body]),
            'provider_message_id'=> $json['id'] ?? null,
            'status'            => $status === 200 ? 'sent' : 'failed',
            'provider_response' => json_encode($json),
        ]);

        // Enviar PDF como documento (opcional)
        if ($invoice->pdf_url) {
            [$s2, $j2] = $this->whats->sendDocument($to, $invoice->pdf_url, $folio.'.pdf', 'Factura '.$folio, $folio);
            WhatsAppLog::create([
                'client_ref'        => $folio,
                'to'                => $to,
                'type'              => 'document',
                'payload'           => json_encode(['link' => $invoice->pdf_url]),
                'provider_message_id'=> $j2['id'] ?? null,
                'status'            => $s2 === 200 ? 'sent' : 'failed',
                'provider_response' => json_encode($j2),
            ]);
        }
    }
}

6) Webhook de estatus y respuestas

// routes/api.php
use App\Http\Controllers\WhatsAppWebhookController;
Route::post('/webhooks/whatsapp', [WhatsAppWebhookController::class, 'handle']);
// app/Http/Controllers/WhatsAppWebhookController.php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;

class WhatsAppWebhookController extends Controller
{
    public function handle(Request $request)
    {
        // 1) Validar firma/HMAC si el proveedor la envía en headers
        // 2) Parsear payload según contrato real de WhatzMeAPI
        $eventType = $request->input('event'); // message_status | message_inbound

        if ($eventType === 'message_status') {
            $providerId = $request->input('id');
            $status     = $request->input('status'); // sent|delivered|read|failed
            DB::table('whatsapp_logs')->where('provider_message_id', $providerId)->update([
                'status' => $status,
                'provider_response' => json_encode($request->all()),
                'updated_at' => now(),
            ]);
        }

        if ($eventType === 'message_inbound') {
            $from  = $request->input('from');
            $text  = $request->input('text.body');
            Log::info('Inbound WhatsApp', ['from' => $from, 'text' => $text]);
            // Aquí puedes abrir un ticket, marcar seguimiento, o responder vía API
        }

        return response()->json(['ok' => true]);
    }
}

Pruebas rápidas con cURL

Enviar texto

curl -X POST "$WHATZMEAPI_BASE_URL/messages" \
 -H "Authorization: Bearer $WHATZMEAPI_TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "to": "+52XXXXXXXXXX",
   "type": "text",
   "text": {"body": "Hola, tu factura F-123 está lista: https://tudominio.com/f/F-123.pdf"},
   "client_ref": "F-123"
 }'

Enviar documento (PDF por URL)

curl -X POST "$WHATZMEAPI_BASE_URL/messages" \
 -H "Authorization: Bearer $WHATZMEAPI_TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "to": "+52XXXXXXXXXX",
   "type": "document",
   "document": {"link": "https://tudominio.com/f/F-123.pdf", "filename": "F-123.pdf"},
   "caption": "Factura F-123",
   "client_ref": "F-123"
 }'

Buenas prácticas

  • Consentimiento y políticas de Meta/WhatsApp: usa plantillas aprobadas cuando aplique (mensajes proactivos fuera de la ventana de 24h).
  • Seguridad: tokens en variables de entorno; valida HMAC de webhooks; usa HTTPS; evita exponer XML/PDF sin control de acceso.
  • Idempotencia: clave única por client_ref + provider_message_id.
  • Observabilidad: logs estructurados, métricas (intentos, entregas, lecturas, fallas), dashboards.
  • Retención: define TTL de logs y sanitiza datos personales conforme a tu política de privacidad.
  • Resiliencia: cola de trabajos (Jobs) y reintentos exponenciales para envíos fallidos.

Checklist de salida a producción

  • Número/s y plantillas aprobados en WhatzMeAPI.
  • Variables de entorno y secretos cargados correctamente.
  • Webhook público con validación de firma/HMAC.
  • Migraciones y rotación de logs en BD.
  • Pruebas E2E: emisión de factura → mensaje enviado → status recibido → documento accesible.
  • Monitoreo/alertas configuradas.

Próximos pasos (ideas de mejora)

  • Botones de pago en el mensaje (p.ej., URL a pasarela) y confirmación automática.
  • Respuestas automáticas (NLP) a dudas frecuentes sobre facturas y pagos.
  • Integración con tu CRM para seguimiento postventa.
  • Segmentación: campañas de cobranza y recordatorios inteligentes.

Integra WhatsApp a tu facturación con WhatzMeAPI: envía PDF de facturas, avisos de pago y recordatorios directo desde tu ERP vía API REST.
Conoce cómo funciona y solicita una demo aquí 👉 https://www.whatzmeapi.com/

About Author

Giss Trejo

0 0 votos
Article Rating
Suscribir
Notificar de
guest
0 Comments
La mas nueva
Más antiguo Más votada
Comentarios.
Ver todos los comentarios
0
¿Te gusta este articulo? por favor comentax