rubis/docs/tech/banking-setup.md
ordinarthur 51217175ad
All checks were successful
Build & Deploy Web / build-and-deploy (push) Successful in 38s
Build & Deploy API / build-and-deploy (push) Successful in 1m36s
feat(banking): intégration Powens AISP + auto-réconciliation factures
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>
2026-05-12 14:03:32 +02:00

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

  1. Crée un compte sur https://console.powens.com/
  2. Demande un domaine sandbox (gratuit, banques fakes, pas de SCA réelle).
  3. Récupère :
    • client_id (entier)
    • client_secret (string)
    • Le slug du domaine (ex : rubis-sandbox). L'API tape sur https://<slug>.biapi.pro/2.0/.
  4. 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)

  1. SPA → /parametres → section Banque → clic Connecter une banque.
  2. Webview Powens s'ouvre → choisir Connecteur de test (banque sandbox).
  3. Credentials sandbox standards : login 1234567, password 123456 (à confirmer sur la doc Powens du moment).
  4. Validation → Powens redirige vers ton tunnel → API enregistre la connexion → 302 vers /parametres/banque?banking=connected.
  5. Vérifier que bank_connections a une ligne state='active' et que bank_accounts contient 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_token chiffré au repos via app/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_id est toujours filtré par auth.user.organizationId (anti-IDOR).
  • IBAN masqué dans les logs (FR76 **** **** **** 1234).
  • Rate limit sur /banking/powens/init (anti-abuse).