Merge pull request #4450 from expsa/ENS-3483

[IMP] payment_applepay, payment_hyperpay: implement PCI DSS v4.0 requirements
This commit is contained in:
abdurrahman-saber 2025-08-31 09:18:15 +03:00 committed by GitHub
commit bf223c46b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 44 additions and 16 deletions

View File

@ -1,20 +1,24 @@
import json
import secrets
import requests
from odoo.http import route, request, Controller
class ApplePayFastCheckout(Controller):
@route('/applepay', type='http', auth='public', website=True, csrf=False)
def apple_pay_iframe(self, **kwargs):
nonce = secrets.token_urlsafe(16)
acquirer_id = request.env['payment.acquirer'].sudo().search([('provider', '=', 'applepay')], limit=1)
if acquirer_id.state == 'test':
url = "https://eu-test.oppwa.com/v1/paymentWidgets.js"
else:
url = "https://oppwa.com/v1/paymentWidgets.js"
url = "https://eu-prod.oppwa.com/v1/paymentWidgets.js"
response = request.render("applepay_fast_checkout.apple_pay_iframe", {'hyperpay_src': url, 'merchant_id': acquirer_id.applepay_entity_id})
integrity = requests.get(f'{url}/v1/fastcheckout/integrity').json().get('integrity', '')
response = request.render("applepay_fast_checkout.apple_pay_iframe", {'hyperpay_src': url, 'merchant_id': acquirer_id.applepay_entity_id, 'nonce': nonce, 'integrity': integrity})
response.headers['Content-Security-Policy'] = "script-src blob: 'self' 'unsafe-inline' 'unsafe-eval' https://*; worker-src blob: 'self' 'unsafe-inline' 'unsafe-eval' https://*;connect-src 'self' https://* wss://*;frame-src 'self' blob: https://*;"
return response

View File

@ -4,12 +4,20 @@
<template id="apple_pay_iframe" name="Apple Pay Iframe">
<html>
<head>
<script t-att-src="hyperpay_src" />
<meta http-equiv="Content-Security-Policy"
t-attf-content="
style-src 'self' https://*.oppwa.com 'unsafe-inline';
frame-src 'self' https://*.oppwa.com https://applepay.cdn-apple.com;
script-src 'self' https://*.oppwa.com https://applepay.cdn-apple.com 'nonce-{{nonce}}';
connect-src 'self' https://*.oppwa.com;
img-src 'self' https://*.oppwa.com;" />
<script t-att-src="hyperpay_src" t-att-integrity="integrity" crossorigin="anonymous" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js" />
<script>
merchant_id = "<t t-esc="merchant_id" />";
</script>
<script src="/applepay_fast_checkout/static/src/js/applepay_iframe.js" />
<script src="/applepay_fast_checkout/static/src/js/applepay_iframe.js" t-att-nonce="nonce"/>
<link rel="stylesheet" href="/applepay_fast_checkout/static/src/css/applepay_iframe_content.css" />
</head>
<body>

View File

@ -6,8 +6,10 @@
#################################################################################
import requests
from odoo import http
import random
import secrets
from odoo import http
from odoo.http import request
from odoo.addons.payment_hyperpay.data.payment_icon import payment_icon
@ -15,8 +17,7 @@ from odoo.addons.payment_hyperpay.data.payment_icon import payment_icon
import logging
_logger = logging.getLogger(__name__)
# test_domain = "https://test.oppwa.com"
# live_domain = "https://oppwa.com"
test_domain = "https://eu-test.oppwa.com"
live_domain = "https://eu-prod.oppwa.com"
@ -46,6 +47,7 @@ class HyperPayController(http.Controller):
@http.route('/payment/hyperpay/checkout/create', type='json', auth='public', csrf=False, website=True)
def create_hyperpay_checkout(self, **post):
_logger.info('--post---%r', post)
nonce = secrets.token_urlsafe(16)
tx = request.env['payment.transaction'].sudo().search([('id', '=', int(post.get('txId', 0)))])
final_response = {}
if tx:
@ -65,6 +67,7 @@ class HyperPayController(http.Controller):
"currency": tx.currency_id and tx.sudo().currency_id.name or '',
"paymentType": "DB",
"env": acq.state,
"integrity": "true",
"customParameters[SHOPPER_tx_id]": tx.id,
"merchantTransactionId": tx.reference,
"billing.street1": partner_id.street or 'Riyadh',
@ -95,7 +98,9 @@ class HyperPayController(http.Controller):
'base_url': base_url,
'data_brands': data_brands,
'acq': acq.id,
'website_id': request.session.get('force_website_id') or request.website.id
'website_id': request.session.get('force_website_id') or request.website.id,
'integrity': resp.get('integrity', ''),
'nonce': nonce,
}
return final_response

