16 — Pasarelas de Pago (Hotmart / dLocal / Shopify / Skool)

Índice de la página
  1. 01Cliente: Martín Rieznik / LevantArte
  2. Área: Pagos — pasarelas externas (4 plataformas × 6 SKUs = 24 productos manuales)
  3. 020. Definición de "done" para esta checklist
  4. 031. Pre-requisitos
  5. 042. Tareas
  6. 2.1 SKUs maestros (registro central)
  7. 2.2 Hotmart (productor de Martín)
  8. 2.3 dLocal (productor de Martín)
  9. 2.4 Shopify (definir titularidad)
  10. 2.5 Skool (condicional)
  11. 2.6 Selector de pasarela en /martin/checkout/[sku]
  12. 2.7 Webhooks unificados y CAPI
  13. 2.8 Tests E2E
  14. 2.9 Refunds y soporte
  15. 053. Variables y posibilidades a anticipar
  16. 064. Multi-tenant: cómo se replica al cliente #2
  17. 075. Recursos y archivos relacionados
  18. 086. Notas y aprendizajes (post-mortem)
  19. 09CHANGELOG

Cliente: Martín Rieznik / LevantArte

Área: Pagos — pasarelas externas (4 plataformas × 6 SKUs = 24 productos manuales)

Configurar las 4 pasarelas externas con los 6 SKUs cada una. Total 24 productos + selector en /martin/checkout/[sku] + webhooks que escriben en Supabase + disparan Conversions API. La pasarela #5 (transferencia) está en checklist 17.

Última actualización: 2026-05-05
Responsable principal (R): Martín (cuentas) + Jesús (coordinación)
Aprobador (A): Jesús
Deadline: 2026-05-31
Depende de: 01_INFRA_TECNICA, 08_OFERTA_Y_STACK
Bloquea a: 02_LANDINGS_Y_PAGES, 13_PIXEL_TRACKING, 15_EQUIPO_VENTAS, 19_GO_LIVE_8_JUNIO


0. Definición de "done" para esta checklist

  • Las 4 pasarelas con los 6 SKUs configurados (24 productos), checkout URL copiada a _CONFIG.md > checkouts.<pasarela>.<sku>. Si Skool no aplica (Martín no tiene cuenta con productos pagos) → marcado N/A explícito en config.
  • Selector de pasarela en /martin/checkout/[sku] muestra dinámicamente solo las pasarelas activas según país del lead + disponibilidad del SKU.
  • Webhooks de cada pasarela (/api/webhook/[pasarela]) escriben en compras (status APPROVED/REFUNDED) y disparan evento Purchase server-side (CAPI Meta).
  • Test E2E: 1 compra real de $1 (refundeada) por cada pasarela activa × 1 SKU = al menos 4 compras de prueba con webhook recibido + fila en Supabase + evento CAPI confirmado en Meta Events Manager.
  • Documentación de cómo refundear en cada pasarela en recursos/refund_playbook.md.

1. Pre-requisitos

# Pre-requisito Provisto por Estado
1 SKUs definidos (TALLER_LUNES $5, TALLER_MARTES $10, TALLER_FULL $15, MEMBRESIA_ANUAL_1PAGO $97, MEMBRESIA_MENSUAL $17, MEMBRESIA_6_MESES $67×6) Jesús + Martín ✅ definidos en plan
2 Acceso productor Hotmart de Martín Martín ⚠️ BLOQUEANTE
3 Acceso productor dLocal de Martín Martín ⚠️ BLOQUEANTE
4 Decisión de titularidad de Shopify (Martín o TooAudience) Jesús + Martín ⚠️ pendiente — asumido TooAudience (ver § 3)
5 Confirmación si Martín tiene Skool con productos pagos Martín ⚠️ BLOQUEANTE — si NO, se excluye Skool del setup
6 Endpoint público https://deacademia.com/api/webhook/[pasarela] desplegado en Vercel Eric ⚠️ pendiente (checklist 01)
7 Tabla compras y eventos en Supabase Eric ⚠️ pendiente (checklist 01)
8 Meta Pixel ID + CAPI access token Eric ⚠️ pendiente (checklist 13)

2. Tareas

