Aller au contenu

Abonnements et paiements

Ce document décrit les offres d'abonnement, les moyens de paiement acceptés et l'intégration de la passerelle de paiement Djomy (page de paiement hébergée, webhook, API mobile).


Offres d'abonnement

Les offres sont stockées en base (entité Plan) et administrables depuis le back-office (/admin/plans). Chaque plan porte :

Champ Description
name, description Intitulé et présentation de l'offre
monthlyPrice, yearlyPrice Tarifs mensuel et annuel
features Liste des fonctionnalités incluses
cardsIncluded Nombre de cartes comprises dans l'offre
additionalCardPrice Tarif d'une carte supplémentaire
badge, popular Mise en avant dans la grille tarifaire
isActive Visibilité de l'offre
sortOrder Ordre d'affichage

Un abonnement souscrit est représenté par l'entité Subscription :

Statut Signification
PENDING Souscrit, en attente de règlement
ACTIVE Actif
CANCELLED Résilié
EXPIRED Échu

Périodicité de facturation : MONTHLY ou YEARLY.

Tout compte ROLE_USER dépourvu d'abonnement se voit attribuer automatiquement un abonnement gratuit lors de son premier accès au tableau de bord (SubscriptionService::createFreeSubscriptionForUser()).


Moyens de paiement

L'entité Payment reconnaît les moyens suivants :

Constante Valeur Usage
DJOMY DJOMY Paiement en ligne via la page hébergée Djomy (web et API mobile)
CREDIT_CARD CREDIT_CARD Valeur historique (ancien paiement par carte Stripe), plus utilisée pour de nouveaux paiements
ORANGE_MONEY ORANGE_MONEY Paiement mobile — non implémenté : six contrôleurs rejettent ce moyen de paiement dès sa sélection (voir Configuration § Paiements)
DELIVERY_CASH DELIVERY_CASH Paiement à la livraison
PRO_SUBSCRIPTION PRO_SUBSCRIPTION Règlement d'un abonnement professionnel
COMPANY_FREE COMPANY_FREE Attribution gratuite à une entreprise
MANUAL MANUAL Saisie manuelle par un administrateur

Statuts d'un paiement :

Constante Valeur
PENDING_STATUS PENDING
ACCEPTED_STATUS ACCEPTED
COMPLETED_STATUS COMPLETED
REJECTED_STATUS REJECTED

Intégration Djomy

Djomy (passerelle guinéenne, mode test « sandbox » ou production) remplace Stripe sur tous les parcours, web et API mobile. Le client est redirigé vers une page de paiement hébergée par Djomy, où il choisit son moyen de paiement (Orange Money, MTN MoMo, Soutra Money, PayCard, Kulu, carte). Les montants sont toujours en GNF. Djomy ne renvoie pas le client vers BCard ni vers l'application mobile après le paiement.

Configuration

Variable Rôle
DJOMY_API_URL URL de l'API Djomy (sandbox ou production)
DJOMY_CLIENT_ID Identifiant client
DJOMY_CLIENT_SECRET Secret client, aussi utilisé pour vérifier la signature des webhooks

Ces valeurs sont des secrets : elles ne doivent jamais être committées. Sans elles, le paiement en ligne répond « indisponible » (message sur le web, HTTP 503 sur l'API). Le guide de test est dans paiement-djomy-tests.md.

Composants

Composant Rôle
DjomyService Client de l'API : jeton, création de la transaction, lecture du statut, signature du webhook, normalisation du téléphone
DjomyCheckoutService Démarre le paiement d'un Payment (référence BCARD-PAY-<id>, jeton de retour, règle des 10 minutes sur une transaction encore PENDING)
PaymentFulfillmentService Valide un paiement réussi : idempotent et verrouillé, il active l'abonnement ou confirme la commande ou la carte physique
MobilePaymentService Briques communes de l'API mobile : relecture du statut, réponses de démarrage et d'erreur
DjomyReturnController Retour du navigateur web (/payment/djomy/return/{token})
DjomyWebhookController Webhook Djomy (POST /webhook/djomy)

Règle d'or : seul un statut Djomy SUCCESS, relu auprès de Djomy, prouve un paiement. Le corps d'un webhook ou d'un retour de navigateur n'est jamais cru tel quel.

Trois chemins de confirmation

Tous aboutissent à PaymentFulfillmentService::fulfill() :

  1. Webhook POST /webhook/djomy : signature X-Webhook-Signature vérifiée, puis statut relu chez Djomy (statut SUCCESS, devise GNF, référence marchande désignant bien le paiement).
  2. Retour navigateur (web uniquement) : le statut est relu chez Djomy.
  3. Vérification de statut (API mobile) : GET /api/payments/{id}/status relit le statut chez Djomy (même contrôle de référence que le webhook, source api_status).

Une tâche planifiée de réconciliation sert de filet de sécurité (voir deploiement.md).

API mobile

L'application Flutter n'utilise plus de SDK Stripe : elle ouvre l'redirect_url renvoyée par l'API dans un navigateur, puis interroge le statut du paiement.

Endpoint Rôle
POST /api/subscription/upgrade-to-pro Crée (ou reprend) l'abonnement Pro PENDING et son paiement, renvoie redirect_url (201)
POST /api/physical-cards/{id}/pay Crée (ou reprend) la commande et le paiement de la carte, renvoie redirect_url (201)
GET /api/payments/{id}/status Relit le statut chez Djomy : COMPLETED, PENDING ou FAILED
POST /api/subscription/confirm-payment Conservé pour compatibilité : même vérification, payment_id dans le corps
POST /api/physical-cards/{id}/confirm-payment Conservé pour compatibilité : même vérification pour le paiement de la carte

Erreurs de démarrage : 400 (téléphone ou moyen de paiement invalide), 409 (paiement déjà en cours ou déjà confirmé), 503 (Djomy non configuré ou indisponible). Détail et exemples : api-mobile-paiement-djomy.md.

Colonnes Stripe conservées

Les colonnes stripe_payment_intent_id (Payment), stripe_session_id (Order) et stripe_subscription_id (Subscription) sont conservées en base pour l'historique ; plus aucun code ne les écrit et la bibliothèque Stripe n'est plus appelée.


Cycle de vie d'une commande de carte physique

Panier
  └─> Commande créée            Order: DRAFT / En attente de paiement
        └─> Paiement initié     Payment: PENDING      PhysicalCard: PENDING_PAYMENT
              └─> Djomy confirme  Payment: COMPLETED  PhysicalCard: CONFIRMED
                    └─> Fabrication                   PhysicalCard: PROCESSING
                          └─> Expédition              PhysicalCard: SHIPPED
                                └─> Réception         PhysicalCard: DELIVERED

Les changements de statut opérés depuis le back-office sont consignés dans le journal d'audit (AuditLog::ACTION_ORDER_STATUS_UPDATED).


Factures

Les factures sont générées en PDF par DomPdfService et téléchargeables depuis le back-office :

  • /admin/invoice/payment/{id} — facture d'un paiement
  • /admin/pro-payments/{id}/invoice — facture d'un paiement professionnel