16 — Pasarelas de Pago (Hotmart / dLocal / Shopify / Skool)
Índice de la página
- 01Cliente: Martín Rieznik / LevantArte
- Área: Pagos — pasarelas externas (4 plataformas × 6 SKUs = 24 productos manuales)
- 020. Definición de "done" para esta checklist
- 031. Pre-requisitos
- 042. Tareas
- 2.1 SKUs maestros (registro central)
- 2.2 Hotmart (productor de Martín)
- 2.3 dLocal (productor de Martín)
- 2.4 Shopify (definir titularidad)
- 2.5 Skool (condicional)
- 2.6 Selector de pasarela en /martin/checkout/[sku]
- 2.7 Webhooks unificados y CAPI
- 2.8 Tests E2E
- 2.9 Refunds y soporte
- 053. Variables y posibilidades a anticipar
- 064. Multi-tenant: cómo se replica al cliente #2
- 075. Recursos y archivos relacionados
- 086. Notas y aprendizajes (post-mortem)
- 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 encompras(status APPROVED/REFUNDED) y disparan eventoPurchaseserver-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.tsdecide 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/hotmartcon eventos:PURCHASE_APPROVED,PURCHASE_REFUNDED,PURCHASE_CHARGEBACK,SUBSCRIPTION_CANCELLATION. Token compartido en env varHOTMART_WEBHOOK_TOKEN. - Implementar handler
/api/webhook/hotmart. R: Eric. Done cuando: valida HMAC, parsea evento, mapea SKU Hotmart → SKU interno, INSERT encomprascon status correcto, disparaPurchasea CAPI conevent_idúnico =transaction_id_externo(deduplicación). - Copiar 6 checkout URLs a
_CONFIG.md. R: Jesús. Done cuando: bloquecheckouts.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: bloquecheckouts.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 mastertooaudience-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
RechargeoBold Subscriptions— instalar). - App de suscripciones Shopify. R: Eric. Done cuando: app
Bold SubscriptionsoRechargeinstalada 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/updateapuntando ahttps://deacademia.com/api/webhook/shopify. Firma HMAC conSHOPIFY_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 directohttps://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: falseen 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.tsxlee:
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 eventocheckout_to_humanen Supabase. - Tracking de evento
checkout_clicked. R: Eric. Done cuando: cada click en botón de pasarela disparacheckout_clickedcon{sku, pasarela, lead_id}a Supabaseeventos. - Persistencia de UTMs y
lead_id. R: Eric. Done cuando: query paramslead_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>.tsexportahandle(event)que: 1) valida firma 2) extraesku, precio, transaction_id, status, customer_email, customer_name3) llama acompras.upsert()4) si APPROVED, llama acapi.purchase()5) si APPROVED, llama adelivery.grant_access(sku, lead)(acceso al producto: agregar a grupo WA, activar en Skool, etc.). - Idempotencia de webhooks. R: Eric. Done cuando:
comprastiene 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_transactionidpara deduplicación con Pixel client-side. - Logs de webhooks fallidos. R: Eric. Done cuando: tabla
webhook_failuresregistra 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.mdcon 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.mdcon 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 CAPIPurchaseconvaluenegativo (o eventoRefundsi 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, recurrenciaclient_config.json > pricing.fees.<pasarela>— comisiones documentadasclient_config.json > pricing.shopify.owner—tooaudienceoclienteclient_config.json > pricing.skool.enabled— boolclient_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 slugrefund_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
comprasconclient_idFK - 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/admision08_OFERTA_Y_STACK.md— definición de los 6 SKUs y bonus13_PIXEL_TRACKING.md— CAPI Purchase event15_EQUIPO_VENTAS.md— closer manda link de checkout17_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.