2.1 SKUs maestros (registro central)

  • Crear recursos/skus_master.md. R: Cortex. Done cuando: tabla con los 6 SKUs: ID interno, nombre comercial, descripción corta, precio USD, precio ARS (referencial), incluye, fechas activas. Es la fuente de verdad para los 4 setups.
  • Definir comportamiento del precio escalonado del taller. R: Jesús. Done cuando: confirmado: TALLER_LUNES y TALLER_MARTES son productos separados (no descuento). TALLER_FULL es el de mié-vie. Lib precio_dinamico.ts decide qué SKU mostrar según día (ver checklist 02).
  • Decidir moneda primaria. R: Jesús + Martín. Done cuando: USD para todas las pasarelas internacionales (Hotmart/dLocal/Shopify), conversión ARS solo para transferencia local. Documentado en _CONFIG.md > pricing.currency.

2.2 Hotmart (productor de Martín)

  • Acceso a cuenta productor de Martín. R: Martín. Done cuando: Jesús es co-productor o tiene login compartido (1Password vault). BLOQUEANTE.
  • Verificar comisión Hotmart. R: Jesús. Done cuando: confirmado 9.9% + $1 por venta (estándar Hotmart). Documentado en _CONFIG.md > pricing.fees.hotmart.
  • Crear producto TALLER_LUNES. R: Jesús. Done cuando: producto Hotmart "Taller LevantArte — Acceso Lunes" $5 USD. Tipo "membresía/comunidad" o "acceso digital" según mejor encaje. Sales page externa = https://deacademia.com/martin/taller. Checkout URL anotada.
  • Crear producto TALLER_MARTES. R: Jesús. Done cuando: idem $10 USD.
  • Crear producto TALLER_FULL. R: Jesús. Done cuando: idem $15 USD.
  • Crear producto MEMBRESIA_ANUAL_1PAGO. R: Jesús. Done cuando: $97 USD pago único, acceso 12 meses.
  • Crear producto MEMBRESIA_MENSUAL. R: Jesús. Done cuando: $17 USD/mes recurrente con cancelación libre.
  • Crear producto MEMBRESIA_6_MESES. R: Jesús. Done cuando: $67 USD × 6 meses (recurrente con fin a 6 cobros).
  • Configurar webhook Hotmart. R: Eric. Done cuando: en Hotmart > Tools > Postback URL apuntando a https://deacademia.com/api/webhook/hotmart con eventos: PURCHASE_APPROVED, PURCHASE_REFUNDED, PURCHASE_CHARGEBACK, SUBSCRIPTION_CANCELLATION. Token compartido en env var HOTMART_WEBHOOK_TOKEN.
  • Implementar handler /api/webhook/hotmart. R: Eric. Done cuando: valida HMAC, parsea evento, mapea SKU Hotmart → SKU interno, INSERT en compras con status correcto, dispara Purchase a CAPI con event_id único = transaction_id_externo (deduplicación).
  • Copiar 6 checkout URLs a _CONFIG.md. R: Jesús. Done cuando: bloque checkouts.hotmart.<sku> lleno con URL final.
  • Test con tarjeta de prueba Hotmart. R: Jesús. Done cuando: 1 compra completada en sandbox, webhook recibido, fila en Supabase.

2.3 dLocal (productor de Martín)

  • Acceso a cuenta productor de Martín. R: Martín. Done cuando: API key + secret en 1Password TooAudience. BLOQUEANTE.
  • Verificar comisión dLocal. R: Jesús. Done cuando: rate confirmado por país (típicamente 4-6% LATAM + costos por método). Documentado.
  • Decidir integración: API directa o checkout hospedado. R: Eric. Done cuando: decidido — para MVP usar dLocal Go (checkout hospedado) por velocidad. API directa en v2. Documentado.
  • Crear 6 productos dLocal Go. R: Eric. Done cuando: los 6 SKUs creados con precios USD, descripción corta, redirect post-pago a https://deacademia.com/martin/[taller|membresia]/gracias?sku=<sku>&tx=<txid>.
  • Configurar webhook dLocal. R: Eric. Done cuando: notification URL = https://deacademia.com/api/webhook/dlocal. Eventos: PAID, REFUNDED, CHARGEBACK. Firma HMAC validada.
  • Implementar handler /api/webhook/dlocal. R: Eric. Done cuando: idem Hotmart con mapping de SKU + INSERT + CAPI.
  • Copiar 6 checkout URLs a _CONFIG.md. R: Eric. Done cuando: bloque checkouts.dlocal.<sku> lleno.
  • Test con tarjeta de prueba dLocal. R: Eric. Done cuando: 1 compra completada sandbox, webhook recibido.

