funnelchat20/wapi-gateway

WAPI Gateway: librería Composer para integrar ZApi, UAZAPI, Funapi y Meta con webhooks automáticos

Maintainers

Package info

github.com/Funnelchat20/wapi-gateway

pkg:composer/funnelchat20/wapi-gateway

Statistics

Installs: 217

Dependents: 0

Suggesters: 0

Stars: 0

v0.2.7 2026-03-18 21:31 UTC

README

Latest Version on Packagist Total Downloads

SDK puro para integrar proveedores de WhatsApp soportados por Funnelchat: Z-API, UAZAPI, Funapi (Whatsmeow Bridge) y Meta (WhatsApp Cloud), sin rutas ni middlewares. La app host consume métodos programáticos mediante un Facade y decide si expone endpoints propios.

⚠️ Estado: Esta librería está en desarrollo activo (v0.x.x). La API puede cambiar entre versiones menores. Se recomienda fijar versiones específicas en composer.json hasta v1.0.0.

Requisitos

  • PHP ^8.2
  • Laravel ^11.0 | ^12.0

Instalación

composer require funnelchat20/wapi-gateway

Publicar configuración

php artisan vendor:publish --tag=wapi-config

Configuración

Variables de entorno por proveedor

Global (Todos los proveedores)

# URL base para webhooks (requerida para recibir notificaciones)
WEBHOOK_BASE_URL=https://tu-app.com

Z-API

ZAPI_TOKEN=tu_token_aqui
ZAPI_CLIENT_TOKEN=tu_client_token
ZAPI_TIMEOUT=29
ZAPI_MAX_ATTEMPTS=2
ZAPI_RETRY_DELAY=500

UAZAPI

UAZAPI_BASE_URL=https://funnelchat.uazapi.com
UAZAPI_ADMIN_TOKEN=tu_admin_token
UAZAPI_TIMEOUT=120

# Opcional: deshabilitar configuración automática de webhooks
UAZAPI_AUTO_CONFIGURE_WEBHOOKS=true

Funapi (Whatsmeow Bridge)

FUNAPI_BASE_URL=https://api.funapi.example.com
FUNAPI_TOKEN=tu_token
FUNAPI_CLIENT_TOKEN=tu_client_token
FUNAPI_TIMEOUT=29
FUNAPI_MAX_ATTEMPTS=2
FUNAPI_RETRY_DELAY=500

Meta (WhatsApp Cloud)

META_APP_ID=tu_app_id
AWS_BUCKET_URL=https://tu-bucket.s3.amazonaws.com

Uso básico

use Funnelchat\WapiGateway\Facades\WapiGateway;
use Funnelchat\WapiGateway\Enums\ProviderEnum;

// Enviar mensaje de texto
$response = WapiGateway::messages(ProviderEnum::ZApi)
    ->sendText($uid, $token, $phone, 'Hola mundo', [
        'delayMessage' => 0,
        'delayTyping' => 0,
        'retry' => true,  // Habilita reintentos automáticos
    ]);

if (isset($response['error'])) {
    // Manejar error
}

// Crear instancia
$data = WapiGateway::instances(ProviderEnum::ZApi)->create($userId, $deviceId);
// $data = ['uid' => '...', 'token' => '...'] o ['error' => '...']

// Obtener estado
$status = WapiGateway::instances(ProviderEnum::ZApi)->status($uid, $token);
// Incluye 'accountStatus' y 'qrCode' automáticamente

Compatibilidad de Proveedores

📨 Mensajería

Método Z-API UAZAPI Funapi Meta
sendText()
sendFile()
sendLocation()
sendButtons() ⚠️
sendButtonLink() ⚠️
sendOptionList() ⚠️
sendPoll() ⚠️
sendLink() ⚠️
sendEvent() ⚠️
sendTemplate() ⚠️
pinMessage() ⚠️

🔧 Instancias

Método Z-API UAZAPI Funapi Meta
create()
status()
qrCode()
logout()
reboot()
me()
checkPhone()
subscribe()
unsubscribe()

👥 Grupos

Método Z-API UAZAPI Funapi Meta
groups()
group()
createGroup()
updateGroupName()
updateGroupDescription()
updateGroupSettings()
updateGroupPhoto()
addParticipants()
addAdmins()
removeParticipants()
removeAdmins()
leaveGroup()
adGroups()
groupInvitationMetadata()
groupInvitationLink()
lightGroupMetadata()
groupMetadata()

