diff --git a/Controller/CancelController.php b/Controller/CancelController.php new file mode 100644 index 0000000..9715f08 --- /dev/null +++ b/Controller/CancelController.php @@ -0,0 +1,15 @@ +addErrorToDisplay('Mondu: Order has been canceled'); + Registry::getUtils()->redirect(Registry::getConfig()->getShopSecureHomeUrl() . 'cl=payment', false); + } +} diff --git a/Controller/DeclinedController.php b/Controller/DeclinedController.php new file mode 100644 index 0000000..69538dc --- /dev/null +++ b/Controller/DeclinedController.php @@ -0,0 +1,15 @@ +addErrorToDisplay('Mondu: Order has been declined'); + Registry::getUtils()->redirect(Registry::getConfig()->getShopSecureHomeUrl() . 'cl=payment', false); + } +} diff --git a/Controller/MonduCheckoutController.php b/Controller/MonduCheckoutController.php index cab05be..2fbcf25 100644 --- a/Controller/MonduCheckoutController.php +++ b/Controller/MonduCheckoutController.php @@ -38,7 +38,10 @@ public function createOrder() $session->setVariable('mondu_order_uuid', $token); } - echo json_encode(['token' => $token]); + echo json_encode([ + 'token' => $token, + 'hostedCheckoutUrl' => $response['hosted_checkout_url'] ?? false + ]); exit(); } diff --git a/Controller/OrderController.php b/Controller/OrderController.php index 9ea163d..0322274 100644 --- a/Controller/OrderController.php +++ b/Controller/OrderController.php @@ -2,22 +2,106 @@ namespace OxidEsales\MonduPayment\Controller; +use OxidEsales\Eshop\Application\Model\Basket; +use OxidEsales\Eshop\Application\Model\Order; +use OxidEsales\Eshop\Application\Model\User; +use OxidEsales\Eshop\Core\Exception\StandardException; use OxidEsales\Eshop\Core\Registry; +use OxidEsales\Eshop\Core\Request; +use OxidEsales\Eshop\Core\UtilsView; +use OxidEsales\MonduPayment\Core\Http\MonduClient; use OxidEsales\MonduPayment\Core\Utils\MonduHelper; +use Symfony\Component\Config\Definition\Exception\Exception; class OrderController extends OrderController_parent { - public function isMonduPayment() - { - $session = Registry::getSession(); - $paymentId = $session->getVariable("paymentid"); - - return MonduHelper::isMonduPayment($paymentId); - } - - public function getPaymentPageUrl() - { - $shopUrl = $this->getConfig()->getShopSecureHomeURL(); - return $shopUrl . '&cl=payment&payerror=2'; - } + private MonduClient $_client; + private User|null|false $_oUser; + + public function __construct() + { + parent::__construct(); + + $this->_client = oxNew(MonduClient::class); + $this->_oUser = $this->getUser(); + } + + public function isMonduPayment() + { + $session = Registry::getSession(); + $paymentId = $session->getVariable('paymentid'); + + return MonduHelper::isMonduPayment($paymentId); + } + + public function getPaymentPageUrl() + { + $shopUrl = \OxidEsales\Eshop\Core\Registry::getConfig()->getShopSecureHomeURL(); + return $shopUrl . '&cl=payment&payerror=2'; + } + + /** + * @throws \Exception + */ + public function execute() + { + if($this->isMonduPayment()){ + $orderUuid = Registry::get(Request::class)->getRequestEscapedParameter('order_uuid'); + + if (!$orderUuid) { + throw new \Exception('Mondu: Not found'); + } + + $monduOrder = $this->_client->getMonduOrder($orderUuid); + $response = $this->_client->confirmOrder($orderUuid); + + if (isset($response['state']) && $response['state'] == 'confirmed') { + try { + $iSuccess = $this->monduExecute($this->getBasket()); + + return $this->_getNextStep($iSuccess); + } catch (Exception $e) { + throw new \Exception('Mondu: Error during the order process'); + } + } + } + + // if user is not logged in set the user + if(!$this->getUser() && isset($this->_oUser)){ + $this->setUser($this->_oUser); + } + + return parent::execute(); + } + + /** + * Save order to database, delete order_id from session and redirect to thank you page + * + * @param Basket $oBasket + * + * @return bool|int|mixed + */ + protected function monduExecute(Basket $oBasket) + { + if (!Registry::getSession()->getVariable('sess_challenge')) { + Registry::getSession()->setVariable('sess_challenge', Registry::getUtilsObject()->generateUID()); + } + + $iSuccess = 0; + $oBasket->calculateBasket(true); + $oOrder = oxNew(Order::class); + + try { + $iSuccess = $oOrder->finalizeOrder($oBasket, $oBasket->getUser()); + } catch (StandardException $e) { + Registry::get(UtilsView::class)->addErrorToDisplay($e); + } + + if ($iSuccess === 1) { + // performing special actions after user finishes order (assignment to special user groups) + $this->_oUser->onOrderExecute($oBasket, $iSuccess); + } + + return $iSuccess; + } } diff --git a/Core/Http/HttpRequest.php b/Core/Http/HttpRequest.php index bb0ac0c..0dbd952 100644 --- a/Core/Http/HttpRequest.php +++ b/Core/Http/HttpRequest.php @@ -143,7 +143,10 @@ public function send_request(string $url, array $data, string $method) $response = json_decode($response, true); - if (@$response['errors'] != null || @$response['error'] != null) { + if ( + (isset($response['errors']) && $response['errors'] != null) || + (isset($response['error']) && $response['error'] != null) + ) { throw new InvalidRequestException('[MONDU__ERROR] ' . json_encode($response), $data, $response); } diff --git a/Core/Http/MonduClient.php b/Core/Http/MonduClient.php index cfa7147..1df7df9 100644 --- a/Core/Http/MonduClient.php +++ b/Core/Http/MonduClient.php @@ -13,8 +13,8 @@ class MonduClient { private Config $_config; private HttpRequest $_client; - private $_baseUrl = ''; - private $_logger = null; + private $_baseUrl; + private $_logger; public function __construct() { @@ -46,6 +46,12 @@ public function updateOrderExternalInfo($orderUuid, $data = []) return $order['order'] ?? null; } + public function confirmOrder($orderUuid, $data = []) + { + $order = $this->sendRequest('POST', 'orders/' . $orderUuid . '/confirm', $data); + return $order['order'] ?? null; + } + public function getMonduOrder($orderUuid) { $order = $this->sendRequest('GET', 'orders/' . $orderUuid); @@ -108,8 +114,8 @@ protected function sendRequest($method = 'GET', $url = '', $body = []) { try { $url = $this->_baseUrl . $url; - $response = $this->_client->send_request($url, $body, $method); - return $response; + + return $this->_client->send_request($url, $body, $method); } catch (InvalidRequestException $e) { $this->_logger->error("MonduClient [{$method} {$url}]: Failed with an exception message: {$e->getString()}"); diff --git a/Core/Mappers/MonduOrderMapper.php b/Core/Mappers/MonduOrderMapper.php index 566bf29..7f8d993 100644 --- a/Core/Mappers/MonduOrderMapper.php +++ b/Core/Mappers/MonduOrderMapper.php @@ -2,6 +2,7 @@ namespace OxidEsales\MonduPayment\Core\Mappers; +use OxidEsales\Eshop\Core\Registry; use OxidEsales\MonduPayment\Core\Utils\MonduHelper; class MonduOrderMapper @@ -36,20 +37,27 @@ protected function getBasketUser() public function getMappedOrderData($paymentMethod) { + $session = Registry::getSession(); $basket = $this->getBasket(); - + $monduOrderUuid = $session->getVariable('mondu_order_uuid'); $tax = array_values($basket->getProductVats(false))[0]; $discount = $basket->getTotalDiscount()->getPrice(); $shipping = $basket->getDeliveryCost()->getPrice(); + $shopUrl = Registry::getConfig()->getCurrentShopUrl(); + $externalReferenceId = uniqid('M_OX_'); $data = [ "currency" => $basket->getBasketCurrency()->name, "payment_method" => $paymentMethod, - "external_reference_id" => uniqid('M_OX_'), + "external_reference_id" => $externalReferenceId, "gross_amount_cents" => round($basket->getPriceForPayment() * 100), "buyer" => MonduHelper::removeEmptyElementsFromArray($this->getBuyerData()), "billing_address" => MonduHelper::removeEmptyElementsFromArray($this->getUserBillingAddress()), "shipping_address" => MonduHelper::removeEmptyElementsFromArray($this->getUserDeliveryAddress()), + "success_url" => $shopUrl . '?cl=order&fnc=execute&order_uuid=' . $monduOrderUuid . '&sDeliveryAddressMD5=' . $this->getBasketUser()->getEncodedDeliveryAddress(), + "cancel_url" => $shopUrl . '?cl=oemonducancel', + "declined_url" => $shopUrl . '?cl=oemondudeclined', + "state_flow" => 'authorization_flow', "lines" => [[ "tax_cents" => round($tax * 100), "shipping_price_cents" => round($shipping * 100), diff --git a/composer.json b/composer.json index 6f5c41c..f39ec85 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "mondu/bnpl-checkout-oxid", "description": "Mondu payment method for the OXID eShop.", "type": "oxideshop-module", - "version": "1.0.3", + "version": "1.1.0", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/metadata.php b/metadata.php index 02dfbfe..0065232 100644 --- a/metadata.php +++ b/metadata.php @@ -10,7 +10,7 @@ 'en' => 'Module for Mondu payment.', ), 'thumbnail' => 'out/src/images/logo.png', - 'version' => '1.0.3', + 'version' => '1.1.0', 'author' => 'Mondu GmbH', 'url' => 'https://www.mondu.ai', 'email' => 'contact@mondu.ai', @@ -34,7 +34,9 @@ ), 'controllers' => array( 'oemonducheckout' => \OxidEsales\MonduPayment\Controller\MonduCheckoutController::class, - 'oemonduwebhooks' => \OxidEsales\MonduPayment\Controller\MonduWebhooksController::class + 'oemonduwebhooks' => \OxidEsales\MonduPayment\Controller\MonduWebhooksController::class, + 'oemonducancel' => \OxidEsales\MonduPayment\Controller\CancelController::class, + 'oemondudeclined' => \OxidEsales\MonduPayment\Controller\DeclinedController::class ), 'blocks' => array( array( diff --git a/out/src/js/mondu_checkout.js b/out/src/js/mondu_checkout.js index 67682e0..98839f3 100644 --- a/out/src/js/mondu_checkout.js +++ b/out/src/js/mondu_checkout.js @@ -36,13 +36,17 @@ class MonduCheckout { event.preventDefault(); if (this._isWidgetLoaded) { - const token = await this._getMonduToken(); + const monduOrderData = await this._getMonduOrderData(); - if (!token) { + if (!monduOrderData || !monduOrderData.token) { window.location.href = this._paymentUrl; } - this._renderWidget(token); + if (monduOrderData.hostedCheckoutUrl) { + window.location.href = monduOrderData.hostedCheckoutUrl; + } else { + this._renderWidget(monduOrderData.token); + } } } @@ -57,13 +61,13 @@ class MonduCheckout { }); } - async _getMonduToken() { + async _getMonduOrderData() { try { const client = new HttpRequest(); const { data } = await client.post('?cl=oemonducheckout&fnc=createOrder', {}); if (data.token !== 'error') { - return data.token; + return data; } else { return null; }