From 8053d3d691885778f149110d3a36382cd9e53fc7 Mon Sep 17 00:00:00 2001 From: Mohamed DAOUD Date: Tue, 17 Mar 2026 18:33:45 +0100 Subject: [PATCH 1/2] fix and enhance stancer --- .../class/actions_stancerdolicloud.class.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/htdocs/stancerdolicloud/class/actions_stancerdolicloud.class.php b/htdocs/stancerdolicloud/class/actions_stancerdolicloud.class.php index f8f1f732d..3fc81859d 100644 --- a/htdocs/stancerdolicloud/class/actions_stancerdolicloud.class.php +++ b/htdocs/stancerdolicloud/class/actions_stancerdolicloud.class.php @@ -366,7 +366,7 @@ public function doPayment($parameters, &$object, &$action, $hookmanager) $lastproxy = end($tmphosts); include_once DOL_DOCUMENT_ROOT.'/website/class/website.class.php'; - $tmpwebsite = new Website($db); + $tmpwebsite = new Website($this->db); $tmpwebsite->fetch(0, $ws); if (preg_replace('/https?:\/\//i', '', $tmpwebsite->virtualhost) == $lastproxy) { @@ -522,9 +522,14 @@ public function doPayment($parameters, &$object, &$action, $hookmanager) $headers[] = "Authorization: Basic ".$encodedkey; $headers[] = "Content-Type: application/json"; + $methods_allowed = ["card", "sepa"]; + if (!empty($ws)) { // If website is specified, we only propose card payment method + $methods_allowed = ["card"]; + } $jsontosenddata = '{ "amount": '.$amount.', "currency": "'.strtolower($currencyCodeType).'", + "methods_allowed": '.json_encode($methods_allowed).', "return_url": "'.$urlback.'"'; $jsontosenddata .= '}'; From 88e625c17dc3ae4fabfe8de3441d9d85ed7ec131 Mon Sep 17 00:00:00 2001 From: Mohamed DAOUD Date: Tue, 24 Mar 2026 13:44:53 +0100 Subject: [PATCH 2/2] enhance and add some controls --- .../class/actions_stancerdolicloud.class.php | 76 ++++++++++++++++--- 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/htdocs/stancerdolicloud/class/actions_stancerdolicloud.class.php b/htdocs/stancerdolicloud/class/actions_stancerdolicloud.class.php index 3fc81859d..adfe5296e 100644 --- a/htdocs/stancerdolicloud/class/actions_stancerdolicloud.class.php +++ b/htdocs/stancerdolicloud/class/actions_stancerdolicloud.class.php @@ -412,7 +412,9 @@ public function doPayment($parameters, &$object, &$action, $hookmanager) $urlback .= 'action=returnDoPaymentStancer'; if ($action == "returnDoPaymentStancer") { - dol_syslog("Data after redirect from stancer payment page with session FinalPaymentAmt = ".$_SESSION["FinalPaymentAmt"]." currencycodeType = ".$_SESSION["currencyCodeType"], LOG_DEBUG); + dol_syslog("Data after redirect from stancer payment page with session FinalPaymentAmt = ".$_SESSION["FinalPaymentAmt"]." currencycodeType = ".$_SESSION["currencyCodeType"], LOG_DEBUG, 0, '_payment'); + + $_SESSION['paymentoksessioncode'] = getRandomPassword(true, null, 20); // key between newpayment.php to paymentok.php to avoid direct access to paymentok.php without going through newpayment.php $stancerurlapi = "api.stancer.com"; if (getDolGlobalInt("STANCER_DOLICLOUD_LIVE")) { @@ -436,7 +438,7 @@ public function doPayment($parameters, &$object, &$action, $hookmanager) if ($ret1["http_code"] == 200) { $result1 = $ret1["content"]; $json1 = json_decode($result1); - $urlredirect .= "paymentok.php?fulltag=".urlencode($FULLTAG); + $urlredirect .= "paymentok.php?fulltag=".urlencode($FULLTAG)."&paymentoksessioncode=".urlencode($_SESSION['paymentoksessioncode']); header("Location: ".$urlredirect); exit; } else { @@ -522,9 +524,9 @@ public function doPayment($parameters, &$object, &$action, $hookmanager) $headers[] = "Authorization: Basic ".$encodedkey; $headers[] = "Content-Type: application/json"; - $methods_allowed = ["card", "sepa"]; - if (!empty($ws)) { // If website is specified, we only propose card payment method - $methods_allowed = ["card"]; + $methods_allowed = ["card"]; + if (getDolGlobalInt("STANCER_DOLICLOUD_ALLOW_SEPA")) { // If SEPA is allowed in configuration, we add it. + $methods_allowed[] = "sepa"; } $jsontosenddata = '{ "amount": '.$amount.', @@ -535,7 +537,7 @@ public function doPayment($parameters, &$object, &$action, $hookmanager) $urlforcheckout = "https://".urlencode($stancerurlapi)."/v2/payment_intents/"; - dol_syslog("Send Post to url=".$urlforcheckout." with session FinalPaymentAmt = ".$FinalPaymentAmt." currencyCodeType = ".$currencyCodeType, LOG_DEBUG); + dol_syslog("Send Post to url=".$urlforcheckout." with session FinalPaymentAmt = ".$FinalPaymentAmt." currencyCodeType = ".$currencyCodeType, LOG_DEBUG, 0, '_payment'); $ret1 = getURLContent($urlforcheckout, 'POSTALREADYFORMATED', $jsontosenddata, 1, $headers); if ($ret1["http_code"] == 200) { @@ -545,7 +547,7 @@ public function doPayment($parameters, &$object, &$action, $hookmanager) $urlforredirect = "https://".urlencode($stancerurlpayment)."/".(!getDolGlobalInt("STANCER_DOLICLOUD_LIVE") ? "test_" : "").$_SESSION["STANCER_DOLICLOUD_PAYMENT_ID"]; // Gestion redirection - dol_syslog("Send redirect to ".$urlforredirect); + dol_syslog("Send redirect to ".$urlforredirect, LOG_DEBUG, 0, '_payment'); header("Location: ".$urlforredirect); exit; @@ -609,6 +611,24 @@ public function isPaymentOK($parameters, &$object, &$action, $hookmanager) $ispaymentok = false; if (in_array($parameters['paymentmethod'], array('stancerdolicloud'))){ + + // Prevents direct access to the paymentok page without a valid session flow. + if (GETPOST('paymentoksessioncode') !== $_SESSION['paymentoksessioncode']) { + $error++; + $errmsg = 'Attempted direct access to the paymentok page without a valid session.'; + dol_syslog($errmsg, LOG_ERR, 0, '_payment'); + $this->errors[] = $errmsg; + } + + // Ensures the session holds a valid Stancer payment ID before any API call is attempted. + $FinalPaymentID = empty($_SESSION["STANCER_DOLICLOUD_PAYMENT_ID"]) ? '' : $_SESSION["STANCER_DOLICLOUD_PAYMENT_ID"]; + if (!$error && empty($FinalPaymentID)) { + $error++; + $errmsg = 'Stancer payment verification failed: STANCER_DOLICLOUD_PAYMENT_ID is not set in session.'; + dol_syslog($errmsg, LOG_ERR, 0, '_payment'); + $this->errors[] = $errmsg; + } + $code = GETPOST("code"); if ($code == "refused") { @@ -630,13 +650,45 @@ public function isPaymentOK($parameters, &$object, &$action, $hookmanager) $FinalPaymentID = $_SESSION["STANCER_DOLICLOUD_PAYMENT_ID"]; $urlforcheckout = "https://".urlencode($stancerurlapi)."/v2/payment_intents/".$FinalPaymentID; - dol_syslog("Send Get to url=".$urlforcheckout." with session STANCER_DOLICLOUD_PAYMENT_ID = ".$FinalPaymentID, LOG_DEBUG); + dol_syslog("Send Get to url=".$urlforcheckout." with session STANCER_DOLICLOUD_PAYMENT_ID = ".$FinalPaymentID, LOG_DEBUG, 0, '_payment'); $ret1 = getURLContent($urlforcheckout, 'GET', "", 1, $headers); if ($ret1["http_code"] == 200) { $result1 = $ret1["content"]; $json = json_decode($result1); - if (in_array($json->status, array("captured", "authorized", "capture_sent", "to_capture"))) { - $ispaymentok = true; + + // Ensures the payment confirmed by Stancer matches exactly what was presented to the user + $FinalPaymentAmt = empty($_SESSION["FinalPaymentAmt"]) ? '' : $_SESSION["FinalPaymentAmt"]; + $currencyCodeType = empty($_SESSION['currencyCodeType']) ? '' : $_SESSION['currencyCodeType']; + + if (!empty($FinalPaymentAmt) && !empty($currencyCodeType)) { + $expectedAmount = (int) round($FinalPaymentAmt * 100); + $expectedCurrency = strtolower($currencyCodeType); + $returnedAmount = isset($json->amount) ? (int) $json->amount : null; + $returnedCurrency = isset($json->currency) ? strtolower($json->currency) : null; + + if ($returnedAmount !== $expectedAmount || $returnedCurrency !== $expectedCurrency) { + $error++; + $errmsg = 'Stancer payment information mismatch: expected amount ' + .$expectedAmount + .' and currency '.$expectedCurrency + .', got amount '.$returnedAmount + .' and currency '.$returnedCurrency; + dol_syslog($errmsg, LOG_ERR, 0, '_payment'); + $this->errors[] = $errmsg; + } + } + + if (!$error) { + if (in_array($json->status, array("captured", "authorized", "capture_sent", "to_capture"))) { + dol_syslog("Stancer payment status OK: ".$json->status, LOG_DEBUG, 0, '_payment'); + $ispaymentok = true; + } else { + $error++; + $errmsg = 'Stancer payment not in an accepted status. Status: '.$json->status; + dol_syslog($errmsg, LOG_ERR, 0, '_payment'); + $this->errors[] = $errmsg; + $ispaymentok = false; + } } } else { $arrayofmessage = array(); @@ -658,6 +710,10 @@ public function isPaymentOK($parameters, &$object, &$action, $hookmanager) $this->errors[] = $langs->trans("UnkownError").' - HTTP code = '.$ret1["http_code"]; } } + + $errmsg = 'Stancer API HTTP error: code='.$ret1["http_code"].' for payment ID '.$FinalPaymentID; + dol_syslog($errmsg, LOG_ERR, 0, '_payment'); + $error++; $ispaymentok = false; }