📇 Contactos

Método Z-API UAZAPI Funapi Meta
contact()
contacts()
sendContact()
addContacts()

🏘️ Comunidades

Método Z-API UAZAPI Funapi Meta
community() ⚠️
communities() ⚠️
communitiesMetadata() ⚠️

📢 Newsletters / Canales

Método Z-API UAZAPI Funapi Meta
createNewsletter() ⚠️
updateNewsletterName() ⚠️
updateNewsletterDescription() ⚠️
updateNewsletterPicture() ⚠️
newsletters() ⚠️
newsletterMetadata() ⚠️

📋 Cola de Mensajes

Método Z-API UAZAPI Funapi Meta
showQueue()
queueCount()
deleteQueueMessage()
clearQueue()

📝 Templates (Solo Meta)

Método Meta
listTemplates()
getTemplate()
createTemplate()
updateTemplate()
deleteTemplate()
uploadHeaderHandle()

Leyenda:

  • ✅ Completamente funcional
  • ⚠️ En desarrollo (retorna error descriptivo)
  • ❌ No soportado por el proveedor

Funcionalidades Avanzadas

Administración de Templates (Meta/WhatsApp Cloud)

use Funnelchat\WapiGateway\Facades\WapiGateway;
use Funnelchat\WapiGateway\Enums\ProviderEnum;

// Listar templates
$templates = WapiGateway::templates(ProviderEnum::WhatsAppCloud)
    ->listTemplates($wabaId, $token, [
        'limit' => 20,
        'after' => 'cursor123'  // Para paginación
    ]);

// Crear template
$template = WapiGateway::templates(ProviderEnum::WhatsAppCloud)
    ->createTemplate($wabaId, $token, [
        'name' => 'welcome_message',
        'language_code' => 'es',
        'category' => 'MARKETING',
        'header' => 'Bienvenido',
        'body' => 'Hola {{1}}, gracias por contactarnos',
        'footer' => 'Equipo de soporte',
        'buttons' => [
            ['type' => 'URL', 'text' => 'Visitar sitio', 'url' => 'https://example.com']
        ]
    ]);

// Eliminar template
$result = WapiGateway::templates(ProviderEnum::WhatsAppCloud)
    ->deleteTemplate($wabaId, $token, 'welcome_message', $templateUid);

Eliminación Concurrente de Mensajes

$deleteRequests = [
    ['messageId' => 'msg1', 'phone' => '123456789', 'owner' => true],
    ['messageId' => 'msg2', 'phone' => '987654321', 'owner' => true],
    // ... más mensajes
];

$results = WapiGateway::groups(ProviderEnum::ZApi)
    ->deleteMessagesConcurrently($uid, $token, $deleteRequests);

// Performance: ~2s para 100 mensajes vs ~30s+ secuencial
foreach ($results as $messageId => $result) {
    if (isset($result['error'])) {
        echo "Error eliminando {$messageId}: {$result['error']}";
    }
}

Procesamiento Asíncrono de Videos

// Automático para .mp4, .mov, .gif
$response = WapiGateway::messages(ProviderEnum::ZApi)
    ->sendFile($uid, $token, $phone, 'https://example.com/video.mp4', [
        'caption' => 'Mi video',
        'retry' => true,
        // 'async' => false  // Desactivar si es necesario
    ]);

Retry Automático

$response = WapiGateway::messages(ProviderEnum::ZApi)
    ->sendText($uid, $token, $phone, 'Mensaje importante', [
        'retry' => true  // Reintenta automáticamente en errores de conexión
    ]);

// El SDK:
// - Solo reintenta en ConnectionException
// - NO reintenta en timeouts (evita duplicados)
// - Configurable vía ZAPI_MAX_ATTEMPTS y ZAPI_RETRY_DELAY

Configuración Automática de Webhooks

El SDK configura webhooks automáticamente al crear instancias para recibir notificaciones en tiempo real de mensajes, conexión/desconexión, y otros eventos.

⚙️ Configuración

Variable requerida:

WEBHOOK_BASE_URL=https://tu-app.com

Si no defines WEBHOOK_BASE_URL, el SDK usará APP_URL como fallback. Si ninguna está configurada, no se configurarán webhooks automáticamente.

📡 Webhooks por Proveedor

Z-API

