docs(payment_paydunya): documente les methodes en francais
This commit is contained in:
@@ -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": "<json>"} or {"data": {...}}
|
||||
# Le callback peut envoyer {"data": "<json>"} 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'):
|
||||
|
||||
Reference in New Issue
Block a user