Configuration
1. Dashboard Asaas
- https://asaas.com (ou sandbox : https://sandbox.asaas.com)
- Paramètres > Intégrations > API > copiez l'Access Token
- Paramètres > Webhooks > Ajouter :
- URL :
https://[tenant].studeia.com/api/webhooks/asaas?token=<VOTRE_TOKEN> - Événements : PAYMENT_RECEIVED, PAYMENT_OVERDUE, PAYMENT_REFUNDED, SUBSCRIPTION_CREATED/UPDATED/DELETED
- URL :
- Personnalisez le token (chaîne aléatoire, cryptographiquement forte)
2. Variables d'environnement
ASAAS_API_KEY=$aact_...
ASAAS_WEBHOOK_TOKEN=...généré-à-l-étape-3...
ASAAS_SANDBOX=true # optionnel, défaut false
3. Créer des produits dans Asaas
Asaas utilise un modèle de facturation récurrente différent de Stripe. Dans Studeia, les plans sont mappés via un code (il n'est pas nécessaire de créer un produit dans le dashboard Asaas).
Flux PIX
L'admin clique sur "Payer avec PIX" dans /institution/billing
↓
POST /api/institution/billing/checkout
Body: { planSlug: "mini", provider: "asaas" }
↓
Studeia AsaasBillingProvider.createCheckout() :
1. Résout asaasCustomerId (crée si inexistant — POST /customers)
2. POST /subscriptions avec billingType=PIX
3. Asaas retourne QR Code + payload PIX copier-coller
4. Studeia retourne { url } page hébergée Asaas avec QR
↓
L'admin scanne le QR dans son application bancaire, paie
↓
PIX confirme en ~30s (instantané)
↓
Webhook PAYMENT_RECEIVED → applyWebhookEvent → promeut Tenant.plan
Durcissement (règles 138, 139)
- Fail-closed : absence de la variable d'env ASAAS_WEBHOOK_TOKEN = 503
asaas_not_configured(règle 138). Anti-contournement. - Validation du token : query param ?token=<...> comparé avec l'env (comparaison timing-safe)
- 5xx en cas d'échec : nouvelles tentatives automatiques
- PaymentLog idempotent : unique [provider, externalEventId]
- Expurgation des données personnelles : rawPayload passe par redactPaymentPayload() — supprime cpfCnpj, email, name, address, billing_details (règle 139)
Customer split par provider
TenantSubscription dispose de champs séparés :
stripeCustomerId— pour StripeasaasCustomerId— pour AsaasexternalCustomerId— héritage (maintenu comme fallback si le provider correspond)
resolveCustomerIdForProvider() dans create-checkout.ts utilise les nouveaux champs — NE JAMAIS réutiliser un ID cross-provider.
Quand utiliser Asaas vs Stripe
| Scénario | Recommandé |
|---|---|
| Client brésilien avec PIX | Asaas |
| Client brésilien avec boleto | Asaas |
| Client brésilien avec carte | Asaas (moins cher) ou Stripe |
| Client international | Stripe USD obligatoire (Asaas bloqué) |
| Grand compte entreprise enterprise | Stripe (plus d'outils — Sigma, rapports personnalisés) |
Comparatif des frais
| Méthode | Asaas | Stripe |
|---|---|---|
| PIX | 1,99% | Non proposé nativement |
| Boleto | R$3,49 fixe | Non proposé nativement |
| Carte de crédit | 4,99% + R$0,49 | 3,99% + R$0,59 (USD) |
| Carte de débit | 1,99% + R$0,39 | Non proposé (BR) |
| Anticipation des créances | Disponible | Non |
Pour un volume B2B brésilien typique : Asaas est ~20-40% moins cher en frais totaux.