Module banking complet en lecture seule via Powens (ex-Budget Insight)
pour détecter automatiquement les paiements clients et arrêter les
relances dès qu'une facture est payée. Réservé plans Pro / Business,
kill switch global BANKING_ENABLED désactivé en prod tant que le KYC
Powens n'est pas validé (cf. .claude/deploy-memory.md).
Backend (apps/api)
- PowensClient bas niveau : init user, code temporaire 30s, build
webview URL, list/get/delete connections, accounts, transactions,
vérif HMAC SHA-256 timing-safe pour webhook.
- BankingService : ensurePowensUser (chiffrement token via Adonis
encryption / APP_KEY), createWebviewUrl avec state HMAC anti-CSRF
(TTL 10 min), handleCallback (upsert connection + accounts +
fire-and-forget mail + sync 90j + reconcile), disconnect (DELETE
Powens + soft-revoke en DB), setReconciliationMode.
- Réconciliation : match transactions ↔ factures sur montant exact
+ label normalisé (numero ou nom client, NFD strip + alphanum).
Confiance HIGH (label matche) vs LOW (montant seul). Mode auto +
HIGH → invoice.status=paid + bonus rubis + cancel relances +
enqueuePaymentThanks (client) + sendInvoiceAutoPaidNotification
(user). Mode manual ou LOW → match_status='suggested' (UI V2).
- Webhook /webhooks/powens : vérif HMAC, lookup org par
powens_user_id, dispatch CONNECTION_SYNCED / NEW_TRANSACTIONS /
USER_SYNC_ENDED → sync incrémental 7j + reconcile, CONNECTION_ERROR
/ SCA_REQUIRED → update state + last_error. Réponse 200 immédiate
puis processing fire-and-forget pour ne pas timeout côté Powens.
- 4 migrations : bank_connections, bank_accounts, bank_transactions
+ colonnes powens_user_id (chiffré APP_KEY) et reconciliation_mode
sur organizations.
- 2 templates React Email : BankConnectedEmail (post-connection,
récap comptes + lien settings) et InvoiceAutoPaidNotificationEmail
(notif user après match auto, lien direct facture + libellé
bancaire détecté). Toujours en branding Rubis (notif Rubis → user,
jamais marque blanche).
- 2 commandes ace : banking:reconcile (rejoue le reconcile sans
reconnecter la banque) et banking:simulate-payment (injecte une
bank_transaction synthétique qui matche une facture, pour test E2E
sans devoir attendre un vrai virement sandbox).
- Kill switch isBankingEnabled() : flag BANKING_ENABLED + check des
credentials Powens. Endpoint public GET /banking/status renvoie
{ enabled }, /banking/powens/init throw 503 banking_disabled si OFF.
- Fix handler exceptions : UNIQUE violation composite (org, X)
rapporte désormais la vraie colonne en faute (numero/slug/…) avec
message lisible « Le numéro de facture "F2026-0013" existe déjà »,
au lieu d'un message ambigu sur organization_id.
Frontend (apps/web)
- /parametres : nouvelle SettingsSection "Banque" gated par kill
switch + plan Pro/Business. Si Free → upsell card avec CTA vers
/parametres/abonnement. Si Pro/Business sans banque → CTA "Connecter
une banque". Si banque connectée → carte avec accounts (IBAN
masqué FR76 **** **** **** 1234), solde, last sync, bouton
Déconnecter. Toggle Manuel/Auto pour reconciliation_mode.
- /parametres/banque/success : nouvelle route dédiée post-callback
avec badge ✓ animé + halo glow rubis, récap des comptes
synchronisés, 2 CTAs ("Voir mes paramètres" / "Retour dashboard"),
note sécurité "lecture seule, aucun déplacement de fonds".
- Hooks : useBankingStatus, useBankConnections (avec opt-out via
{ enabled }), useInitBanking, useDisconnectBank, useBankingSettings,
useUpdateBankingSettings.
Infrastructure (k3s)
- ConfigMap rubis-api-config : BANKING_ENABLED='false' par défaut,
BANKING_PROVIDER='powens', POWENS_DOMAIN='rubis',
POWENS_API_BASE_URL='https://rubis.biapi.pro/2.0/',
POWENS_REDIRECT_URI='https://app.rubis.pro/api/v1/banking/powens/callback'.
- Secret rubis-app-secrets : 3 nouvelles clés POWENS_CLIENT_ID,
POWENS_CLIENT_SECRET, POWENS_WEBHOOK_SECRET (valeurs sandbox posées
via kubectl patch, à remplacer post-KYC).
Sécurité
- Token Powens chiffré au repos via Adonis encryption (AES-256-GCM,
clé APP_KEY).
- State HMAC SHA-256 signé sur APP_KEY pour le flow webview
(anti-CSRF + porte l'org_id à travers le redirect).
- Webhook HMAC SHA-256 sur header BI-Signature avec
POWENS_WEBHOOK_SECRET, comparaison timing-safe.
- IBAN masqué côté API (transformer).
- Scope par org sur tous les endpoints (anti-IDOR).
- Rate limiting via le middleware Adonis existant.
- Idempotence DB : UNIQUE (org, powens_connection_id), (connection,
powens_account_id), (account, powens_id) → rejouer un event ou un
callback ne pose pas de problème.
Documentation
- /docs/tech/banking-setup.md : procédure complète setup dev avec
Cloudflare Quick Tunnel, compte sandbox Powens, whitelist URLs.
- /.claude/deploy-memory.md : section "Banking (Powens) — activation
prod" avec procédure en 6 étapes (KYC → secrets → ConfigMap →
flip flag → smoke test), snippet kubectl patch pour rotation
ciblée de secrets.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
5.2 KiB
Banking setup — Powens (AISP) en dev local
Version : 0.1 · 2026-05-12
L'intégration banking utilise Powens (ex-Budget Insight) comme agrégateur AISP (Account Information Service Provider). On lit les transactions des comptes pros de l'user pour détecter les virements entrants et matcher avec les factures émises. Lecture seule — Rubis ne déplace jamais d'argent.
Architecture du flux
[Front /parametres]
│ POST /api/v1/banking/powens/init
▼
[API] crée/récupère le user Powens de l'org
génère un code temporaire
renvoie webviewUrl
│
▼
[Powens webview] user choisit sa banque + se logue
│
▼ redirige sur POWENS_REDIRECT_URI
[API] /api/v1/banking/powens/callback
valide le state HMAC
fetch la connection, accounts, transactions
302 vers WEB_URL/parametres/banque?banking=connected
│
▼
[Front] poll /banking/connections, affiche la banque connectée
Webhook Powens (POST /api/v1/webhooks/powens) garde les transactions fraîches sans cron lourd.
Pourquoi un tunnel HTTPS en dev
Powens refuse les redirect_uri en http:// ou sur localhost. Il faut une URL HTTPS publique whitelistée dans la console Powens. En dev local, on monte un tunnel Cloudflare Quick Tunnel (gratuit, sans compte, sans config DNS).
1. Compte Powens sandbox
- Crée un compte sur https://console.powens.com/
- Demande un domaine sandbox (gratuit, banques fakes, pas de SCA réelle).
- Récupère :
client_id(entier)client_secret(string)- Le slug du domaine (ex :
rubis-sandbox). L'API tape surhttps://<slug>.biapi.pro/2.0/.
- Génère un webhook secret dans la console (sera utilisé pour la vérif HMAC).
Renseigne dans apps/api/.env :
BANKING_ENABLED=true
BANKING_PROVIDER=powens
POWENS_DOMAIN=rubis-sandbox
POWENS_API_BASE_URL=https://rubis-sandbox.biapi.pro/2.0/
POWENS_CLIENT_ID=...
POWENS_CLIENT_SECRET=...
POWENS_WEBHOOK_SECRET=...
2. Tunnel HTTPS — Cloudflare Quick Tunnel
Installer cloudflared
brew install cloudflared # macOS
# ou : https://github.com/cloudflare/cloudflared/releases
Lancer le tunnel sur l'API Adonis (port 3333)
cloudflared tunnel --url http://localhost:3333
Sortie attendue :
+--------------------------------------------------------------------------------------------+
| Your quick Tunnel has been created! Visit it at (it may take a few moments to be reachable): |
| https://random-name-here.trycloudflare.com |
+--------------------------------------------------------------------------------------------+
Copie cette URL.
Renseigner POWENS_REDIRECT_URI
Dans apps/api/.env :
POWENS_REDIRECT_URI=https://random-name-here.trycloudflare.com/api/v1/banking/powens/callback
Redémarre pnpm dev:api pour prendre la nouvelle valeur.
Whitelister l'URL côté Powens
Console Powens → ton appli → Allowed redirect URIs → ajoute :
https://random-name-here.trycloudflare.com/api/v1/banking/powens/callback
Et l'URL prod (à ajouter dès qu'on déploie) :
https://app.rubis.pro/api/v1/banking/powens/callback
Whitelister l'URL webhook
Même console → Webhooks → ajoute :
https://random-name-here.trycloudflare.com/api/v1/webhooks/powens
⚠️ L'URL Cloudflare Quick Tunnel change à chaque restart du
cloudflared. Faut re-whitelister et update le.envà chaque session. Pour du long terme, prendre un ngrok payant (~10$/mois, URL stable) ou monter un Cloudflare Tunnel nommé avec un domaine perso.
3. Tester de bout en bout (sandbox)
- SPA →
/parametres→ section Banque → clic Connecter une banque. - Webview Powens s'ouvre → choisir Connecteur de test (banque sandbox).
- Credentials sandbox standards : login
1234567, password123456(à confirmer sur la doc Powens du moment). - Validation → Powens redirige vers ton tunnel → API enregistre la connexion → 302 vers
/parametres/banque?banking=connected. - Vérifier que
bank_connectionsa une lignestate='active'et quebank_accountscontient les comptes du connecteur de test.
4. Prod
- Domaine Powens prod (KYC à valider).
POWENS_REDIRECT_URI=https://app.rubis.pro/api/v1/banking/powens/callback.POWENS_API_BASE_URL=https://<slug-prod>.biapi.pro/2.0/.- Webhook URL whitelistée :
https://app.rubis.pro/api/v1/webhooks/powens.
5. Gating plan
Le module banking est réservé aux plans Pro et Business. Le middleware requirePaidPlan sur les routes /banking/* renvoie 403 pour les comptes Free. La section banking dans la SPA affiche un upsell pour les Free.
6. Sécurité — résumé
powens_tokenchiffré au repos viaapp/services/encryption(cléAPP_KEY).- State HMAC signé sur la webview, vérifié au callback (anti-CSRF).
- Webhook HMAC (
POWENS_WEBHOOK_SECRET) vérifié avant tout traitement. - Scope par org : un
connection_idest toujours filtré parauth.user.organizationId(anti-IDOR). - IBAN masqué dans les logs (
FR76 **** **** **** 1234). - Rate limit sur
/banking/powens/init(anti-abuse).