2.4 Shopify (definir titularidad)

  • Decidir titularidad de la tienda Shopify. R: Jesús (A: Martín). Done cuando: documentado en _CONFIG.md > pricing.shopify.owner. Decisión asumida: TooAudience (tienda master tooaudience-paraguas.myshopify.com) que vende como reseller, simplifica multi-tenant. Implicancias fiscales: TooAudience factura al cliente final, settle interno con Martín en el balance mensual del 50/50. Si Martín prefiere tener su propia tienda, switch en sem 2.
  • Crear / configurar tienda Shopify TooAudience. R: Eric. Done cuando: tienda en plan Basic Shopify ($25/mes). Métodos de pago: Shopify Payments (USD) + PayPal + tarjetas internacionales. Sin envíos físicos (todos productos digitales).
  • Crear 6 productos Shopify. R: Eric. Done cuando: los 6 SKUs como productos digitales (sin shipping). Variantes si aplica (membresía mensual recurrente requiere app Recharge o Bold Subscriptions — instalar).
  • App de suscripciones Shopify. R: Eric. Done cuando: app Bold Subscriptions o Recharge instalada para MEMBRESIA_MENSUAL y MEMBRESIA_6_MESES. Configurada con cancelación libre.
  • Configurar webhook Shopify. R: Eric. Done cuando: webhooks orders/paid, orders/refunded, orders/cancelled, subscription_contracts/update apuntando a https://deacademia.com/api/webhook/shopify. Firma HMAC con SHOPIFY_WEBHOOK_SECRET.
  • Implementar handler /api/webhook/shopify. R: Eric. Done cuando: idem.
  • Copiar 6 checkout URLs (o permalinks de variant). R: Eric. Done cuando: bloque checkouts.shopify.<sku> lleno con permalink directo https://tooaudience-paraguas.myshopify.com/cart/<variant_id>:1?attributes[client]=martin&attributes[lead_id]=...
  • Test con tarjeta de prueba Shopify. R: Eric. Done cuando: 1 compra Bogus Gateway sandbox.

2.5 Skool (condicional)

  • Confirmar con Martín si tiene cuenta Skool con productos pagos activos. R: Jesús. Done cuando: respuesta SI/NO documentada en _CONFIG.md > pricing.skool.enabled.
  • Si NO. R: Jesús. Done cuando: marcado enabled: false en config + selector en /martin/checkout/[sku] no muestra opción Skool. Saltar al § 2.6.
  • Si SI — acceso Skool admin de Martín. R: Martín. Done cuando: Jesús co-admin de la comunidad. BLOQUEANTE para resto del § 2.5.
  • Skool — limitación conocida: no soporta SKUs separados de "taller" como productos one-time. R: Cortex (research). Done cuando: validado: Skool solo permite "memberships" recurrentes o "courses" gated. Decisión: para Skool ofrecer solo MEMBRESIA_ANUAL_1PAGO + MEMBRESIA_MENSUAL + MEMBRESIA_6_MESES (3 SKUs, no 6). Los 3 SKUs de taller no aplican a Skool. Documentar workaround en _CONFIG.md.
  • Crear 3 productos Skool (membresías). R: Jesús. Done cuando: los 3 SKUs de membresía configurados con precios.
  • Webhooks Skool. R: Eric. Done cuando: Skool API tiene webhooks limitados — usar Zapier/Make como puente si necesario, apuntando a https://deacademia.com/api/webhook/skool. Si Skool no soporta, polling cron cada 5 min al endpoint de members.
  • Implementar handler /api/webhook/skool. R: Eric. Done cuando: valida + INSERT + CAPI.
  • Copiar 3 checkout URLs Skool. R: Jesús. Done cuando: bloque checkouts.skool.<sku> lleno (3 URLs, no 6).
  • Test compra real $1 Skool. R: Jesús. Done cuando: 1 compra completada, webhook/polling detectado.

2.6 Selector de pasarela en /martin/checkout/[sku]

  • Definir lógica de selector. R: Eric + Cortex. Done cuando: app/[client_slug]/checkout/[sku]/page.tsx lee:
    1. País del lead (de Supabase o IP geolocation)
    2. SKU solicitado
    3. Pasarelas disponibles según _CONFIG.md > checkouts.<pasarela>.<sku> (no muestra si no existe)
    Muestra: hero del producto + precio + 4-5 botones de pasarela ordenados por preferencia regional (LATAM: dLocal primero; resto: Hotmart o Shopify primero).
  • Botón "Hablar con asesor" antes del checkout. R: Eric + Cortex. Done cuando: botón secundario "Tengo dudas, hablame por WhatsApp" → /martin/admision (form pre-checkout, ver checklist 02). Crea evento checkout_to_human en Supabase.
  • Tracking de evento checkout_clicked. R: Eric. Done cuando: cada click en botón de pasarela dispara checkout_clicked con {sku, pasarela, lead_id} a Supabase eventos.
  • Persistencia de UTMs y lead_id. R: Eric. Done cuando: query params lead_id, closer_slug, UTMs se propagan al checkout externo (vía URL params o session storage).
  • Copy de la página. R: Cortex. Done cuando: H1 del producto + bullet points de qué incluye + bullet de garantía + 4 botones de pasarela con label corto ("Pagar con tarjeta — Hotmart", "Pagar con tarjeta LATAM — dLocal", "Pagar internacional — Shopify", "Pagar con transferencia (-5%)"). Audit redaccion-publicitaria ≥ 4.0/5.0.