Configurados en el payload de creación de instancia:

  • /webhooks/zapi/received - Mensajes recibidos
  • /webhooks/zapi/received-and-delivery - Mensajes y confirmaciones de entrega
  • /webhooks/zapi/disconnected - Instancia desconectada
  • /webhooks/zapi/connected - Instancia conectada
  • /webhooks/zapi/message-status - Estados de mensajes enviados
  • /webhooks/zapi/block - Bloqueos/desbloqueos

UAZAPI

Configurados vía POST /webhook después de crear la instancia:

  • /webhooks/uazapi/messages - Mensajes recibidos
  • /webhooks/uazapi/messages_update - Actualizaciones de mensajes
  • /webhooks/uazapi/connection - Cambios de conexión
  • /webhooks/uazapi/messages - Eventos de grupos (comparte ruta con messages)

Opciones:

# Deshabilitar webhooks automáticos para UAZAPI
UAZAPI_AUTO_CONFIGURE_WEBHOOKS=false

Funapi

Configurados en el payload de creación de instancia:

  • /webhooks/funapi/received - Mensajes recibidos
  • /webhooks/funapi/received-and-delivery - Mensajes y confirmaciones de entrega
  • /webhooks/funapi/disconnected - Instancia desconectada
  • /webhooks/funapi/connected - Instancia conectada
  • /webhooks/funapi/message-status - Estados de mensajes enviados
  • /webhooks/funapi/block - Bloqueos/desbloqueos

💡 Ejemplo de Uso

// 1. Configurar .env
// WEBHOOK_BASE_URL=https://mi-app.com

// 2. Crear instancia (webhooks se configuran automáticamente)
$instance = WapiGateway::instances(ProviderEnum::Uazapi)->create($userId, $deviceId);
// Los webhooks ya están configurados en UAZAPI

// 3. La app host debe tener rutas para recibir webhooks
Route::post('/webhooks/uazapi/messages', [WebhookController::class, 'uazapiMessages']);
Route::post('/webhooks/uazapi/connection', [WebhookController::class, 'uazapiConnection']);
// etc...

🔍 Logging

El SDK registra automáticamente el éxito/error de la configuración de webhooks:

// Logs en UAZAPI
Log::info('UAZAPI webhook configured successfully', [...]);
Log::warning('UAZAPI webhook configuration failed', [...]);
Log::warning('UAZAPI webhook configuration skipped: WEBHOOK_BASE_URL not configured', [...]);

Device Logging (Request/Response a DB)

El SDK puede loggear cada request HTTP a los proveedores en la tabla device_logs, incluyendo el payload enviado y la respuesta recibida. El logging está deshabilitado por defecto — solo se activa explícitamente en el proyecto que lo necesite.

Configuración

# Habilitar logging a DB (deshabilitado por defecto)
WAPI_GATEWAY_LOGGING_ENABLED=true

# Conexión de base de datos a usar (por defecto: mysql)
WAPI_GATEWAY_DATABASE_CONNECTION=mysql

Importante: No habilites WAPI_GATEWAY_LOGGING_ENABLED=true sin antes publicar y correr la migración. Con queue: sync (común en desarrollo), el job fallará directamente si la tabla no existe.

Migración

La migración no se ejecuta automáticamente al instalar el paquete. Solo debe publicarse en el proyecto que será dueño de la tabla device_logs (actualmente user-api):

# Solo en user-api:
php artisan vendor:publish --tag=wapi-migrations
php artisan migrate

Los demás proyectos que usen el paquete (new-groups, sync-group-api, etc.) no necesitan publicar la migración — solo configurar WAPI_GATEWAY_DATABASE_CONNECTION apuntando a la misma base de datos donde user-api creó la tabla.

Estructura de la tabla device_logs

Campo Tipo Descripción
instance_uid string UID de la instancia de WhatsApp
provider string Proveedor usado (zapi, uazapi, funapi, meta)
method string Método ejecutado (sendText, status, createGroup, etc.)
url text URL completa del request HTTP
request_payload json Body enviado al proveedor (ej: {"phone": "549...", "message": "Hola"})
response_body json Respuesta del proveedor
status_code smallint Código HTTP de respuesta
duration_ms int Tiempo de respuesta en milisegundos
is_error boolean Si el request resultó en error
sent_at timestamp Fecha y hora real del request HTTP (distinto de created_at que es cuando se persistió el registro)

Queue

Los logs se despachan vía el job StoreDeviceLogJob en la cola device-logs. Asegurate de tener un worker procesando esa cola:

