From fb090d363a30f26c70b02744e50737657af4322e Mon Sep 17 00:00:00 2001 From: mthiam Date: Tue, 10 Feb 2026 15:53:19 +0100 Subject: [PATCH] fix: redirection --- addons/payment_paydunya/controllers/main.py | 28 +++++++----- .../models/payment_transaction.py | 43 ++++++++++++++----- 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/addons/payment_paydunya/controllers/main.py b/addons/payment_paydunya/controllers/main.py index d87aad4..77124ce 100644 --- a/addons/payment_paydunya/controllers/main.py +++ b/addons/payment_paydunya/controllers/main.py @@ -1,5 +1,6 @@ import logging import requests +import json from odoo import http _logger = logging.getLogger(__name__) @@ -43,7 +44,7 @@ class PaydunyaController(http.Controller): tx_model = http.request.env['payment.transaction'].sudo() handled = tx_model._handle_notification_data(data) if handled: - return http.request.redirect('/payment/process') + return http.request.redirect('/shop/confirmation') except Exception: _logger.exception('Error handling PayDunya notification data') @@ -56,25 +57,32 @@ class PaydunyaController(http.Controller): _logger.info('PayDunya payment cancelled: token=%s', token) return http.request.redirect('/shop/cart') - @http.route('/payment/paydunya/callback', type='http', auth='public', methods=['POST'], csrf=False) + @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. """ try: - # The 'data' parameter contains the JSON-encoded transaction info - import json + payload = {} data_str = http.request.params.get('data') if data_str: - data = json.loads(data_str) - _logger.info('PayDunya IPN callback received: %s', data) - - tx_model = http.request.env['payment.transaction'].sudo() - tx_model._handle_notification_data(data) + payload = {'data': data_str} + elif http.request.params: + payload = dict(http.request.params) + else: + raw_body = http.request.httprequest.get_data(as_text=True) + if raw_body: + try: + payload = json.loads(raw_body) + except Exception: + payload = {'data': raw_body} + + _logger.info('PayDunya IPN callback received: %s', payload) + tx_model = http.request.env['payment.transaction'].sudo() + tx_model._handle_notification_data(payload) except Exception: _logger.exception('Error handling PayDunya callback') # Always return 200 OK to PayDunya return 'OK' - diff --git a/addons/payment_paydunya/models/payment_transaction.py b/addons/payment_paydunya/models/payment_transaction.py index 2dd3f68..a8f356a 100644 --- a/addons/payment_paydunya/models/payment_transaction.py +++ b/addons/payment_paydunya/models/payment_transaction.py @@ -1,6 +1,7 @@ import logging import requests import hashlib +import json from odoo import models _logger = logging.getLogger(__name__) @@ -9,6 +10,29 @@ _logger = logging.getLogger(__name__) class PaymentTransaction(models.Model): _inherit = 'payment.transaction' + def _normalize_paydunya_notification_data(self, data): + """Normalize PayDunya notification payload across callback and confirm formats.""" + payload = data + if not isinstance(payload, dict): + return {} + + # Callback can send {"data": ""} or {"data": {...}} + if 'data' in payload: + wrapped = payload.get('data') + if isinstance(wrapped, str): + try: + payload = json.loads(wrapped) + except Exception: + payload = payload + elif isinstance(wrapped, dict): + payload = wrapped + + # Confirm endpoint can send {"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.""" self.ensure_one() @@ -140,11 +164,7 @@ class PaymentTransaction(models.Model): def _get_tx_from_notification(self, data): """Find the transaction corresponding to a PayDunya notification payload.""" - # Extract invoice data if wrapped - if isinstance(data, dict) and 'data' in data: - invoice_data = data['data'] - else: - invoice_data = data + invoice_data = self._normalize_paydunya_notification_data(data) token = invoice_data.get('invoice', {}).get('token') or invoice_data.get('token') if not token: @@ -158,21 +178,22 @@ class PaymentTransaction(models.Model): 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. """ - # Extract the actual data if wrapped - if isinstance(data, dict) and 'data' in data: - invoice_data = data['data'] - else: - invoice_data = data + 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 provided_hash = invoice_data.get('hash') + if not provided_hash and isinstance(data, dict): + provided_hash = data.get('hash') if provided_hash: provider = self.env['payment.provider'].search([('code', '=', 'paydunya')], limit=1) if provider and provider.paydunya_master_key: expected_hash = hashlib.sha512( provider.paydunya_master_key.encode() ).hexdigest() - if provided_hash != expected_hash: + if str(provided_hash).lower() != expected_hash: _logger.warning('PayDunya: Hash mismatch! Possible security issue. Expected %s, got %s', expected_hash, provided_hash) return False -- 2.49.1