From 5a2e34211d0c584847176738b3c40a8b3bdda9bc Mon Sep 17 00:00:00 2001 From: mthiam Date: Tue, 10 Feb 2026 23:11:54 +0100 Subject: [PATCH] docs(payment_paydunya): documente les methodes en francais --- addons/payment_paydunya/controllers/main.py | 21 +++--- .../models/payment_transaction.py | 65 ++++++++++--------- 2 files changed, 44 insertions(+), 42 deletions(-) diff --git a/addons/payment_paydunya/controllers/main.py b/addons/payment_paydunya/controllers/main.py index adc1e65..0cd4393 100644 --- a/addons/payment_paydunya/controllers/main.py +++ b/addons/payment_paydunya/controllers/main.py @@ -9,7 +9,7 @@ _logger = logging.getLogger(__name__) class PaydunyaController(http.Controller): @http.route('/payment/paydunya/return', type='http', auth='public', methods=['GET'], csrf=False) def paydunya_return(self, **kwargs): - """Handle return from PayDunya after payment attempt.""" + """Traite le retour utilisateur PayDunya après tentative de paiement.""" token = kwargs.get('token') or http.request.params.get('token') tx_id = kwargs.get('tx_id') or http.request.params.get('tx_id') sale_order_id = kwargs.get('sale_order_id') or http.request.params.get('sale_order_id') @@ -29,7 +29,7 @@ class PaydunyaController(http.Controller): data = {} if token and provider: - # Build check URL using the confirm endpoint + # Construit l'URL de vérification via l'endpoint de confirmation. base = provider._get_paydunya_api_base() confirm_url = base + '/checkout-invoice/confirm/{}'.format(token) @@ -41,7 +41,7 @@ class PaydunyaController(http.Controller): } try: - # GET request to verify status + # Vérifie le statut de paiement côté PayDunya. resp = requests.get(confirm_url, headers=headers, timeout=15) data = resp.json() _logger.info('PayDunya confirm response: %s', data) @@ -49,12 +49,12 @@ class PaydunyaController(http.Controller): _logger.exception('Error verifying PayDunya invoice: %s', e) data = {'token': token, 'status': 'failed', 'sale_order_id': sale_order_id, 'tx_id': tx_id} elif tx: - # Fallback when PayDunya did not return token: rely on tx current state. + # Fallback: si PayDunya ne renvoie pas de token, on s'appuie sur la transaction. data = {'tx_id': tx.id, 'sale_order_id': sale_order_id} else: return http.request.redirect('/') - # Trigger transaction handling with the confirmed data + # Déclenche la mise à jour de la transaction avec les données confirmées. try: handled = tx_model._handle_notification_data(data) tx = tx or tx_model._get_tx_from_notification(data) @@ -73,16 +73,17 @@ class PaydunyaController(http.Controller): @http.route('/payment/paydunya/cancel', type='http', auth='public', methods=['GET'], csrf=False) def paydunya_cancel(self, **kwargs): - """Handle cancellation from PayDunya.""" + """Traite l'annulation de paiement renvoyée par PayDunya.""" token = kwargs.get('token') or http.request.params.get('token') _logger.info('PayDunya payment cancelled: token=%s', token) return http.request.redirect('/shop/cart') @http.route('/payment/paydunya/callback', type='http', auth='public', methods=['GET', 'POST'], csrf=False) def paydunya_callback(self, **kwargs): - """Handle IPN callback from PayDunya. - - PayDunya sends data as application/x-www-form-urlencoded with 'data' key containing JSON. + """Traite le callback IPN envoyé par PayDunya. + + PayDunya peut envoyer des données en `application/x-www-form-urlencoded` + dans une clé `data` contenant du JSON. """ try: payload = {} @@ -105,5 +106,5 @@ class PaydunyaController(http.Controller): except Exception: _logger.exception('Error handling PayDunya callback') - # Always return 200 OK to PayDunya + # Retourne toujours HTTP 200 à PayDunya. return 'OK' diff --git a/addons/payment_paydunya/models/payment_transaction.py b/addons/payment_paydunya/models/payment_transaction.py index 80924b7..5a7ceac 100644 --- a/addons/payment_paydunya/models/payment_transaction.py +++ b/addons/payment_paydunya/models/payment_transaction.py @@ -11,7 +11,7 @@ class PaymentTransaction(models.Model): _inherit = 'payment.transaction' def _paydunya_assign_transaction_payment_method_line(self): - """Ensure transaction has a valid journal/payment method line for account payment.""" + """Assigne un journal et une méthode de paiement valides à la transaction.""" self.ensure_one() if 'payment_method_line_id' not in self._fields: return False @@ -39,7 +39,7 @@ class PaymentTransaction(models.Model): return True def _paydunya_extract_sale_order_id(self, payload): - """Extract a sale order id from return/callback payload.""" + """Extrait l'ID de commande depuis la charge utile retour/callback.""" if not isinstance(payload, dict): return None @@ -64,7 +64,7 @@ class PaymentTransaction(models.Model): return None def _paydunya_extract_tx_id(self, payload): - """Extract an Odoo transaction id from return/callback payload.""" + """Extrait l'ID de transaction Odoo depuis la charge utile retour/callback.""" if not isinstance(payload, dict): return None @@ -88,7 +88,7 @@ class PaymentTransaction(models.Model): return None def _paydunya_get_related_sale_order(self, sale_order_id=None): - """Locate the sale order linked to this transaction.""" + """Retrouve la commande liée à la transaction.""" self.ensure_one() if 'sale.order' not in self.env: return False @@ -114,7 +114,7 @@ class PaymentTransaction(models.Model): return self.env['sale.order'] def _paydunya_finalize_sale_order(self, sale_order_id=None): - """Confirm sale order and create/post invoice when transaction is paid.""" + """Confirme la commande et crée/poste la facture quand le paiement est validé.""" self.ensure_one() if 'sale.order' not in self.env: return False @@ -145,8 +145,8 @@ class PaymentTransaction(models.Model): 'payment_date': fields.Date.context_today(self), }) - # Force a valid journal/method line to avoid - # "Please define a payment method line on your payment." + # Force un journal + une méthode valides pour éviter + # l'erreur "Please define a payment method line on your payment." if not wizard.journal_id: company = posted_invoices[:1].company_id journal = self.env['account.journal'].search([ @@ -180,12 +180,12 @@ class PaymentTransaction(models.Model): return True def _normalize_paydunya_notification_data(self, data): - """Normalize PayDunya notification payload across callback and confirm formats.""" + """Normalise les payloads PayDunya (callback/IPN et confirmation).""" payload = data if not isinstance(payload, dict): return {} - # Callback can send {"data": ""} or {"data": {...}} + # Le callback peut envoyer {"data": ""} ou {"data": {...}}. if 'data' in payload: wrapped = payload.get('data') if isinstance(wrapped, str): @@ -196,14 +196,14 @@ class PaymentTransaction(models.Model): elif isinstance(wrapped, dict): payload = wrapped - # Confirm endpoint can send {"response_data": {...}} + # L'endpoint de confirmation peut envoyer {"response_data": {...}}. if isinstance(payload, dict) and isinstance(payload.get('response_data'), dict): payload = payload['response_data'] return payload if isinstance(payload, dict) else {} def _get_paydunya_channel(self, processing_values): - """Return the PayDunya channel to force based on selected payment method.""" + """Détermine le canal PayDunya à forcer selon le moyen de paiement choisi.""" self.ensure_one() selected_code = ( @@ -212,7 +212,7 @@ class PaymentTransaction(models.Model): or getattr(getattr(self, 'payment_method_id', False), 'code', False) ) - # Odoo checkout often sends only payment_method_id; resolve it to code. + # Le checkout Odoo envoie souvent seulement `payment_method_id`. if not selected_code: pm_id = processing_values.get('payment_method_id') if pm_id: @@ -237,10 +237,10 @@ class PaymentTransaction(models.Model): return channel_map.get(str(selected_code).lower()) def _get_specific_rendering_values(self, processing_values): - """Create invoice on PayDunya and return rendering values for redirection.""" + """Crée la facture côté PayDunya et renvoie les données de redirection.""" self.ensure_one() provider = False - # try common fields used across Odoo versions + # Compatibilité entre versions Odoo pour récupérer le provider. provider = getattr(self, 'provider_id', False) or getattr(self, 'acquirer_id', False) if not provider: provider = self.env['payment.provider'].search([('code', '=', 'paydunya')], limit=1) @@ -260,8 +260,8 @@ class PaymentTransaction(models.Model): cancel_url = base_url + '/payment/paydunya/cancel' + query callback_url = base_url + '/payment/paydunya/callback' + query - # Build payload according to PayDunya documentation - # Required: invoice.total_amount, store.name + # Construit le payload conformément à la documentation PayDunya. + # Champs requis: invoice.total_amount, store.name. payload = { 'invoice': { 'total_amount': int(self.amount), @@ -282,7 +282,7 @@ class PaymentTransaction(models.Model): }, } - # Add customer info if available + # Ajoute les informations client si disponibles. if self.partner_id: payload['invoice']['customer'] = { 'name': self.partner_id.name or '', @@ -292,7 +292,7 @@ class PaymentTransaction(models.Model): channel = self._get_paydunya_channel(processing_values or {}) if channel: - # Force a single payment channel so PayDunya redirects to the selected operator flow. + # Force un seul canal pour rediriger vers l'opérateur sélectionné. payload['invoice']['channels'] = [channel] _logger.info( 'PayDunya checkout channel: selected=%s resolved=%s', @@ -302,7 +302,7 @@ class PaymentTransaction(models.Model): channel, ) - # Authentication headers + # En-têtes d'authentification API PayDunya. headers = { 'Content-Type': 'application/json', 'PAYDUNYA-MASTER-KEY': provider.paydunya_master_key or '', @@ -314,22 +314,22 @@ class PaymentTransaction(models.Model): resp = requests.post(create_url, json=payload, headers=headers, timeout=15) data = resp.json() - # Check response_code (success = '00') + # Vérifie le code de réponse (succès = '00'). response_code = data.get('response_code') if response_code != '00': _logger.warning('PayDunya API error: %s - %s', response_code, data.get('response_text')) return {} - # Extract token from response + # Extrait le token de la réponse. token = data.get('token') - # response_text is the checkout URL + # response_text contient l'URL de checkout. redirect_url = data.get('response_text') if token: - # store reference to match notifications + # Stocke la référence provider pour faire le matching des notifications. self.provider_reference = token _logger.info('PayDunya invoice created: token=%s, url=%s', token, redirect_url) - # Return the rendering values expected by the redirect form template. + # Renvoie les valeurs attendues par le template de redirection. return { 'api_url': redirect_url, 'token': token, @@ -342,7 +342,7 @@ class PaymentTransaction(models.Model): return {} def _get_tx_from_notification(self, data): - """Find the transaction corresponding to a PayDunya notification payload.""" + """Retrouve la transaction correspondant à une notification PayDunya.""" invoice_data = self._normalize_paydunya_notification_data(data) tx_id = self._paydunya_extract_tx_id(invoice_data) if not tx_id and isinstance(data, dict): @@ -361,17 +361,18 @@ class PaymentTransaction(models.Model): return tx or None def _handle_notification_data(self, data): - """Handle PayDunya notification payload and update transaction state. - - Validates the hash according to PayDunya documentation (SHA-512 of MASTER-KEY). - PayDunya sends data as application/x-www-form-urlencoded with key 'data' containing JSON. + """Traite une notification PayDunya et met à jour l'état de transaction. + + Valide le hash selon la documentation PayDunya (SHA-512 du MASTER-KEY). + PayDunya peut envoyer des données `application/x-www-form-urlencoded` + avec une clé `data` contenant du JSON. """ invoice_data = self._normalize_paydunya_notification_data(data) if not invoice_data: _logger.warning('PayDunya: invalid notification payload: %s', data) return False - # Verify hash to ensure the callback is from PayDunya + # Vérifie le hash pour s'assurer que le callback vient bien de PayDunya. provided_hash = invoice_data.get('hash') if not provided_hash and isinstance(data, dict): provided_hash = data.get('hash') @@ -391,7 +392,7 @@ class PaymentTransaction(models.Model): _logger.warning('PayDunya: no transaction found for notification: %s', invoice_data) return False - # Extract status (valid values: pending, completed, cancelled, failed) + # Extrait le statut (pending, completed, cancelled, failed). status = invoice_data.get('status') or ( invoice_data.get('invoice', {}).get('status') ) @@ -441,7 +442,7 @@ class PaymentTransaction(models.Model): _logger.info('PayDunya: Transaction %s marked as cancelled/failed', tx.reference) return True - # fallback: mark as pending + # Fallback: marque la transaction en attente. if hasattr(tx, '_set_pending'): tx._set_pending() elif hasattr(tx, '_set_transaction_pending'):