php artisan queue:work --queue=device-logs

El dispatch del job está protegido con try-catch — si la infraestructura de cola falla (Redis caído, etc.), el error se loggea pero no interrumpe la operación de WhatsApp que lo originó.

Protección contra payloads grandes

Los payloads de request y response se truncan automáticamente si superan 200KB (para evitar exceder el límite de 256KB de SQS). Cuando esto ocurre, el campo almacena {"_truncated": true, "original_size": 350000}.

Notas

  • Los métodos GET (status, qrCode, me, contact, etc.) registran request_payload como [] ya que no envían body.
  • Los métodos POST (sendText, sendFile, createGroup, addContacts, etc.) registran el payload completo enviado al proveedor.
  • El logging es asíncrono y no impacta el tiempo de respuesta de las operaciones.
  • El job usa 3 reintentos con backoff progresivo (5s, 15s, 30s) y timeout de 10s.

Sistema de Resources

El SDK transforma las respuestas de cada proveedor para mantener compatibilidad con WAPI original:

// Ejemplo: status() con fetch automático de QR
$status = WapiGateway::instances(ProviderEnum::ZApi)->status($uid, $token);
// Retorna: ['accountStatus' => 'authenticated', 'qrCode' => null]
// o: ['accountStatus' => 'got qr code', 'qrCode' => 'data:image/png;base64,...']

// checkPhone() normalizado
$result = WapiGateway::instances(ProviderEnum::ZApi)->checkPhone($uid, $token, $phone);
// Retorna: ['result' => true] (no 'exists', sino 'result')

// me() con campos adicionales
$me = WapiGateway::instances(ProviderEnum::ZApi)->me($uid, $token);
// Retorna: ['phone' => '...', 'name' => '...', 'avatar' => '...', 'locale' => 'es', 'isBusiness' => true]

Limitaciones de Funapi

Funapi (Whatsmeow Bridge) tiene las siguientes limitaciones conocidas:

❌ No Soportado

  • checkPhone() - No existe endpoint equivalente

⚠️ En Desarrollo

Los siguientes métodos retornan error descriptivo indicando que están en desarrollo:

  • sendButtons()
  • sendButtonLink()
  • sendOptionList()
  • sendPoll()
  • sendLink()
  • sendEvent()
  • sendTemplate()

✅ Totalmente Funcional

  • Mensajería básica: texto, archivos, ubicación, contactos
  • Gestión de instancias completa
  • Operaciones de grupos completas
  • Cola de mensajes completa

Manejo de Errores

Todos los métodos retornan arrays con:

  • Éxito: ['messageId' => '...', 'status' => 'queued', ...]
  • Error: ['error' => 'mensaje descriptivo']
$response = WapiGateway::messages(ProviderEnum::ZApi)->sendText(...);

if (isset($response['error'])) {
    // Error normalizado del proveedor
    Log::error('Error enviando mensaje', ['error' => $response['error']]);
    return response()->json(['error' => $response['error']], 409);
}

// Éxito
return response()->json($response);

Integración en Controladores

use Funnelchat\WapiGateway\Facades\WapiGateway;
use Funnelchat\WapiGateway\Enums\ProviderEnum;

class WhatsAppController extends Controller
{
    public function send(Request $request)
    {
        $provider = match($request->provider) {
            'zapi' => ProviderEnum::ZApi,
            'uazapi' => ProviderEnum::Uazapi,
            'funapi' => ProviderEnum::Funapi,
            'meta' => ProviderEnum::WhatsAppCloud,
        };

        $response = WapiGateway::messages($provider)
            ->sendText(
                $request->uid,
                $request->header('token'),
                $request->phone,
                $request->message,
                ['retry' => true]
            );

        return isset($response['error'])
            ? response()->json(['error' => $response['error']], 409)
            : response()->json($response);
    }
}

Changelog

v0.3.0 - 2025-XX-XX (En desarrollo)

