fix: redirection
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
|
import json
|
||||||
from odoo import http
|
from odoo import http
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
@@ -43,7 +44,7 @@ class PaydunyaController(http.Controller):
|
|||||||
tx_model = http.request.env['payment.transaction'].sudo()
|
tx_model = http.request.env['payment.transaction'].sudo()
|
||||||
handled = tx_model._handle_notification_data(data)
|
handled = tx_model._handle_notification_data(data)
|
||||||
if handled:
|
if handled:
|
||||||
return http.request.redirect('/payment/process')
|
return http.request.redirect('/shop/confirmation')
|
||||||
except Exception:
|
except Exception:
|
||||||
_logger.exception('Error handling PayDunya notification data')
|
_logger.exception('Error handling PayDunya notification data')
|
||||||
|
|
||||||
@@ -56,25 +57,32 @@ class PaydunyaController(http.Controller):
|
|||||||
_logger.info('PayDunya payment cancelled: token=%s', token)
|
_logger.info('PayDunya payment cancelled: token=%s', token)
|
||||||
return http.request.redirect('/shop/cart')
|
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):
|
def paydunya_callback(self, **kwargs):
|
||||||
"""Handle IPN callback from PayDunya.
|
"""Handle IPN callback from PayDunya.
|
||||||
|
|
||||||
PayDunya sends data as application/x-www-form-urlencoded with 'data' key containing JSON.
|
PayDunya sends data as application/x-www-form-urlencoded with 'data' key containing JSON.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# The 'data' parameter contains the JSON-encoded transaction info
|
payload = {}
|
||||||
import json
|
|
||||||
data_str = http.request.params.get('data')
|
data_str = http.request.params.get('data')
|
||||||
if data_str:
|
if data_str:
|
||||||
data = json.loads(data_str)
|
payload = {'data': data_str}
|
||||||
_logger.info('PayDunya IPN callback received: %s', data)
|
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}
|
||||||
|
|
||||||
tx_model = http.request.env['payment.transaction'].sudo()
|
_logger.info('PayDunya IPN callback received: %s', payload)
|
||||||
tx_model._handle_notification_data(data)
|
tx_model = http.request.env['payment.transaction'].sudo()
|
||||||
|
tx_model._handle_notification_data(payload)
|
||||||
except Exception:
|
except Exception:
|
||||||
_logger.exception('Error handling PayDunya callback')
|
_logger.exception('Error handling PayDunya callback')
|
||||||
|
|
||||||
# Always return 200 OK to PayDunya
|
# Always return 200 OK to PayDunya
|
||||||
return 'OK'
|
return 'OK'
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import json
|
||||||
from odoo import models
|
from odoo import models
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
@@ -9,6 +10,29 @@ _logger = logging.getLogger(__name__)
|
|||||||
class PaymentTransaction(models.Model):
|
class PaymentTransaction(models.Model):
|
||||||
_inherit = 'payment.transaction'
|
_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": "<json>"} 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):
|
def _get_paydunya_channel(self, processing_values):
|
||||||
"""Return the PayDunya channel to force based on selected payment method."""
|
"""Return the PayDunya channel to force based on selected payment method."""
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
@@ -140,11 +164,7 @@ class PaymentTransaction(models.Model):
|
|||||||
|
|
||||||
def _get_tx_from_notification(self, data):
|
def _get_tx_from_notification(self, data):
|
||||||
"""Find the transaction corresponding to a PayDunya notification payload."""
|
"""Find the transaction corresponding to a PayDunya notification payload."""
|
||||||
# Extract invoice data if wrapped
|
invoice_data = self._normalize_paydunya_notification_data(data)
|
||||||
if isinstance(data, dict) and 'data' in data:
|
|
||||||
invoice_data = data['data']
|
|
||||||
else:
|
|
||||||
invoice_data = data
|
|
||||||
|
|
||||||
token = invoice_data.get('invoice', {}).get('token') or invoice_data.get('token')
|
token = invoice_data.get('invoice', {}).get('token') or invoice_data.get('token')
|
||||||
if not token:
|
if not token:
|
||||||
@@ -158,21 +178,22 @@ class PaymentTransaction(models.Model):
|
|||||||
Validates the hash according to PayDunya documentation (SHA-512 of MASTER-KEY).
|
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.
|
PayDunya sends data as application/x-www-form-urlencoded with key 'data' containing JSON.
|
||||||
"""
|
"""
|
||||||
# Extract the actual data if wrapped
|
invoice_data = self._normalize_paydunya_notification_data(data)
|
||||||
if isinstance(data, dict) and 'data' in data:
|
if not invoice_data:
|
||||||
invoice_data = data['data']
|
_logger.warning('PayDunya: invalid notification payload: %s', data)
|
||||||
else:
|
return False
|
||||||
invoice_data = data
|
|
||||||
|
|
||||||
# Verify hash to ensure the callback is from PayDunya
|
# Verify hash to ensure the callback is from PayDunya
|
||||||
provided_hash = invoice_data.get('hash')
|
provided_hash = invoice_data.get('hash')
|
||||||
|
if not provided_hash and isinstance(data, dict):
|
||||||
|
provided_hash = data.get('hash')
|
||||||
if provided_hash:
|
if provided_hash:
|
||||||
provider = self.env['payment.provider'].search([('code', '=', 'paydunya')], limit=1)
|
provider = self.env['payment.provider'].search([('code', '=', 'paydunya')], limit=1)
|
||||||
if provider and provider.paydunya_master_key:
|
if provider and provider.paydunya_master_key:
|
||||||
expected_hash = hashlib.sha512(
|
expected_hash = hashlib.sha512(
|
||||||
provider.paydunya_master_key.encode()
|
provider.paydunya_master_key.encode()
|
||||||
).hexdigest()
|
).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',
|
_logger.warning('PayDunya: Hash mismatch! Possible security issue. Expected %s, got %s',
|
||||||
expected_hash, provided_hash)
|
expected_hash, provided_hash)
|
||||||
return False
|
return False
|
||||||
|
|||||||
Reference in New Issue
Block a user