Intégration Stripe (paiements & caution)

Statut : non implémenté. Cette page documente comment l'intégration Stripe sera câblée (acompte 30% + caution pré-autorisée, cf. docs/plateforme-conciergerie-deployment-plan.md). Aucun code Stripe n'existe encore dans ce dépôt — c'est une référence pour le futur développement, pas un mode d'emploi d'une fonctionnalité déjà en ligne.

1. Obtenir les clés API Stripe

Dans le Dashboard Stripe, aller dans Developers > API keys. Stripe fournit deux jeux de clés :

  • Mode test (clés préfixées sk_test_... / pk_test_...) : aucun mouvement d'argent réel, à utiliser pour tout le développement et les tests de recette.
  • Mode live (clés préfixées sk_live_... / pk_live_...) : mouvements réels, à activer seulement une fois l'intégration validée en test.

La clé secrète (sk_...) ne doit jamais être exposée côté client ni committée dans le dépôt ; seule la clé publique (pk_...) est destinée au navigateur/formulaire de paiement.

2. Variables d'environnement prévues

Une fois l'intégration codée, l'app lirait ces variables (même schéma que BEDS24_API_TOKEN aujourd'hui — via le service app de docker-compose.yml, jamais en clair dans le dépôt) :

  • STRIPE_SECRET_KEY — clé secrète utilisée côté serveur pour créer les PaymentIntents et interroger l'API Stripe.
  • STRIPE_PUBLISHABLE_KEY — clé publique injectée dans la page de paiement (formulaire Stripe Elements/Payment Element) côté navigateur.
  • STRIPE_WEBHOOK_SECRET — secret de signature utilisé pour vérifier l'authenticité des événements webhook reçus (voir section 5).

3. Flux de l'acompte (30%) — capture automatique

Selon le plan de déploiement, à la réservation directe le client verse un acompte de 30% du montant du séjour. Ce flux correspond à un PaymentIntent Stripe standard, en capture automatique (capture_method: automatic, la valeur par défaut) : le montant est débité immédiatement à la confirmation du paiement. Une fois implémenté, chaque paiement d'acompte réussi serait enregistré comme une ligne dans payments avec kind = 'deposit' et stripe_payment_intent renseigné avec l'identifiant du PaymentIntent Stripe correspondant.

4. Flux de la caution (dépôt de garantie) — capture manuelle

La caution fonctionne différemment : c'est une autorisation, pas un débit. Le mécanisme Stripe adapté est un PaymentIntent en capture manuelle (capture_method: manual) :

  1. À l'arrivée (ou à la réservation, selon la politique retenue), on crée un PaymentIntent en capture manuelle : la carte du client est autorisée (les fonds sont bloqués) mais pas débitée.
  2. Si un dégât est constaté au départ, on capture tout ou partie du montant autorisé (PaymentIntent.capture) — c'est à ce moment que l'argent est effectivement prélevé.
  3. Si tout est en ordre, on annule l'autorisation (PaymentIntent.cancel) après le départ et un délai de grâce (le temps de l'inspection du logement) — les fonds sont alors libérés sans qu'aucun débit n'ait lieu.

Différence clé avec l'acompte : l'acompte débite tout de suite (capture automatique), la caution ne fait qu'autoriser puis capture ou annule plus tard (capture manuelle). Une autorisation Stripe non capturée expire au bout de 7 jours (cartes) — le délai de grâce doit rester dans cette fenêtre, ou prévoir une nouvelle autorisation.

Une fois implémenté, ces paiements seraient enregistrés dans payments avec kind = 'security_hold' (cf. contrainte CHECK (kind IN ('deposit', 'security_hold', 'balance')) du schéma), et stripe_payment_intent pointant vers le PaymentIntent d'autorisation.

5. Webhooks

Stripe notifie l'application de manière asynchrone via des webhooks signés. Les événements pertinents pour ce flux seraient :

  • payment_intent.succeeded — l'acompte (capture auto) a été débité avec succès, ou une caution a été capturée suite à un dégât.
  • payment_intent.canceled — l'autorisation de caution a été annulée (libération des fonds sans débit).
  • charge.dispute.created — un client conteste un prélèvement (utile en particulier pour un débit de caution suite à dégât, pour suivre les litiges).

STRIPE_WEBHOOK_SECRET sert à vérifier la signature de chaque requête webhook reçue (en-tête Stripe-Signature) afin de s'assurer qu'elle provient bien de Stripe et n'a pas été forgée.

6. Étapes pour passer en production

  1. Développer et tester intégralement en mode test, avec les cartes de test Stripe (succès, refus, 3-D Secure, etc.) pour valider les deux flux (acompte capture auto, caution capture manuelle + annulation).
  2. Tester le webhook en local avec le CLI Stripe (stripe listen --forward-to) puis en environnement de recette avec une URL publique.
  3. Une fois la recette validée, basculer STRIPE_SECRET_KEY, STRIPE_PUBLISHABLE_KEY et STRIPE_WEBHOOK_SECRET vers les valeurs live dans la configuration du service app.