View File

@ -16,7 +16,7 @@ odoo.define("payment_hyperpay.payment_hyperpay", function (require) {
// Reference
// https://dev.to/pulljosh/how-to-load-html-css-and-js-code-into-an-iframe-2blc
const getGeneratedPageURL = ({ html, css, js }) => {
const getGeneratedPageURL = ({ html, css, js, meta, nonce}) => {
const getBlobURL = (code, type) => {
const blob = new Blob([code], { type });
return URL.createObjectURL(blob);
@ -25,11 +25,12 @@ odoo.define("payment_hyperpay.payment_hyperpay", function (require) {
const source = `
<html>
<head>
${meta}
${css}
${js}
</head>
<body>
<script>
<script nonce="${nonce}">
var wpwlOptions = {
onReady: function(){
var shopOrigin = $('input[name="shopOrigin"]');
@ -66,7 +67,7 @@ odoo.define("payment_hyperpay.payment_hyperpay", function (require) {
}
}
</script>
<script>
<script nonce="${nonce}">
var wpwlOptions = {
browser: {threeDChallengeWindow: 5 },
locale: "ar",
@ -103,20 +104,20 @@ odoo.define("payment_hyperpay.payment_hyperpay", function (require) {
txId: self.tx_id,
}).then(function (result) {
if (result) {
self._renderHyperpayModal(result.checkoutId, result.domain, result.base_url, result.data_brands, result.acq, result.website_id);
self._renderHyperpayModal(result.checkoutId, result.domain, result.base_url, result.data_brands, result.acq, result.website_id, result);
} else {
console.log("Error Occured");
}
});
},
_renderHyperpayModal: function (checkoutId, domain, base_url, data_brands, acq, website_id) {
_renderHyperpayModal: function (checkoutId, domain, base_url, data_brands, acq, website_id, result) {
var self = this;
try {
var $modal_html = $($(".payment_hyper_modal").get()[0]);
$modal_html.appendTo($("body")).modal({ keyboard: false, backdrop: "static" });
var style_css = '<link rel="stylesheet" href="' + base_url + '/payment_hyperpay/static/src/css/hyperpay_style.css" />';
var script = '<script async src="' + domain + "/v1/paymentWidgets.js?checkoutId=" + checkoutId + '"></script>';
var js_script = '<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>';
var script = `<script async src="${domain}/v1/paymentWidgets.js?checkoutId=${checkoutId}" integrity="${result.integrity}" crossorigin="anonymous"></script>`
var js_script = '<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>'
var shopperResultUrlTag =
'<form action="' +
base_url +
@ -131,10 +132,20 @@ odoo.define("payment_hyperpay.payment_hyperpay", function (require) {
theIframe.style = "display:none";
var html = script + shopperResultUrlTag;
let meta = `<meta http-equiv="Content-Security-Policy"
t-attf-content="
style-src 'self' https://*.oppwa.com 'unsafe-inline';
frame-src 'self' https://*.oppwa.com;
script-src 'self' https://*.oppwa.com https://src.mastercard.com https://p11.techlab-cdn.com 'nonce-${result.nonce}';
connect-src 'self' https://*.oppwa.com;
img-src 'self' https://*.oppwa.com;" />`
const url = getGeneratedPageURL({
html: html,
css: style_css,
js: js_script,
meta: meta,
nonce: result.nonce
});
theIframe.src = url;
$("#hyperpay-modal-body")[0].appendChild(theIframe);