Configuración
1. Panel de Asaas
- https://asaas.com (o sandbox: https://sandbox.asaas.com)
- Configuraciones > Integraciones > API > copiar Access Token
- Configuraciones > Webhooks > Agregar:
- URL:
https://[tenant].studeia.com/api/webhooks/asaas?token=<TU_TOKEN> - Eventos: PAYMENT_RECEIVED, PAYMENT_OVERDUE, PAYMENT_REFUNDED, SUBSCRIPTION_CREATED/UPDATED/DELETED
- URL:
- Personaliza el token (cadena aleatoria, criptográficamente segura)
2. Variables de entorno
ASAAS_API_KEY=$aact_...
ASAAS_WEBHOOK_TOKEN=...generado-en-el-paso-3...
ASAAS_SANDBOX=true # opcional, por defecto false
3. Crear productos en Asaas
Asaas usa un modelo de cobro recurrente diferente al de Stripe. En Studeia, los planes se mapean mediante código (no es necesario crear un producto en el panel de Asaas).
Flujo PIX
Admin hace clic en "Pagar con PIX" en /institution/billing
↓
POST /api/institution/billing/checkout
Body: { planSlug: "mini", provider: "asaas" }
↓
Studeia AsaasBillingProvider.createCheckout():
1. Resuelve asaasCustomerId (crea si no existe — POST /customers)
2. POST /subscriptions con billingType=PIX
3. Asaas retorna QR Code + payload PIX para copiar y pegar
4. Studeia retorna { url } página hosted de Asaas con QR
↓
Admin escanea el QR en la app bancaria, paga
↓
PIX confirma en ~30s (instantáneo)
↓
Webhook PAYMENT_RECEIVED → applyWebhookEvent → promueve Tenant.plan
Hardening (reglas 138, 139)
- Fail-closed: ausencia de ASAAS_WEBHOOK_TOKEN en el env = 503
asaas_not_configured(regla 138). Anti-bypass. - Validación de token: query param ?token=<...> comparado con el env (comparación timing-safe)
- 5xx en fallo: reintenta automáticamente
- PaymentLog idempotente: unique [provider, externalEventId]
- Redacción de PII: rawPayload pasa por redactPaymentPayload() — elimina cpfCnpj, email, name, address, billing_details (regla 139)
Customer split por proveedor
TenantSubscription tiene campos separados:
stripeCustomerId— para StripeasaasCustomerId— para AsaasexternalCustomerId— legado (mantenido como fallback si el proveedor coincide)
resolveCustomerIdForProvider() en create-checkout.ts usa los nuevos campos — NUNCA reutilices un ID entre proveedores.
Cuándo usar Asaas vs Stripe
| Escenario | Recomendado |
|---|---|
| Cliente brasileño con PIX | Asaas |
| Cliente brasileño con boleto | Asaas |
| Cliente brasileño con tarjeta | Asaas (más económico) o Stripe |
| Cliente internacional | Stripe USD obligatorio (Asaas bloqueado) |
| Cuenta empresarial enterprise grande | Stripe (más herramientas — Sigma, informes personalizados) |
Comisiones comparadas
| Método | Asaas | Stripe |
|---|---|---|
| PIX | 1.99% | NO ofrece nativo |
| Boleto | R$3.49 fijo | NO ofrece nativo |
| Tarjeta de crédito | 4.99% + R$0.49 | 3.99% + R$0.59 (USD) |
| Tarjeta de débito | 1.99% + R$0.39 | NO ofrece (BR) |
| Anticipo de cobros | Disponible | NO |
Para volumen B2B brasileño típico: Asaas es ~20-40% más económico en comisiones totales.