Cette page s'adresse aux développeurs qui souhaitent créer ou intégrer une passerelle de paiement compatible avec l'interface PaymentController (version 2) introduite dans frappe/payments.
Jusqu'à présent, les passerelles de paiement dans Dokos suivaient une interface dite v1 : chaque intégration implémentait sa propre logique d'initialisation de session et de retour de paiement, directement dans le code de la Demande de paiement.
L'interface v2 (PaymentController) centralise cette logique dans l'application payments et expose un contrat clair via PaymentController.initiate(). La Demande de paiement délègue entièrement à cette classe dès qu'elle détecte une passerelle v2.
Lors de la validation d'une Demande de paiement, Dokos appelle _is_v2_gateway(payment_gateway) pour déterminer quelle branche exécuter :
_process_v2_gateway() est appeléLa détection repose sur payments.utils.is_v2_gateway(). Si l'application payments ne dispose pas de cette fonction (version ancienne), le système bascule silencieusement en mode v1.
Pour qu'une passerelle soit reconnue comme v2, elle doit :
payments.utils.is_v2_gateway() — cette logique est gérée côté application payments.PaymentController (depuis payments.controllers.payment_controller) et implémenter la méthode initiate(tx_data).initiate(tx_data)Cette méthode reçoit un dictionnaire TxData et doit :
payment_url (l'URL vers laquelle le client sera redirigé)TxDataLors d'une demande de paiement v2, Dokos construit un objet TxData via get_tx_data(). Voici les champs transmis :
| Champ | Valeur | Description |
|---|---|---|
reference_doctype | Payment Request | Type du document de référence |
reference_docname | Nom de la Demande de paiement | Identifiant unique de la demande |
payer_name | Nom du tiers (client/fournisseur) | Nom affiché au payeur |
payer_email | Email du contact | Email du payeur |
amount | Résultat de get_request_amount() | Montant partiel ou total |
currency | Devise de la demande | ISO 4217 |
contact | Champs contact filtrés | Voir ci-dessous |
address | Champs adresse filtrés | Voir ci-dessous |
get_request_amount() est utilisé (et non grand_total) afin de prendre en charge les scénarios de paiement partiel. Une facture peut faire l'objet de plusieurs demandes de paiement successives.reference_doctype pointe vers Payment Request (et non la facture ou la commande sous-jacente) car c'est la Demande de paiement qui gère les callbacks et la réconciliation comptable.Seuls les champs pertinents pour le paiement sont transmis (pas le document Contact complet) :
email_idmobile_nophoneaddress_line1address_line2citystatepincodecountryVoici la structure minimale d'une passerelle v2 :
from payments.controllers.payment_controller import PaymentController
class MaPasserelleController(PaymentController):
def initiate(self, tx_data: dict) -> dict:
"""
Initialise une session de paiement.
Args:
tx_data: Dictionnaire TxData fourni par Dokos.
Returns:
dict avec au minimum 'payment_url'.
"""
# 1. Créer la session côté passerelle externe
session = self._create_remote_session(
amount=tx_data["amount"],
currency=tx_data["currency"],
customer_email=tx_data["contact"].get("email_id"),
)
# 2. Créer le Payment Session Log dans Frappe
self.create_payment_session_log(
reference_doctype=tx_data["reference_doctype"],
reference_docname=tx_data["reference_docname"],
session_id=session.id,
)
# 3. Retourner l'URL de paiement
return {"payment_url": session.checkout_url}
Les passerelles v1 existantes ne sont pas affectées. Le code de la Demande de paiement vérifie d'abord si la passerelle est v2 avant toute modification de comportement. En l'absence de l'application payments dans la version requise, le système bascule automatiquement sur le flux v1.
Cette fonctionnalité nécessite :
frappe/payments avec le support PaymentController (PR #192)