Skip to content

Commit 8bfbbbe

Browse files
PT-1866: Refactor the webhook processing flow (#25)
1 parent e03cf0f commit 8bfbbbe

15 files changed

+186
-62
lines changed

Controller/Admin/OrderMain.php

+6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ public function __construct()
2424

2525
public function sendOrder()
2626
{
27+
$oOrder = $this->getOrder();
28+
29+
if (stripos($oOrder->oxorder__oxtransstatus->value, 'pending') !== false) {
30+
return MonduHelper::showErrorMessage('MONDU_SENDING_PENDING_ORDER_ERROR');
31+
}
32+
2733
if ($this->isMonduPayment() && !$this->_monduShippingProcessor->shipMonduOrder()) {
2834
return MonduHelper::showErrorMessage('MONDU_CREATE_INVOICE_ERROR');
2935
}

Controller/Admin/OrderOverview.php

+10
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ public function render()
3131

3232
public function sendorder()
3333
{
34+
$oOrder = $this->getOrder();
35+
36+
if (stripos($oOrder->oxorder__oxtransstatus->value, 'pending') !== false) {
37+
return MonduHelper::showErrorMessage('MONDU_SENDING_PENDING_ORDER_ERROR');
38+
}
39+
40+
if ($this->isMonduPayment() && !$this->_monduShippingProcessor->shipMonduOrder()) {
41+
return MonduHelper::showErrorMessage('MONDU_CREATE_INVOICE_ERROR');
42+
}
43+
3444
if ($this->isMonduPayment() && !$this->_monduShippingProcessor->shipMonduOrder()) {
3545
return MonduHelper::showErrorMessage('MONDU_CREATE_INVOICE_ERROR');
3646
}

Controller/MonduWebhooksController.php

+19-8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
use Symfony\Component\HttpFoundation\Response;
88
use OxidEsales\MonduPayment\Core\WebhookHandler\WebhookHandler;
99
use OxidEsales\MonduPayment\Core\Config;
10+
use OxidEsales\MonduPayment\Model\MonduOrder;
11+
use OxidEsales\Eshop\Application\Model\Order;
12+
use OxidEsales\MonduPayment\Core\Utils\MonduHelper;
1013

1114
class MonduWebhooksController extends \OxidEsales\Eshop\Application\Controller\FrontendController
1215
{
@@ -37,15 +40,23 @@ private function handleRequest(Request $request): Response
3740
{
3841
$content = $request->getContent();
3942
$headers = $request->headers;
43+
$params = json_decode($content, true);
44+
$signatureIsValid = false;
45+
$shopId = $this->_webhookHandler->getShopId($params);
46+
$shopIds = $shopId ? [['OXID' => $shopId]] : MonduHelper::getAllShopIds();
4047

41-
$signature = hash_hmac('sha256', $content, $this->_config->getWebhooksSecret());
42-
if ($signature !== $headers->get('X-Mondu-Signature')) {
43-
$this->_logger->debug('MonduWebhooksController [WebhooksSecret]: ' . print_r($this->_config->getWebhooksSecret(), true));
44-
$this->_logger->debug('MonduWebhooksController [Content]: ' . print_r($content, true));
45-
$this->_logger->debug('MonduWebhooksController [X-Mondu-Signature]: ' . print_r($headers->get('X-Mondu-Signature'), true));
46-
$this->_logger->debug('MonduWebhooksController [Signature]: ' . print_r($signature, true));
48+
foreach ($shopIds as $shopId) {
49+
if (isset($shopId['OXID'])) {
50+
$signature = hash_hmac('sha256', $content, $this->_webhookHandler->getWebhookSecretByShopId($shopId['OXID']));
51+
if ($signature === $headers->get('X-Mondu-Signature')) {
52+
$signatureIsValid = true;
53+
break;
54+
}
55+
}
56+
}
4757

48-
return new Response('Invalid signature', 401);
58+
if (!$signatureIsValid) {
59+
return new Response($logData, 401);
4960
}
5061

5162
$params = json_decode($content, true);
@@ -57,4 +68,4 @@ private function handleRequest(Request $request): Response
5768
['content-type' => 'application/json']
5869
);
5970
}
60-
}
71+
}

Controller/OrderController.php

+10-14
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
class OrderController extends OrderController_parent
1818
{
19-
const EE_EDITION_CODE = 'EE';
20-
2119
private MonduClient $_client;
2220
private User|null|false $_oUser;
2321
private LoggerInterface $_logger;
@@ -64,16 +62,14 @@ public function execute()
6462
}
6563
$response = $this->_client->confirmOrder($orderUuid, $data);
6664
$this->_logger->debug('MonduOrderController [execute $response]: ' . print_r($response, true));
67-
if (isset($response['state']) && $response['state'] == 'confirmed') {
68-
try {
69-
$iSuccess = $this->monduExecute($oBasket);
70-
$edition = oxRegistry::getConfig()->getEdition();
7165

72-
if ($edition === self::EE_EDITION_CODE && $iSuccess == 1) {
73-
return oxRegistry::getUtils()->redirect(oxRegistry::getConfig()->getShopHomeURL() . 'index.php?cl=success', false);
74-
}
66+
if (isset($response['state']) && ($response['state'] == 'confirmed' || $response['state'] == 'pending')) {
67+
$isPending = $response['state'] == 'pending';
7568

76-
return $this->_getNextStep($iSuccess);
69+
try {
70+
$iSuccess = $this->monduExecute($oBasket, $isPending);
71+
72+
return $this->getNextStep($iSuccess);
7773
} catch (Exception $e) {
7874
throw new \Exception('Mondu: Error during the order process');
7975
}
@@ -92,10 +88,10 @@ public function execute()
9288
* Save order to database, delete order_id from session and redirect to thank you page
9389
*
9490
* @param Basket $oBasket
95-
*
91+
* @param bool $isPending
9692
* @return bool|int|mixed
9793
*/
98-
protected function monduExecute(Basket $oBasket)
94+
protected function monduExecute(Basket $oBasket, bool $isPending)
9995
{
10096
if (!Registry::getSession()->getVariable('sess_challenge')) {
10197
Registry::getSession()->setVariable('sess_challenge', Registry::getUtilsObject()->generateUID());
@@ -106,7 +102,7 @@ protected function monduExecute(Basket $oBasket)
106102
$oOrder = oxNew(Order::class);
107103

108104
try {
109-
$iSuccess = $oOrder->finalizeOrder($oBasket, $oBasket->getUser());
105+
$iSuccess = $oOrder->finalizeOrder($oBasket, $oBasket->getUser(), false, $isPending);
110106
} catch (StandardException $e) {
111107
Registry::get(UtilsView::class)->addErrorToDisplay($e);
112108
}
@@ -118,4 +114,4 @@ protected function monduExecute(Basket $oBasket)
118114

119115
return $iSuccess;
120116
}
121-
}
117+
}

Core/Config.php

+7-5
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,15 @@ public function getShopVersion()
6161
return oxNew(\OxidEsales\EshopCommunity\Core\ShopVersion::class)->getVersion();
6262
}
6363

64-
public function getWebhooksSecret()
64+
public function getWebhooksSecret($shopId = null)
6565
{
66-
return $this->getParameter('oemonduWebhookSecret');
66+
return \OxidEsales\Eshop\Core\Registry::getConfig()->getShopConfVar('oemonduWebhookSecret', $shopId, 'module:oemondu');
67+
//return $this->getParameter('oemonduWebhookSecret');
6768
}
6869

69-
public function setWebhooksSecret($webhookSecret)
70+
public function setWebhooksSecret($webhookSecret, $shopId)
7071
{
71-
$this->setParameter('oemonduWebhookSecret', $webhookSecret);
72+
$this->setParameter('oemonduWebhookSecret', $webhookSecret, $shopId);
7273
}
7374

7475
public function getIsMerchantIdentified()
@@ -107,13 +108,14 @@ protected function getParameter($paramName)
107108
return $this->getConfig()->getConfigParam($paramName);
108109
}
109110

110-
protected function setParameter($paramName, $paramValue)
111+
protected function setParameter($paramName, $paramValue, $shopId = null)
111112
{
112113
$moduleSettingBridge = ContainerFactory::getInstance()
113114
->getContainer()
114115
->get(ModuleSettingBridgeInterface::class);
115116

116117
$moduleSettingBridge->save($paramName, $paramValue, 'oemondu');
118+
\OxidEsales\Eshop\Core\Registry::getConfig()->saveShopConfVar('str', $paramName, $paramValue, $shopId, 'module:oemondu');
117119
}
118120

119121
protected function getConfig()

Core/Http/MonduClient.php

+7-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public function adjustOrder($orderUuid, $data = [])
9191
public function getWebhooksSecret()
9292
{
9393
$response = $this->sendRequest('GET', 'webhooks/keys');
94-
$this->_config->setWebhooksSecret($response['webhook_secret'] ?? '');
94+
$this->_config->setWebhooksSecret($response['webhook_secret'] ?? '', \OxidEsales\Eshop\Core\Registry::getConfig()->getShopId());
9595
return $response['webhook_secret'];
9696
}
9797

@@ -101,6 +101,12 @@ public function registerWebhook($webhookParams)
101101
return $response;
102102
}
103103

104+
public function deleteWebhook($webhookParams)
105+
{
106+
$response = $this->sendRequest('DELETE', 'webhooks/uuid');
107+
return $response;
108+
}
109+
104110
public function logEvent($eventData)
105111
{
106112
try {

Core/Utils/MonduHelper.php

+13
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use OxidEsales\Eshop\Core\Registry;
66
use OxidEsales\EshopCommunity\Internal\Framework\Module\Setup\Bridge\ModuleActivationBridgeInterface;
77
use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory;
8+
use OxidEsales\EshopCommunity\Internal\Framework\Database\QueryBuilderFactoryInterface;
89

910
class MonduHelper
1011
{
@@ -77,4 +78,16 @@ public static function isOrderAdjusted($oldOrder, $newOrder)
7778
$oldOrder->getFieldData('oxorder__oxdiscount') != $newOrder->getFieldData('oxorder__oxdiscount') ||
7879
!self::ordersHaveSameArticles($oldOrder, $newOrder);
7980
}
81+
82+
public static function getAllShopIds()
83+
{
84+
$container = ContainerFactory::getInstance()->getContainer();
85+
$queryBuilderFactory = $container->get(QueryBuilderFactoryInterface::class);
86+
87+
$queryBuilder = $queryBuilderFactory->create();
88+
$queryBuilder->select('OXID')
89+
->from('oxshops');
90+
91+
return $queryBuilder->execute()->fetchAllAssociative();
92+
}
8093
}

Core/WebhookHandler/WebhookHandler.php

+56-2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
namespace OxidEsales\MonduPayment\Core\WebhookHandler;
44

5+
use OxidEsales\Eshop\Application\Model\Order;
56
use OxidEsales\Eshop\Core\Registry;
67
use OxidEsales\MonduPayment\Model\MonduInvoice;
78
use OxidEsales\MonduPayment\Model\MonduOrder;
89
use Psr\Log\LoggerInterface;
910
use Symfony\Component\HttpFoundation\Response;
11+
use OxidEsales\Eshop\Core\DatabaseProvider;
1012

1113
class WebhookHandler
1214
{
@@ -19,7 +21,9 @@ public function __construct()
1921

2022
public function handleWebhook($params)
2123
{
22-
$this->_logger->debug('MonduWebhookHandler [handleWebhook]: ' . print_r($params, true));
24+
$logger = \OxidEsales\Eshop\Core\Registry::getLogger();
25+
$logger->debug('MonduWebhooksController [WebhooksSecret]: ' . print_r($params, true));
26+
2327
switch ($params['topic']) {
2428
case 'order/confirmed':
2529
case 'order/authorized':
@@ -42,13 +46,63 @@ public function handleOrderStateChanged($params)
4246
$monduOrder = $this->getOrder($params['order_uuid']);
4347

4448
if ($monduOrder) {
49+
if (
50+
stripos($params['topic'], 'confirmed') !== false ||
51+
stripos($params['topic'], 'authorized') !== false
52+
) {
53+
$order = oxNew(Order::class);
54+
$order->load($monduOrder->getFieldData('oemondu_orders__oxid_order_id'));
55+
56+
$sQuery = "
57+
UPDATE
58+
oxorder
59+
SET
60+
oxfolder = 'ORDERFOLDER_NEW',
61+
oxtransstatus = 'OK'
62+
WHERE
63+
OXORDERNR = '{$order->getFieldData('oxorder__oxordernr')}'
64+
";
65+
DatabaseProvider::getDb()->execute($sQuery);
66+
} else if (stripos($params['topic'], 'declined') !== false) {
67+
$order = oxNew(Order::class);
68+
$order->load($monduOrder->getFieldData('oemondu_orders__oxid_order_id'));
69+
70+
$sQuery = "
71+
UPDATE
72+
oxorder
73+
SET
74+
oxfolder = 'ORDERFOLDER_PROBLEMS',
75+
oxtransstatus = 'CANCELLED'
76+
WHERE
77+
OXORDERNR = '{$order->getFieldData('oxorder__oxordernr')}'
78+
";
79+
DatabaseProvider::getDb()->execute($sQuery);
80+
}
4581
$monduOrder->updateOrderState($params['order_state']);
4682
return [['order' => $monduOrder], Response::HTTP_OK];
4783
}
4884

4985
return [['error' => 'Order not found'], Response::HTTP_BAD_REQUEST];
5086
}
5187

88+
public function getWebhookSecretByShopId($shopId)
89+
{
90+
return Registry::getConfig()->getShopConfVar(
91+
'oemonduWebhookSecret',
92+
$shopId,
93+
'module:oemondu'
94+
);
95+
}
96+
97+
public function getShopId($params)
98+
{
99+
$monduOrder = $this->getOrder($params['order_uuid']);
100+
$order = oxNew(Order::class);
101+
$order->load($monduOrder->getFieldData('oemondu_orders__oxid_order_id'));
102+
103+
return $order->oxorder__oxshopid->value;
104+
}
105+
52106
public function handleInvoiceStateChanged($params, $state)
53107
{
54108
$this->_logger->debug('MonduWebhookHandler [handleInvoiceStateChanged]: ' . print_r($params, true));
@@ -75,4 +129,4 @@ private function getInvoice($invoiceUuid)
75129
$monduInvoice->loadByInvoiceUuid($invoiceUuid);
76130
return $monduInvoice;
77131
}
78-
}
132+
}

Event/SettingChangedSubscriber.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ protected function registerWebhooks()
5858
if ($response['status'] === 409) return;
5959

6060
if (!$response['webhook']) {
61-
$errorMessage = $response['status'] === 403 ? 'INVALID_API_KEY' : 'MONDU_REGISTER_WEBHOOK_ERROR';
62-
63-
return MonduHelper::showErrorMessage($errorMessage);
61+
if ($response['status'] === 403) {
62+
return MonduHelper::showErrorMessage('INVALID_API_KEY');
63+
}
6464
}
6565
}
6666
}