Added:

  • ✨ Soporte completo para Newsletters/Canales: createNewsletter(), updateNewsletterName(), updateNewsletterDescription(), updateNewsletterPicture(), newsletters(), newsletterMetadata()
  • ✨ Soporte para Comunidades: community(), communities(), communitiesMetadata()
  • ✨ Sección de Contactos: contact(), contacts(), sendContact(), addContacts()
  • ✨ Métodos de grupos: adGroups(), groupInvitationMetadata(), groupInvitationLink(), lightGroupMetadata(), groupMetadata()
  • ✨ Método pinMessage() en MessagesContract
  • ✨ Device Logging: StoreDeviceLogJob registra request payload y response en tabla device_logs vía cola asíncrona
  • ✨ Campo sent_at en device_logs para capturar fecha/hora real del request HTTP
  • ✨ Migración publicable vía --tag=wapi-migrations (solo para el proyecto dueño de la tabla)
  • ✨ Trait LogsDeviceRequests — elimina ~160 líneas duplicadas entre los 4 clientes

Fixed:

  • 🐛 request_payload en device_logs ahora contiene el body HTTP real enviado al proveedor en todos los métodos POST (antes se guardaba [])
  • 🐛 StoreDeviceLogJob::dispatch() envuelto en try-catch para no interrumpir operaciones de WhatsApp si la cola falla
  • 🐛 Payloads >200KB se truncan automáticamente para no exceder límite de SQS (256KB)
  • 🐛 ZApiClient::create() ahora pasa instance_uid correctamente (antes pasaba string vacío)
  • 🐛 Variable de entorno WAPI_GATEWAY_DATABASE_CONNECTION consistente entre config y README
  • 🐛 $backoff corregido de [5, 15] a [5, 15, 30] para matchear $tries = 3
  • 🐛 Métodos sendFile, sendLocation, y newsletter updates en FunapiClient/ZApiClient ahora logean el payload del request

Changed:

  • 🔧 Default de logging_enabled cambiado de true a false (opt-in explícito)
  • 🔧 Migración no se auto-carga — solo se publica en el proyecto que la necesite (user-api)
  • 🔧 Casts de DeviceLog ampliados: status_code => integer, duration_ms => integer

v0.2.0 - 2025-01-XX

Added:

  • ✨ Configuración automática de webhooks al crear instancias
  • ✨ Variable de entorno WEBHOOK_BASE_URL para configuración centralizada
  • ✨ Método privado configureWebhooks() en UazapiClient con logging completo
  • ✨ Soporte para deshabilitar webhooks con UAZAPI_AUTO_CONFIGURE_WEBHOOKS=false

Changed:

  • 🔧 ZApiClient, UazapiClient y FunapiClient ahora usan WEBHOOK_BASE_URL en lugar de APP_URL
  • 🔧 Webhooks solo se configuran si WEBHOOK_BASE_URL está definida

v0.1.0 - 2025-01-07

Added:

  • ✨ Sistema completo de Resources para transformación de respuestas
  • ✨ Soporte para Funapi (Whatsmeow Bridge) - 36/44 métodos funcionales
  • ✨ Fetch automático de QR code en método status() cuando la instancia está desconectada
  • ✨ Normalización de respuestas entre proveedores (accountStatus, result, locale, etc.)
  • ✨ 20 Resources implementados (10 Z-API + 10 UAZAPI)
  • ✨ Administración completa de templates para Meta/WhatsApp Cloud
  • ✨ Método deleteMessagesConcurrently() para eliminación paralela de mensajes
  • ✨ Retry automático configurable en métodos de envío
  • ✨ Procesamiento asíncrono automático para videos
  • ✨ Timeouts configurables por proveedor

Fixed:

  • 🐛 Corregidos endpoints de Funapi (/send-image y /send-document sin sufijos)
  • 🐛 Métodos no soportados de Funapi ahora retornan errores descriptivos

Changed:

  • 🔧 Timeout por defecto reducido de 120s a 29s (configurable)
  • 🔧 Retry inteligente: no reintenta en timeouts, solo en ConnectionException

Performance:

  • ⚡ Eliminación de 100 mensajes: ~2s con HTTP Pool vs ~30s+ secuencial
  • ⚡ Reducción de timeouts en envíos de videos gracias a procesamiento async

Roadmap

  • Completar endpoints faltantes de Funapi (botones, listas, encuestas)
  • Tests automatizados para todos los proveedores
  • Documentación de DTOs y respuestas por operación
  • Soporte para webhooks (configuración automática implementada)
  • v1.0.0 - Release estable cuando todos los proveedores estén 100% completos

Contribución

Esta es una librería privada de Funnelchat. Para reportar bugs o solicitar features, contacta a: soporte@funnelchat.io

Licencia

Privado (Funnelchat). Todos los derechos reservados.