¿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
- ERP/Facturación (Laravel, PHP 8.3, MySQL 8) genera CFDI (XML/PDF) y dispara eventos de negocio.
- Conector WhatsApp (servicio interno en Laravel) publica mensajes al API REST de WhatzMeAPI.
- Webhooks: WhatzMeAPI envía entregas/lecturas/respuestas al Webhook del ERP.
- 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ón | Momento | Mensaje sugerido | Tipo |
---|---|---|---|
CFDI emitido | Inmediato | “Hola {{nombre}}, tu factura {{folio}} por {{importe}} está lista.” + link PDF/XML | Texto + link / Documento |
Pago recibido | Inmediato | “¡Gracias! Registramos tu pago de {{importe}} de la factura {{folio}}.” | Texto |
Recordatorio de pago | X días antes del vencimiento | “Tu factura {{folio}} vence el {{fecha}}. ¿Necesitas apoyo para pagar?” | Texto + Botones |
Cancelación | Inmediato | “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.
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/