Files
plan-lealtad/docs/configuracion.md
T
2026-05-11 08:30:22 -04:00

12 KiB

Configuración — Lealtad Demo n8n

Este documento resume la configuración del demo de sistema de lealtad usando Supabase, n8n y WhatsApp Business API / Meta Graph.

Nota de seguridad: esta versión está preparada para subirse a Gitea.
No contiene tokens, teléfonos reales, credenciales, IDs reales de producción ni datos personales.


1. Objetivo del proyecto

El flujo permite que un cliente escriba por WhatsApp y que n8n:

  1. Reciba el mensaje desde WhatsApp.
  2. Extraiga el teléfono y el texto del mensaje.
  3. Consulte en Supabase si el cliente existe.
  4. Cree el cliente si es nuevo.
  5. Maneje el estado del bot:
    • NEW
    • AWAITING_NAME
    • ACTIVE
  6. Responda por WhatsApp según el estado del cliente.
  7. Entregue una URL de Wallet / pase digital.

2. Arquitectura general

flowchart LR
  A[WhatsApp / Meta] --> B[n8n WhatsApp Trigger]
  B --> C[Code Node: parsear mensaje]
  C --> D[Supabase RPC: process_wa_customer]
  D --> E{Switch por estado}
  E -->|NEW| F[Actualizar a AWAITING_NAME]
  F --> G[Enviar mensaje solicitando nombre]
  E -->|AWAITING_NAME| H[Guardar nombre y activar cliente]
  H --> I[Enviar link de Wallet]
  E -->|ACTIVE| J[Responder saldo, tier y link de Wallet]

3. Valores que deben reemplazarse

Antes de usar este proyecto en una demo real o producción, reemplaza los siguientes placeholders:

Placeholder Descripción
TU_PROYECTO Subdominio del proyecto en Supabase
TU_SUPABASE_ANON_KEY Supabase anon key configurada en credenciales de n8n
TU_TOKEN_DE_META Token de Meta / WhatsApp Business API
TU_PHONE_NUMBER_ID ID del número de WhatsApp en Meta
COMPANY_ID_DEMO UUID de la empresa creada en la tabla companies
TU_PORTAL Dominio del portal donde se consulta/genera el Wallet

Parte 1 — Configuración en Supabase

En Supabase, abre el SQL Editor, pega el siguiente script y ejecútalo.

Este script crea:

  • Tabla companies
  • Tabla customers
  • Tabla transactions
  • Trigger de gamificación automática
  • RPC process_wa_customer
-- 1. Habilitar extensión para IDs seguros (UUID)
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- 2. TABLA: Empresas
CREATE TABLE companies (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  name TEXT NOT NULL,
  wa_phone_id TEXT -- ID del número de WhatsApp en Meta
);

-- 3. TABLA: Clientes finales
CREATE TABLE customers (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  company_id UUID REFERENCES companies(id) ON DELETE CASCADE,
  phone TEXT NOT NULL,
  name TEXT,
  balance NUMERIC(10,2) DEFAULT 0.00,
  tier TEXT DEFAULT 'Member',
  bot_state TEXT DEFAULT 'NEW',
  pass_id TEXT UNIQUE DEFAULT uuid_generate_v4(),
  created_at TIMESTAMPTZ DEFAULT NOW(),
  UNIQUE(company_id, phone)
);