2.7 Webhooks unificados y CAPI

  • Estructura común de handler webhook. R: Eric. Done cuando: módulo lib/webhooks/<pasarela>.ts exporta handle(event) que: 1) valida firma 2) extrae sku, precio, transaction_id, status, customer_email, customer_name 3) llama a compras.upsert() 4) si APPROVED, llama a capi.purchase() 5) si APPROVED, llama a delivery.grant_access(sku, lead) (acceso al producto: agregar a grupo WA, activar en Skool, etc.).
  • Idempotencia de webhooks. R: Eric. Done cuando: compras tiene unique constraint en (pasarela, transaction_id_externo). Reintentos del mismo evento no duplican.
  • CAPI Purchase event con event_id único. R: Eric. Done cuando: event_id = pasarela_transactionid para deduplicación con Pixel client-side.
  • Logs de webhooks fallidos. R: Eric. Done cuando: tabla webhook_failures registra payload + error + timestamp. Cron diario manda resumen a Slack/WA interno.

2.8 Tests E2E

  • Test Hotmart con tarjeta sandbox. R: Jesús. Done cuando: compra exitosa, webhook OK, Supabase OK, CAPI evento visible en Meta.
  • Test dLocal con tarjeta sandbox. R: Eric. Done cuando: idem.
  • Test Shopify con Bogus Gateway. R: Eric. Done cuando: idem.
  • Test Skool real $1. R: Jesús. Done cuando: idem (si aplica).
  • Compras reales $1 (refundeadas) por cada pasarela activa × 1 SKU. R: Jesús + Eric. Done cuando: 4 compras reales documentadas en recursos/test_compras.md con timestamp + transaction ID + refund OK.
  • Match rate CAPI ≥ 70%. R: Eric. Done cuando: Meta Events Manager > Test events muestra match rate adecuado en los 4 eventos de prueba.

2.9 Refunds y soporte

  • Documentar cómo refundear en cada pasarela. R: Cortex. Done cuando: recursos/refund_playbook.md con paso a paso + screenshots por pasarela. Política unificada: refund completo dentro de 7 días sin preguntas.
  • Webhook de refund actualiza Supabase. R: Eric. Done cuando: compras.status = REFUNDED + compras.refund_at = now() + evento CAPI Purchase con value negativo (o evento Refund si la pasarela lo soporta).
  • Trigger post-refund. R: Eric + Cortex. Done cuando: lead removido del grupo WA correspondiente + acceso al curso revocado en Skool + email de confirmación enviado.

3. Variables y posibilidades a anticipar

Escenario Plan B
Hotmart rechaza apertura de cuenta o demora > 7 días Arrancar con dLocal + Shopify como pasarelas activas. Hotmart se suma cuando esté.
dLocal no aprueba productor (más exigente con KYC) Arrancar con Hotmart + Shopify. dLocal suma cuando esté.
Shopify cobra fees de transacción extra (Shopify Payments no disponible en ARG/algunos países) Cobrar con PayPal + tarjeta internacional (mayor fee 2.9% + 30¢). Documentar en pricing.
Skool no tiene cuenta productor → se excluye Marcado en config + selector no muestra. No bloquea launch.
Webhook de pasarela cae temporalmente (caída del lado de ellos) Reconciliación cron cada 1h: pull de transacciones de últimas 24h por API → comparar con compras → upsert faltantes.
Lead paga pero no se le otorga acceso (delivery falla) Tabla delivery_failures + alerta inmediata a Jesús. SLA: resolver en ≤ 1h. Mensaje WA disculpa + acceso manual.
Doble cobro (lead paga 2x el mismo SKU por error) Política: refund automático del 2do cobro detectado dentro de 5 min. Cron de detección.
Chargeback fraudulento Hotmart/dLocal lo manejan automático. Si pierde dispute, marcado en compras + lead a blacklist.
Cambio de precio del SKU mid-week (ej. Martín dice "subimos el taller a $20") Variable en _CONFIG.md > pricing.skus.<sku>.price. Productos en pasarelas se editan manual + redeployar config. Documentar en CHANGELOG.
Pasarela cobra en ARS pero quisimos USD Conversión automática del side-pasarela. Diferencia ajustada en balance mensual.
Tarjeta del lead rechazada por fraude Selector ofrece otra pasarela (botón "Probar con otro método"). Si todas rechazan → handoff a closer + transferencia (checklist 17).
Compra completada pero lead_id no propagó Match por email post-hoc en cron diario. Atribución manual si necesario.
Cambio de comisión Hotmart a 12% Recalcular pricing — ajustar precios de venta o absorber. Documentar.