Model/Order.php

+26-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ class Order extends Order_parent
2020
*/
2121
private LoggerInterface $_logger;
2222

23-
2423
public function __construct()
2524
{
2625
parent::__construct();
@@ -99,8 +98,12 @@ public function deleteAllArticles()
9998
*
10099
* @return integer
101100
*/
102-
public function finalizeOrder(\OxidEsales\Eshop\Application\Model\Basket $oBasket, $oUser, $blRecalculatingOrder = false)
103-
{
101+
public function finalizeOrder(
102+
\OxidEsales\Eshop\Application\Model\Basket $oBasket,
103+
$oUser,
104+
$blRecalculatingOrder = false,
105+
$isPending = false
106+
) {
104107
$result = parent::finalizeOrder($oBasket, $oUser, $blRecalculatingOrder);
105108

106109
$this->_logger->debug('MonduOrder [finalizeOrder $result]: ' . print_r($result, true));
@@ -119,6 +122,19 @@ public function finalizeOrder(\OxidEsales\Eshop\Application\Model\Basket $oBaske
119122
$monduOrderUuid,
120123
['external_reference_id' => (string) $this->getFieldData('oxorder__oxordernr')]
121124
);
125+
126+
if ($isPending) {
127+
128+
$this->oxorder__oxfolder = new \OxidEsales\Eshop\Core\Field('ORDERFOLDER_PROBLEMS');
129+
$this->oxorder__oxtransstatus = new \OxidEsales\Eshop\Core\Field('PENDING');
130+
131+
$monduOrder = $this->getOrder($monduOrderUuid);
132+
133+
if ($monduOrder) {
134+
$monduOrder->updateOrderState($params['order_state']);
135+
}
136+
$this->save();
137+
}
122138
}
123139

124140
if ($this->isMonduPayment() && !$this->getMonduOrders()) {
@@ -127,4 +143,11 @@ public function finalizeOrder(\OxidEsales\Eshop\Application\Model\Basket $oBaske
127143

128144
return $result;
129145
}
146+
147+
private function getOrder($orderUuid)
148+
{
149+
$monduOrder = oxNew(MonduOrder::class);
150+
$monduOrder->loadByOrderUuid($orderUuid);
151+
return $monduOrder;
152+
}
130153
}

composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "mondu/bnpl-checkout-oxid",
33
"description": "Mondu payment method for the OXID eShop.",
44
"type": "oxideshop-module",
5-
"version": "1.1.8",
5+
"version": "1.1.6",
66
"license": [
77
"OSL-3.0",
88
"AFL-3.0"

0 commit comments

Comments
 (0)