From 829110afe48c9e729373c56204aef59addb6c8d5 Mon Sep 17 00:00:00 2001 From: david-fiaty-cko Date: Tue, 31 Oct 2017 16:33:02 +0000 Subject: [PATCH 01/12] Enhanced order status management Implemented order status management for authorisation, capture, refund and flag. --- Controller/Payment/Verify.php | 3 +- Gateway/Config/Config.php | 52 +++++++++++---- Model/Service/CallbackService.php | 6 +- etc/adminhtml/system.xml | 104 +++++++++++++++++++----------- etc/config.xml | 15 ++--- 5 files changed, 117 insertions(+), 63 deletions(-) diff --git a/Controller/Payment/Verify.php b/Controller/Payment/Verify.php index a3b64f37e..e0522e90a 100755 --- a/Controller/Payment/Verify.php +++ b/Controller/Payment/Verify.php @@ -243,8 +243,7 @@ public function placeLocalPaymentOrder() { ->setLastOrderStatus($order->getStatus()); // Update order status - $order->setState(\Magento\Sales\Model\Order::STATE_NEW); - $order->setStatus($this->gatewayConfig->getNewOrderStatus()); + $order->setStatus($this->gatewayConfig->getOrderStatusCaptured()); // Set email sent $order->setEmailSent(1); diff --git a/Gateway/Config/Config.php b/Gateway/Config/Config.php index 28c2bca32..8159d48da 100755 --- a/Gateway/Config/Config.php +++ b/Gateway/Config/Config.php @@ -23,7 +23,6 @@ class Config extends BaseConfig { const KEY_CC_TYPES = 'cctypes'; const KEY_USE_CVV = 'useccv'; const KEY_COUNTRY_CREDIT_CARD = 'countrycreditcard'; - const KEY_INTEGRATION = 'integration'; const KEY_PUBLIC_KEY = 'public_key'; const KEY_SECRET_KEY = 'secret_key'; @@ -32,39 +31,34 @@ class Config extends BaseConfig { const KEY_AUTO_CAPTURE_TIME = 'auto_capture_time'; const KEY_VERIFY_3DSECURE = 'verify_3dsecure'; const KEY_ATTEMPT_N3D = 'attemptN3D'; - const KEY_SANDBOX_API_URL = 'sandbox_api_url'; const KEY_LIVE_API_URL = 'live_api_url'; - const KEY_SANDBOX_EMBEDDED_URL = 'sandbox_embedded_url'; const KEY_SANDBOX_HOSTED_URL = 'sandbox_hosted_url'; const KEY_LIVE_EMBEDDED_URL = 'live_embedded_url'; const KEY_LIVE_HOSTED_URL = 'live_hosted_url'; - const MIN_AUTO_CAPTURE_TIME = 0; const MAX_AUTO_CAPTURE_TIME = 168; - - CONST KEY_USE_DESCRIPTOR = 'descriptor_enable'; + const KEY_USE_DESCRIPTOR = 'descriptor_enable'; const KEY_DESCRIPTOR_NAME = 'descriptor_name'; const KEY_DESCRIPTOR_CITY = 'descriptor_city'; - const CODE_3DSECURE = 'three_d_secure'; - const KEY_THEME_COLOR = 'theme_color'; const KEY_BUTTON_LABEL = 'button_label'; - const KEY_NEW_ORDER_STATUS = 'new_order_status'; + const KEY_ORDER_STATUS_AUTHORIZED = 'order_status_authorized'; + const KEY_ORDER_STATUS_CAPTURED = 'order_status_captured'; + const KEY_ORDER_STATUS_REFUNDED = 'order_status_refunded'; + const KEY_ORDER_STATUS_FLAGGED = 'order_status_flagged'; const KEY_ACCEPTED_CURRENCIES = 'accepted_currencies'; const KEY_PAYMENT_CURRENCY = 'payment_currency'; const KEY_CUSTOM_CURRENCY = 'custom_currency'; const KEY_PAYMENT_MODE = 'payment_mode'; const KEY_AUTO_GENERATE_INVOICE = 'auto_generate_invoice'; - const KEY_EMBEDDED_THEME = 'embedded_theme'; const KEY_EMBEDDED_CSS = 'embedded_css'; const KEY_CUSTOM_CSS = 'custom_css'; const KEY_CSS_FILE = 'css_file'; - const KEY_VAULT_TITLE = 'checkout_com_cc_vault/title'; /** @@ -128,6 +122,42 @@ public function getNewOrderStatus() { return (string) $this->getValue(self::KEY_NEW_ORDER_STATUS); } + /** + * Returns the authorized order status. + * + * @return string + */ + public function getOrderStatusAuthorized() { + return (string) $this->getValue(self::KEY_ORDER_STATUS_AUTHORIZED); + } + + /** + * Returns the captured order status. + * + * @return string + */ + public function getOrderStatusCaptured() { + return (string) $this->getValue(self::KEY_ORDER_STATUS_CAPTURED); + } + + /** + * Returns the refunded order status. + * + * @return string + */ + public function getOrderStatusRefunded() { + return (string) $this->getValue(self::KEY_ORDER_STATUS_REFUNDED); + } + + /** + * Returns the flagged order status. + * + * @return string + */ + public function getOrderStatusFlagged() { + return (string) $this->getValue(self::KEY_ORDER_STATUS_FLAGGED); + } + /** * Returns the design settings. * diff --git a/Model/Service/CallbackService.php b/Model/Service/CallbackService.php index b37d67377..a20460041 100755 --- a/Model/Service/CallbackService.php +++ b/Model/Service/CallbackService.php @@ -136,8 +136,7 @@ public function run() { // Perform authorize complementary actions if ($commandName == 'authorize') { // Update order status - $order->setState(\Magento\Sales\Model\Order::STATE_NEW); - $order->setStatus(\Magento\Sales\Model\Order::STATE_PENDING_PAYMENT); + $order->setStatus($this->gatewayConfig->getOrderStatusAuthorized()); // Delete comments history foreach ($order->getAllStatusHistory() as $orderComment) { @@ -158,8 +157,7 @@ public function run() { // Perform capture complementary actions if ($commandName == 'capture') { // Update order status - $order->setState(\Magento\Sales\Model\Order::STATE_NEW); - $order->setStatus($this->gatewayConfig->getNewOrderStatus()); + $order->setStatus($this->gatewayConfig->getOrderStatusCaptured()); // Create new comment $newComment = 'Captured amount of ' . ChargeAmountAdapter::getStoreAmountOfCurrency($this->gatewayResponse['response']['message']['value'], $this->gatewayResponse['response']['message']['currency']) . ' ' . $this->gatewayResponse['response']['message']['currency'] .' Transaction ID: ' . $this->gatewayResponse['response']['message']['id']; diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index d4ca2c381..135770778 100755 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -41,29 +41,13 @@ payment/checkout_com/sort_order - - - CheckoutCom\Magento2\Model\Adminhtml\Source\OrderStatus - payment/checkout_com/new_order_status - Select the status that should be used for new orders. - - - - - Magento\Config\Model\Config\Source\Yesno - payment/checkout_com/auto_generate_invoice - - When set to "Yes", an invoice will be generated automatically for the new order status selected above. - - - - + CheckoutCom\Magento2\Model\Adminhtml\Source\Integration payment/checkout_com/integration - + CheckoutCom\Magento2\Model\Adminhtml\Source\PaymentMode payment/checkout_com/payment_mode @@ -72,7 +56,7 @@ - + payment/checkout_com/theme_color Provide a color code in hexadecimal format. Ex: #00b660. @@ -81,7 +65,7 @@ - + payment/checkout_com/button_label @@ -89,7 +73,7 @@ - + CheckoutCom\Magento2\Model\Adminhtml\Source\EmbeddedTheme payment/checkout_com/embedded_theme @@ -98,7 +82,7 @@ - + CheckoutCom\Magento2\Model\Adminhtml\Source\CssFile payment/checkout_com/css_file @@ -107,7 +91,7 @@ - + CheckoutCom\Magento2\Model\Config\Backend\CustomCss checkout_com @@ -187,15 +171,63 @@ - + + + + + + CheckoutCom\Magento2\Model\Adminhtml\Source\OrderStatus + payment/checkout_com/new_order_status + Select the status that should be used for new orders. + + + + + CheckoutCom\Magento2\Model\Adminhtml\Source\OrderStatus + payment/checkout_com/order_status_authorized + Select the status that should be used for orders with successful payment authorisation. + + + + + CheckoutCom\Magento2\Model\Adminhtml\Source\OrderStatus + payment/checkout_com/order_status_captured + Select the status that should be used for orders with successful payment capture. + + + + + CheckoutCom\Magento2\Model\Adminhtml\Source\OrderStatus + payment/checkout_com/order_status_refunded + Select the status that should be used for new orders with successful payment refund. + + + + + CheckoutCom\Magento2\Model\Adminhtml\Source\OrderStatus + payment/checkout_com/order_status_flagged + Select the status that should be used for flagged orders. + + + + + Magento\Config\Model\Config\Source\Yesno + payment/checkout_com/auto_generate_invoice + + When set to "Yes", an invoice will be generated automatically for the new order status selected above. + + + + + - + CheckoutCom\Magento2\Model\Adminhtml\Source\PaymentCurrency payment/checkout_com/payment_currency - + CheckoutCom\Magento2\Model\Adminhtml\Source\CustomCurrency payment/checkout_com/custom_currency @@ -205,7 +237,7 @@ If you select a single specific currency, please make sure the selected currency is available and activated in your merchant account. Contact your Checkout.com account manager or send an email to support@checkout.com for more information. - + Magento\Config\Model\Config\Source\Locale\Currency payment/checkout_com/accepted_currencies @@ -213,35 +245,35 @@ - + - + Magento\Payment\Model\Config\Source\Allspecificcountries - + Magento\Directory\Model\Config\Source\Country - + - + Magento\Config\Model\Config\Backend\Encrypted payment/checkout_com/public_key - + Magento\Config\Model\Config\Backend\Encrypted payment/checkout_com/secret_key - + Magento\Config\Model\Config\Backend\Encrypted payment/checkout_com/private_shared_key @@ -249,7 +281,7 @@ - + @@ -268,7 +300,7 @@ - + diff --git a/etc/config.xml b/etc/config.xml index 828f518ff..6066d5a5b 100755 --- a/etc/config.xml +++ b/etc/config.xml @@ -19,46 +19,41 @@ 1 1 1 - processing - complete + pending + pending_payment + processing + canceled + fraud base_currency USD USD 0 1 - #00b660 Pay Now cards standard default - sandbox hosted authorize 0 0 - - - - - 1 public_key,secret_key cvv,number cc_type,cc_number,card_expiry_month,card_expiry_year,card_last_4,status,responseMessage,responseAdvancedInfo,responseCode,authCode,three_d_secure - 1 1.00 USD From 756350b93d70403c2d036d0a5f44bc77805485f7 Mon Sep 17 00:00:00 2001 From: david-fiaty-cko Date: Tue, 31 Oct 2017 16:54:06 +0000 Subject: [PATCH 02/12] Order comments override Make order comments override optional in module config. --- Gateway/Config/Config.php | 11 ++++++++++- Model/Service/CallbackService.php | 32 ++++++++++++++++++------------- etc/adminhtml/system.xml | 12 +++++++++++- etc/config.xml | 1 + 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/Gateway/Config/Config.php b/Gateway/Config/Config.php index 8159d48da..10d99c02d 100755 --- a/Gateway/Config/Config.php +++ b/Gateway/Config/Config.php @@ -60,7 +60,7 @@ class Config extends BaseConfig { const KEY_CUSTOM_CSS = 'custom_css'; const KEY_CSS_FILE = 'css_file'; const KEY_VAULT_TITLE = 'checkout_com_cc_vault/title'; - + const KEY_ORDER_COMMENTS_OVERRIDE = 'order_comments_override'; /** * @var array */ @@ -219,6 +219,15 @@ public function isActive() { return (bool) in_array($quote->getQuoteCurrencyCode(), $this->getAcceptedCurrencies()); } + /** + * Determines if the core order comments need override. + * + * @return bool + */ + public function overrideOrderComments() { + return (bool) $this->getValue(self::KEY_ORDER_COMMENTS_OVERRIDE); + } + /** * Determines if debug mode is enabled. * diff --git a/Model/Service/CallbackService.php b/Model/Service/CallbackService.php index a20460041..6265150c1 100755 --- a/Model/Service/CallbackService.php +++ b/Model/Service/CallbackService.php @@ -114,6 +114,8 @@ public function run() { $order = $this->getAssociatedOrder(); $payment = $order->getPayment(); + $overrideComments = $this->gatewayConfig->overrideOrderComments(); + if($payment instanceof Payment) { $infoInstance = $payment->getMethodInstance()->getInfoInstance(); $this->callbackMethod->setInfoInstance($infoInstance); @@ -138,20 +140,21 @@ public function run() { // Update order status $order->setStatus($this->gatewayConfig->getOrderStatusAuthorized()); - // Delete comments history - foreach ($order->getAllStatusHistory() as $orderComment) { - $orderComment->delete(); - } - // Set email sent $order->setEmailSent(1); - // Create new comment - $newComment = 'Authorized amount of ' . ChargeAmountAdapter::getStoreAmountOfCurrency($this->gatewayResponse['response']['message']['value'], $this->gatewayResponse['response']['message']['currency']) . ' ' . $this->gatewayResponse['response']['message']['currency'] .' Transaction ID: ' . $this->gatewayResponse['response']['message']['id']; + if ($overrideComments) { + // Delete comments history + foreach ($order->getAllStatusHistory() as $orderComment) { + $orderComment->delete(); + } - // Add the new comment - $order->addStatusToHistory($order->getStatus(), $newComment, $notify = true); + // Create new comment + $newComment = 'Authorized amount of ' . ChargeAmountAdapter::getStoreAmountOfCurrency($this->gatewayResponse['response']['message']['value'], $this->gatewayResponse['response']['message']['currency']) . ' ' . $this->gatewayResponse['response']['message']['currency'] .' Transaction ID: ' . $this->gatewayResponse['response']['message']['id']; + // Add the new comment + $order->addStatusToHistory($order->getStatus(), $newComment, $notify = true); + } } // Perform capture complementary actions @@ -159,13 +162,16 @@ public function run() { // Update order status $order->setStatus($this->gatewayConfig->getOrderStatusCaptured()); - // Create new comment - $newComment = 'Captured amount of ' . ChargeAmountAdapter::getStoreAmountOfCurrency($this->gatewayResponse['response']['message']['value'], $this->gatewayResponse['response']['message']['currency']) . ' ' . $this->gatewayResponse['response']['message']['currency'] .' Transaction ID: ' . $this->gatewayResponse['response']['message']['id']; + if ($overrideComments) { + // Create new comment + $newComment = 'Captured amount of ' . ChargeAmountAdapter::getStoreAmountOfCurrency($this->gatewayResponse['response']['message']['value'], $this->gatewayResponse['response']['message']['currency']) . ' ' . $this->gatewayResponse['response']['message']['currency'] .' Transaction ID: ' . $this->gatewayResponse['response']['message']['id']; - // Add the new comment - $order->addStatusToHistory($order->getStatus(), $newComment, $notify = true); + // Add the new comment + $order->addStatusToHistory($order->getStatus(), $newComment, $notify = true); + } } + // Save the order $this->orderRepository->save($order); } } diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 135770778..d9b762aeb 100755 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -172,7 +172,7 @@ - + @@ -217,6 +217,16 @@ When set to "Yes", an invoice will be generated automatically for the new order status selected above. + + + + Magento\Config\Model\Config\Source\Yesno + payment/checkout_com/order_comments_override + + Set to "Yes" when using multiple currencies to avoid orders being flagged as fraud by Magento core. + + + diff --git a/etc/config.xml b/etc/config.xml index 6066d5a5b..2ed437516 100755 --- a/etc/config.xml +++ b/etc/config.xml @@ -24,6 +24,7 @@ processing canceled fraud + 1 base_currency USD USD From d02879ab6d041b9d96f8f99a95c9f1520cf387fb Mon Sep 17 00:00:00 2001 From: david-fiaty-cko Date: Tue, 31 Oct 2017 17:00:45 +0000 Subject: [PATCH 03/12] Updated module config comment Updated the comment for the automatic invoice generation field. --- etc/adminhtml/system.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index d9b762aeb..b7489ae74 100755 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -214,7 +214,7 @@ Magento\Config\Model\Config\Source\Yesno payment/checkout_com/auto_generate_invoice - When set to "Yes", an invoice will be generated automatically for the new order status selected above. + When set to "Yes", an invoice will be generated automatically for the captured order status selected above. From 73d3fc5a5270003ead680d87c930b75b3dc02f80 Mon Sep 17 00:00:00 2001 From: david-fiaty-cko Date: Tue, 7 Nov 2017 17:40:55 +0000 Subject: [PATCH 04/12] Pre and post auth order creation. Added a feature to decide if order should be created before or after payment authorisation. --- Controller/Payment/PlaceOrder.php | 130 ++++++++++--- Controller/Payment/PlaceOrderAjax.php | 130 +++++++++++++ Controller/Payment/Verify.php | 21 +-- Gateway/Config/Config.php | 11 ++ Gateway/Http/Client/AbstractTransaction.php | 11 +- Gateway/Response/TransactionHandler.php | 15 +- Model/Adminhtml/Source/OrderCreation.php | 38 ++++ Model/Resource/MobilePayment.php | 3 - Model/Service/CallbackService.php | 74 +++++--- Model/Service/PaymentTokenService.php | 8 +- Model/Service/TokenChargeService.php | 173 ++++++++++++++++++ Model/Service/TransactionService.php | 74 ++++++++ Model/Ui/ConfigProvider.php | 1 + etc/adminhtml/system.xml | 40 ++-- etc/config.xml | 2 + .../view/payment/method-renderer/cc-form.js | 5 +- .../view/payment/method-renderer/embedded.js | 8 +- .../js/view/payment/method-renderer/hosted.js | 91 +++++++-- 18 files changed, 725 insertions(+), 110 deletions(-) create mode 100755 Controller/Payment/PlaceOrderAjax.php create mode 100644 Model/Adminhtml/Source/OrderCreation.php create mode 100755 Model/Service/TokenChargeService.php create mode 100755 Model/Service/TransactionService.php diff --git a/Controller/Payment/PlaceOrder.php b/Controller/Payment/PlaceOrder.php index c869903ee..798032c50 100755 --- a/Controller/Payment/PlaceOrder.php +++ b/Controller/Payment/PlaceOrder.php @@ -11,18 +11,34 @@ namespace CheckoutCom\Magento2\Controller\Payment; use Magento\Framework\App\Action\Context; -use Magento\Checkout\Model\Session; +use Magento\Checkout\Model\Session as CheckoutSession; use Magento\Customer\Model\Session as CustomerSession; use CheckoutCom\Magento2\Gateway\Config\Config as GatewayConfig; use CheckoutCom\Magento2\Model\Service\OrderService; use Magento\Customer\Api\Data\GroupInterface; +use Magento\Sales\Api\Data\OrderInterface; +use CheckoutCom\Magento2\Model\Ui\ConfigProvider; +use Magento\Sales\Model\Order\Payment\Transaction; +use Magento\Sales\Model\Order\Payment\Transaction\BuilderInterface; +use CheckoutCom\Magento2\Model\Service\TokenChargeService; +use Magento\Sales\Model\Order\Email\Sender\OrderSender; class PlaceOrder extends AbstractAction { /** - * @var Session + * @var TokenChargeService */ - protected $session; + protected $tokenChargeService; + + /** + * @var CheckoutSession + */ + protected $checkoutSession; + + /** + * @var OrderInterface + */ + protected $orderInterface; /** * @var OrderService @@ -34,19 +50,39 @@ class PlaceOrder extends AbstractAction { */ protected $customerSession; + /** + * @var OrderSender + */ + private $orderSender; + /** * PlaceOrder constructor. * @param Context $context - * @param Session $session + * @param CheckoutSession $checkoutSession * @param GatewayConfig $gatewayConfig + * @param OrderInterface $orderInterface * @param OrderService $orderService + * @param Order $orderManager + * @param OrderSender $orderSender */ - public function __construct(Context $context, Session $session, GatewayConfig $gatewayConfig, OrderService $orderService, CustomerSession $customerSession) { + public function __construct( + Context $context, + CheckoutSession $checkoutSession, + GatewayConfig $gatewayConfig, + OrderService $orderService, + OrderInterface $orderInterface, + CustomerSession $customerSession, + TokenChargeService $tokenChargeService, + OrderSender $orderSender + ) { parent::__construct($context, $gatewayConfig); - $this->session = $session; - $this->customerSession = $customerSession; - $this->orderService = $orderService; + $this->checkoutSession = $checkoutSession; + $this->customerSession = $customerSession; + $this->orderService = $orderService; + $this->orderInterface = $orderInterface; + $this->tokenChargeService = $tokenChargeService; + $this->orderSender = $orderSender; } /** @@ -55,31 +91,70 @@ public function __construct(Context $context, Session $session, GatewayConfig $g * @return \Magento\Framework\Controller\Result\Redirect */ public function execute() { + // Retrieve the request parameters - $resultRedirect = $this->getResultRedirect(); - $cardToken = $this->getRequest()->getParam('cko-card-token'); - $email = $this->getRequest()->getParam('cko-context-id'); - $agreement = array_keys($this->getRequest()->getPostValue('agreement', [])); - $quote = $this->session->getQuote(); + $params = array( + 'cardToken' => $this->getRequest()->getParam('cko-card-token'), + 'email' => $this->getRequest()->getParam('cko-context-id'), + 'agreement' => array_keys($this->getRequest()->getPostValue('agreement', [])), + 'quote' => $this->checkoutSession->getQuote() + ); + + $order = null; + if (isset($this->customerSession->getData('checkoutSessionData')['orderTrackId'])) { + $order = $this->orderInterface->loadByIncrementId($this->customerSession->getData('checkoutSessionData')['orderTrackId']); + } + + if ($order) { + $this->updateOrder($params, $order); + } + else { + $this->createOrder($params); + } + } + + public function updateOrder($params, $order) { + + // Create the charge for order already placed + $updateSuccess = $this->tokenChargeService->sendChargeRequest($params['cardToken'], $order); + + // Update payment data + $order = $this->updatePaymentData($order); + + // Send email + if ($updateSuccess) { + $order->setEmailSent(1)->save(); + $this->orderSender->send($order); + } + + // 3D Secure redirection if needed + if($this->gatewayConfig->isVerify3DSecure()) { + $this->place3DSecureRedirectUrl(); + exit(); + } + + return $this->_redirect('checkout/onepage/success', ['_secure' => true]); + } + + public function createOrder($params) { // Check for guest email - if ($quote->getCustomerEmail() === null + if ($params['quote']->getCustomerEmail() === null && $this->customerSession->isLoggedIn() === false && isset($this->customerSession->getData('checkoutSessionData')['customerEmail']) - && $this->customerSession->getData('checkoutSessionData')['customerEmail'] === $email) + && $this->customerSession->getData('checkoutSessionData')['customerEmail'] === $params['email']) { - $quote->setCustomerId(null) - ->setCustomerEmail($email) + $params['quote']->setCustomerId(null) + ->setCustomerEmail($params['email']) ->setCustomerIsGuest(true) ->setCustomerGroupId(GroupInterface::NOT_LOGGED_IN_ID); } // Perform quote and order validation try { - // Create an order from the quote - $this->validateQuote($quote); - $this->orderService->execute($quote, $cardToken, $agreement); + $this->validateQuote($params['quote']); + $this->orderService->execute($params['quote'], $params['cardToken'], $params['agreement']); // 3D Secure redirection if needed if($this->gatewayConfig->isVerify3DSecure()) { @@ -87,13 +162,22 @@ public function execute() { exit(); } - return $resultRedirect->setPath('checkout/onepage/success', ['_secure' => true]); + return $this->_redirect('checkout/onepage/success', ['_secure' => true]); } catch (\Exception $e) { $this->messageManager->addExceptionMessage($e, $e->getMessage()); } - return $resultRedirect->setPath('checkout/cart', ['_secure' => true]); + return $this->_redirect('checkout/cart', ['_secure' => true]); + } + + public function updatePaymentData($order) { + $payment = $order->getPayment(); + $payment->setMethod(ConfigProvider::CODE); + $payment->save(); + $order->save(); + + return $order; } /** @@ -104,7 +188,7 @@ public function execute() { public function place3DSecureRedirectUrl() { echo '