4. Multi-tenant: cómo se replica al cliente #2

Esta sección alimenta sop/lanzar-cliente/16_pasarelas_pago.md.

  • Variables a externalizar:
  • client_config.json > pricing.skus[] — array de SKUs con id, nombre, precio, descripción, recurrencia
  • client_config.json > pricing.fees.<pasarela> — comisiones documentadas
  • client_config.json > pricing.shopify.ownertooaudience o cliente
  • client_config.json > pricing.skool.enabled — bool
  • client_config.json > checkouts.<pasarela>.<sku> — URLs por pasarela
  • Env vars por cliente: HOTMART_WEBHOOK_TOKEN_<SLUG>, DLOCAL_API_KEY_<SLUG>, SHOPIFY_API_KEY_<SLUG>, SKOOL_API_KEY_<SLUG>
  • Templates a guardar en sop/lanzar-cliente/templates/pagos/:
  • skus_master_TEMPLATE.md — estructura de los 6 SKUs base (cliente puede reducir o sumar)
  • webhook_handlers/ — código TypeScript de los 4 handlers, parametrizado por slug
  • refund_playbook_TEMPLATE.md
  • _CONFIG_pricing_section_TEMPLATE.yml
  • Sustituciones automáticas:
  • {{CLIENTE_SLUG}} → en URLs de redirect post-pago
  • {{CLIENTE_NOMBRE}} → en nombres de productos en pasarelas
  • {{CURRENCY}} → moneda primaria
  • {{COUNTRIES}} → lista de países donde se vende (afecta orden de pasarelas)
  • Componentes reusables 1:1:
  • Selector /[slug]/checkout/[sku]/page.tsx (ya parametrizado por slug)
  • Handlers /api/webhook/[pasarela]/route.ts (leen client_id por SKU prefix o header)
  • Tabla compras con client_id FK
  • Cron de reconciliación
  • Decisión repetible: si cliente nuevo NO tiene Hotmart o dLocal → arrancar con Shopify + transferencia, sumar otras después. SOP debe contemplarlo.

5. Recursos y archivos relacionados

  • 02_LANDINGS_Y_PAGES.md/martin/checkout/[sku] y /martin/admision
  • 08_OFERTA_Y_STACK.md — definición de los 6 SKUs y bonus
  • 13_PIXEL_TRACKING.md — CAPI Purchase event
  • 15_EQUIPO_VENTAS.md — closer manda link de checkout
  • 17_TRANSFERENCIA.md — pasarela #5 (manual)
  • ../02_PLAN_INTERNO_EQUIPO.md — § 2 detalle pasarelas
  • _CONFIG.md > checkouts — registry de las 30 URLs (a llenar)
  • recursos/skus_master.md — fuente de verdad SKUs (a crear)
  • recursos/refund_playbook.md — paso a paso refunds (a crear)
  • recursos/test_compras.md — log de tests E2E (a crear)
  • Hotmart docs: https://developers.hotmart.com/docs/en/
  • dLocal docs: https://docs.dlocal.com/
  • Shopify webhooks: https://shopify.dev/docs/api/admin-rest/2024-01/resources/webhook
  • Skool API (limitada): https://www.skool.com/api

6. Notas y aprendizajes (post-mortem)

Llenar después del primer ciclo (semana 8-12 jun).

  • (vacío)

CHANGELOG

  • 2026-05-05 — JT — Creación inicial. Asumido: Shopify titular TooAudience (multi-tenant friendly). Asumido: Skool ofrece solo 3 SKUs membresía si Martín tiene cuenta. Asumido: dLocal con dLocal Go (checkout hospedado) en MVP. Pendiente confirmar Skool con Martín.