-- 4. TABLA: Transacciones
CREATE TABLE transactions (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  customer_id UUID REFERENCES customers(id) ON DELETE CASCADE,
  amount_spent NUMERIC(10,2) NOT NULL,
  cashback_earned NUMERIC(10,2) NOT NULL,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- 5. TRIGGER: Gamificación automática
CREATE OR REPLACE FUNCTION update_customer_stats() RETURNS TRIGGER AS $$
DECLARE
  new_balance NUMERIC;
BEGIN
  -- Sumar cashback al cliente
  UPDATE customers
  SET balance = balance + NEW.cashback_earned
  WHERE id = NEW.customer_id
  RETURNING balance INTO new_balance;

  -- Lógica de estatus dinámico
  IF new_balance >= 100 THEN
    UPDATE customers SET tier = 'Black' WHERE id = NEW.customer_id;
  ELSIF new_balance >= 50 THEN
    UPDATE customers SET tier = 'Gold' WHERE id = NEW.customer_id;
  END IF;

  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER after_transaction_insert
AFTER INSERT ON transactions
FOR EACH ROW EXECUTE FUNCTION update_customer_stats();

-- 6. RPC: función que n8n llama para consultar/crear cliente
CREATE OR REPLACE FUNCTION process_wa_customer(
  p_phone TEXT,
  p_company_id UUID
)
RETURNS TABLE (
  customer_id UUID,
  state TEXT,
  c_name TEXT,
  pass_id TEXT,
  c_balance NUMERIC,
  c_tier TEXT
) AS $$
DECLARE
  v_id UUID;
  v_state TEXT;
  v_name TEXT;
  v_pass TEXT;
  v_bal NUMERIC;
  v_tier TEXT;
BEGIN
  -- Buscar cliente existente
  SELECT id, bot_state, name, customers.pass_id, balance, tier
  INTO v_id, v_state, v_name, v_pass, v_bal, v_tier
  FROM customers
  WHERE phone = p_phone
    AND company_id = p_company_id;

  -- Si es la primera vez que escribe, crearlo en estado NEW
  IF v_id IS NULL THEN
    INSERT INTO customers (phone, company_id, bot_state)
    VALUES (p_phone, p_company_id, 'NEW')
    RETURNING id, bot_state, name, customers.pass_id, balance, tier
    INTO v_id, v_state, v_name, v_pass, v_bal, v_tier;
  END IF;

  RETURN QUERY SELECT v_id, v_state, v_name, v_pass, v_bal, v_tier;
END;
$$ LANGUAGE plpgsql;

4. Crear empresa demo

Después de crear las tablas, inserta una empresa de prueba en companies.

Ejemplo:

INSERT INTO companies (name, wa_phone_id)
VALUES ('Empresa Demo', 'TU_PHONE_NUMBER_ID');

Luego copia el id generado por Supabase. Ese valor será usado en n8n como:

COMPANY_ID_DEMO

No subas a Git/Gitea el UUID real de producción si este repositorio se usará como plantilla.


Parte 2 — Flujo en n8n

El flujo en n8n procesa mensajes de WhatsApp, consulta Supabase y responde según el estado del cliente.


Nodo 1 — WhatsApp Trigger

Configura el nodo de entrada con las credenciales de WhatsApp / Meta.

Responsabilidad:

  • Escuchar mensajes entrantes.
  • Recibir payloads de WhatsApp.
  • Disparar el flujo cuando llega un mensaje.

Nodo 2 — Code Node: Parsear JSON de Meta

Este nodo limpia el payload de Meta y devuelve solo los campos necesarios:

  • Teléfono del cliente
  • Texto recibido
  • ID del número de WhatsApp
  • ID de empresa demo
const body = $input.first().json;

// Evitar procesar payloads sin mensajes reales
if (!body?.messages?.length) {
  return [];
}

const msg = body.messages[0];
const phone_id = body.metadata?.phone_number_id;

return [
  {
    json: {
      phone: msg.from,
      text: msg.type === 'text' ? (msg.text?.body || '').trim() : '',
      phone_id,
      company_id: 'COMPANY_ID_DEMO'
    }
  }
];

Reemplaza COMPANY_ID_DEMO por el UUID real de la empresa creada en Supabase, o configura este valor mediante variables/credenciales según tu estándar interno.


Nodo 3 — HTTP Request: Consultar a Supabase RPC

Este nodo consulta la función process_wa_customer en Supabase.

Configuración sugerida:

Campo Valor
Method POST
URL https://TU_PROYECTO.supabase.co/rest/v1/rpc/process_wa_customer
Authentication Header Auth / Custom Auth
Header apikey TU_SUPABASE_ANON_KEY
Header Authorization Bearer TU_SUPABASE_ANON_KEY
Send Body Activado
Body Type JSON

Body:

{
  "p_phone": "{{ $json.phone }}",
  "p_company_id": "{{ $json.company_id }}"
}

Nodo 4 — Switch Node: Enrutador por estado

Conecta la salida del HTTP Request de Supabase al Switch.

El Switch debe evaluar el campo:

{{ $json.state }}

Rutas:

Ruta Condición Descripción
Ruta 0 state == NEW Cliente nuevo
Ruta 1 state == AWAITING_NAME Cliente está dando su nombre
Ruta 2 state == ACTIVE Cliente ya está activo

Parte 3 — Ramas del Switch


Rama 0 — Estado NEW

Objetivo: pedir el nombre del cliente.

Paso 1 — Actualizar estado en Supabase

Nodo: HTTP Request

Campo Valor
Method PATCH
URL https://TU_PROYECTO.supabase.co/rest/v1/customers?id=eq.{{ $json.customer_id }}
Auth Misma configuración de Supabase
Header recomendado Prefer: return=minimal
Body JSON

Body:

{
  "bot_state": "AWAITING_NAME"
}

Paso 2 — Enviar WhatsApp solicitando nombre

Nodo: HTTP Request

Campo Valor
Method POST
URL https://graph.facebook.com/v19.0/{{ $('Code').item.json.phone_id }}/messages
Header Authorization Bearer TU_TOKEN_DE_META
Body JSON

Body:

{
  "messaging_product": "whatsapp",
  "to": "{{ $('Code').item.json.phone }}",
  "type": "text",
  "text": {
    "body": "¡Hola! 👋 Bienvenido a nuestro Club VIP. Para personalizar tu Billetera Digital, ¿cómo te gusta que te llamen?"
  }
}

Rama 1 — Estado AWAITING_NAME

Objetivo: guardar el nombre del cliente y activar su perfil.

Paso 1 — Guardar nombre y pasar a ACTIVE

Nodo: HTTP Request

Campo Valor
Method PATCH
URL https://TU_PROYECTO.supabase.co/rest/v1/customers?id=eq.{{ $json.customer_id }}
Auth Misma configuración de Supabase
Body JSON

Body:

{
  "bot_state": "ACTIVE",
  "name": "{{ $('Code').item.json.text }}"
}

Nodo: HTTP Request

Campo Valor
Method POST
URL https://graph.facebook.com/v19.0/{{ $('Code').item.json.phone_id }}/messages
Header Authorization Bearer TU_TOKEN_DE_META
Body JSON

Body:

{
  "messaging_product": "whatsapp",
  "to": "{{ $('Code').item.json.phone }}",
  "type": "text",
  "text": {
    "body": "¡Listo, {{ $('Code').item.json.text }}! 🎉\n\nTu perfil está activo. Descarga tu pase VIP y agrégalo a tu Apple/Google Wallet tocando este enlace:\n\n👉 https://TU_PORTAL/wallet/{{ $('HTTP Request').item.json.pass_id }}"
  }
}

Rama 2 — Estado ACTIVE

Objetivo: responder saldo, nivel y link de recuperación del Wallet.

Nodo: HTTP Request

Campo Valor
Method POST
URL https://graph.facebook.com/v19.0/{{ $('Code').item.json.phone_id }}/messages
Header Authorization Bearer TU_TOKEN_DE_META
Body JSON

Body:

{
  "messaging_product": "whatsapp",
  "to": "{{ $('Code').item.json.phone }}",
  "type": "text",
  "text": {
    "body": "¡Hola de nuevo, {{ $json.c_name }}! 💳\n\nEres nivel *{{ $json.c_tier }}* y tu saldo actual para gastar es de *$ {{ $json.c_balance }}*.\n\nSi perdiste tu tarjeta, recupérala aquí:\n👉 https://TU_PORTAL/wallet/{{ $json.pass_id }}"
  }
}

Parte 4 — Credenciales necesarias en n8n

Configura manualmente las credenciales dentro de n8n. No deben versionarse en Gitea.

WhatsApp / Meta

  • WhatsApp Trigger OAuth o credencial equivalente.
  • Token de Meta para llamadas a Graph API.
  • Phone Number ID de Meta.

Supabase

Configura headers para HTTP Request:

apikey: TU_SUPABASE_ANON_KEY
Authorization: Bearer TU_SUPABASE_ANON_KEY

Parte 5 — Importación del workflow

  1. Entra a n8n.
  2. Importa el archivo n8n/plan-de-incentivo.template.json.
  3. Asigna manualmente las credenciales de WhatsApp y Supabase.
  4. Reemplaza los placeholders:
    • TU_PROYECTO
    • TU_PHONE_NUMBER_ID
    • COMPANY_ID_DEMO
    • TU_PORTAL
  5. Ejecuta una prueba con un número autorizado de WhatsApp.
  6. Verifica que el cliente se cree en Supabase.
  7. Verifica que el bot responda según el estado correcto.

Parte 6 — Seguridad antes de subir a Gitea

Antes de hacer commit, verifica que no existan:

  • pinData
  • Teléfonos reales
  • Nombres reales de clientes/personas
  • Tokens de Meta
  • Supabase anon key real
  • Supabase service role key
  • Credential IDs reales de n8n
  • Credential names reales de n8n
  • webhookId
  • instanceId
  • Workflow ID real
  • versionId
  • UUID real de company_id
  • URLs reales internas o sensibles

El workflow que se suba al repositorio debe funcionar como template reutilizable, no como exportación exacta de producción.


Parte 7 — Checklist rápido

  • Supabase schema creado.
  • Empresa demo creada en companies.
  • COMPANY_ID_DEMO reemplazado en n8n.
  • Credenciales de WhatsApp configuradas manualmente.
  • Credenciales de Supabase configuradas manualmente.
  • Workflow importado en n8n.
  • pinData eliminado del JSON antes de subirlo.
  • IDs, tokens y teléfonos reales eliminados.
  • Prueba end-to-end ejecutada correctamente.