diff --git a/Block/Adminhtml/System/Config/Form/Field/UpaWebhookSetting.php b/Block/Adminhtml/System/Config/Form/Field/UpaWebhookSetting.php new file mode 100644 index 00000000..cfae33f0 --- /dev/null +++ b/Block/Adminhtml/System/Config/Form/Field/UpaWebhookSetting.php @@ -0,0 +1,65 @@ +scopeConfig = $scopeConfig; + parent::__construct($context, $data); + } + + /** + * @param AbstractElement $element + * @return string + */ + protected function _getElementHtml(AbstractElement $element) + { + $request = $this->getRequest(); + $website = $request->getParam('website'); + $store = $request->getParam('store'); + + if ($store) { + $scope = ScopeInterface::SCOPE_STORE; + $scopeId = $store; + } elseif ($website) { + $scope = ScopeInterface::SCOPE_WEBSITE; + $scopeId = $website; + } else { + $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT; + $scopeId = null; + } + + if ($this->scopeConfig->isSetFlag('payment/omise/is_upa_feature_flag_enabled', $scope, $scopeId)) { + $element->setValue('1'); + $element->setDisabled(true); + $html = parent::_getElementHtml($element) + . ''; + } else { + $html = parent::_getElementHtml($element); + } + + return '
' + . $html . '
'; + } +} diff --git a/Controller/Callback/UPACallback.php b/Controller/Callback/UPACallback.php index beffef73..122eb8eb 100644 --- a/Controller/Callback/UPACallback.php +++ b/Controller/Callback/UPACallback.php @@ -1,21 +1,11 @@ session = $session; $this->omise = $omise; - $this->charge = $charge; - $this->helper = $helper; - $this->emailHelper = $emailHelper; - $this->config = $config; - $this->checkoutSession = $checkoutSession; - $this->request = $request; - $this->omiseCheckoutSession = $omiseCheckoutSession; $this->omise->defineUserAgent(); $this->omise->defineApiVersion(); $this->omise->defineApiKeys(); @@ -114,7 +48,6 @@ public function __construct( */ public function execute() { - $finalPayment = []; $order = $this->session->getLastRealOrder(); if (!$this->isValid($order)) { @@ -126,139 +59,6 @@ public function execute() return $this->redirect(self::PATH_SUCCESS); } - try { - $payment = $order->getPayment(); - $checkoutSession = $this->getCheckoutSession($payment); - $sessionPayments = $checkoutSession->payments; - - if ($checkoutSession && !is_array($sessionPayments) || empty($sessionPayments)) { - $errorMessage = __('The payment session is invalid or no payment information was found. Please contact our support if you have any questions.'); - return $this->redirectBackToCart($order, $errorMessage); - } - - $sessionStatus = $checkoutSession->status; - $finalPayment = $this->getFinalPayment($sessionPayments, $sessionStatus); - - if (!empty($finalPayment) && !empty($finalPayment['charge_id'])) { - $chargeId = $finalPayment['charge_id']; - $charge = $this->charge->find($chargeId); - } else { - $errorMessage = __('The payment session is invalid or no payment information was found. Please contact our support if you have any questions.'); - return $this->redirectBackToCart($order, $errorMessage); - } - - if (!$charge instanceof \Omise\Payment\Model\Api\BaseObject) { - throw new LocalizedException( - __('Couldn\'t retrieve charge transaction. Please contact administrator.') - ); - } - if ($charge instanceof \Omise\Payment\Model\Api\Error) { - // restoring the cart - $this->checkoutSession->restoreQuote(); - throw new LocalizedException(__($charge->getMessage())); - } - if ($charge->isFailed()) { - $this->handleFailure($charge); - return; - } - - // Do not proceed if webhook is enabled - if ($this->config->isWebhookEnabled()) { - return $this->redirect(self::PATH_SUCCESS); - } - - $payment->setTransactionId($charge->id); - $payment->setLastTransId($charge->id); - $payment->setAdditionalInformation('charge_id', $charge->id); - - if ($charge->isSuccessful()) { - return $this->handleSuccess($order, $charge, $payment); - } - - return $this->handlePending($order, $payment); - } catch (Exception $e) { - $this->cancel($order, $e->getMessage()); - return $this->redirect(self::PATH_CART); - } - } - - /** - * Mark order as failed - * - * @param object $charge - */ - private function handleFailure($charge) - { - // restoring the cart - $this->checkoutSession->restoreQuote(); - $failureMessage = $charge->failure_message ? - ucfirst($charge->failure_message) : - __('Payment cancelled'); - $errorMessage = __( - 'Payment failed. %1, please contact our support if you have any questions.', - $failureMessage - ); - - // This cancels the order, logs error and displays message in cart page - throw new \Magento\Framework\Exception\LocalizedException($errorMessage); - } - - /** - * Mark order as success - * - * @param object $order - * @param object $charge - * @param object $payment - */ - private function handleSuccess($order, $charge, $payment) - { - // Update order state and status. - $order->setState(Order::STATE_PROCESSING); - $order->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING)); - - $invoice = $this->helper->createInvoiceAndMarkAsPaid($order, $charge->id, $charge->capture); - $this->emailHelper->sendInvoiceAndConfirmationEmails($order); - - if ($charge->capture) { - // Add transaction. - $payment->addTransactionCommentsToOrder( - $payment->addTransaction(Transaction::TYPE_PAYMENT, $invoice), - __( - $comment = __('Amount of %1 has been paid via Omise Gateway.'), - $order->getBaseCurrency()->formatTxt($invoice->getBaseGrandTotal()) - ) - ); - } - $order->save(); - return $this->redirect(self::PATH_SUCCESS); - } - - /** - * Mark order as pending - * - * @param object $order - * @param object $payment - */ - private function handlePending($order, $payment) - { - // Update order state and status. - $order->setState(Order::STATE_PAYMENT_REVIEW); - $order->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PAYMENT_REVIEW)); - - // Add transaction. - $transaction = $payment->addTransaction(Transaction::TYPE_PAYMENT); - $transaction->setIsClosed(false); - $payment->addTransactionCommentsToOrder( - $transaction, - __('The payment is under processing.
Due to bank processing, this might take up to an hour to - complete. The payment status will be updated once the processing result is available (you can - check the latest status on the Omise Dashboard).') - ); - - $order->save(); - - // TODO: Should redirect users to a page that tell users that - // their payment is in review instead of success page. return $this->redirect(self::PATH_SUCCESS); } @@ -299,16 +99,6 @@ private function isValid($order) return true; } - /** - * @param \Magento\Sales\Model\Order $order - * - * @return \Magento\Sales\Api\Data\InvoiceInterface - */ - protected function invoice(Order $order) - { - return $order->getInvoiceCollection()->getLastItem(); - } - /** * @param string $path * @@ -330,74 +120,4 @@ protected function invalid(Order $order, $message) $this->messageManager->addErrorMessage($message); } - - /** - * @param \Magento\Sales\Model\Order $order - * @param \Magento\Framework\Phrase|string $message - */ - protected function cancel(Order $order, $message) - { - if ($order->hasInvoices()) { - $invoice = $this->invoice($order); - $invoice->cancel(); - $order->addRelatedObject($invoice); - } - - $order->registerCancellation($message)->save(); - $this->messageManager->addErrorMessage($message); - } - - private function redirectBackToCart($order, $errorMessage) - { - $this->invalid($order, $errorMessage); - $this->checkoutSession->restoreQuote(); - return $this->redirect(self::PATH_CART); - } - - /** - * @param Magento\Sales\Model\Order\Payment $payment - * @return \Omise\Payment\Model\Api\CheckoutSession|null - */ - private function getCheckoutSession($payment) - { - $sessionId = $payment->getAdditionalInformation('session_id'); - if (empty($sessionId)) { - $this->checkoutSession->restoreQuote(); - throw new LocalizedException( - __('Cannot retrieve a session reference id. Please contact our support to confirm your payment.') - ); - } - - $checkoutSession = $this->omiseCheckoutSession->getSessionInfo($sessionId); - return $checkoutSession; - } - - /** - * @param array $payments - * @param string $sessionStatus - * @return array|null - */ - private function getFinalPayment($payments, $sessionStatus) - { - if (!is_array($payments) || empty($payments)) { - return null; - } - - $paymentsWithChargeId = array_filter($payments, function ($payment) { - return !empty($payment['charge_id']); - }); - - if (empty($paymentsWithChargeId)) { - return null; - } - - foreach ($paymentsWithChargeId as $payment) { - $paymentStatus = strtolower(isset($payment['status']) ? (string) $payment['status'] : ''); - if ($paymentStatus === strtolower((string) $sessionStatus)) { - return $payment; - } - } - - return end($paymentsWithChargeId); - } } diff --git a/Gateway/Request/UPAPaymentDataBuilder.php b/Gateway/Request/UPAPaymentDataBuilder.php index af9a909e..425fe3dc 100644 --- a/Gateway/Request/UPAPaymentDataBuilder.php +++ b/Gateway/Request/UPAPaymentDataBuilder.php @@ -12,6 +12,11 @@ class UPAPaymentDataBuilder implements BuilderInterface { + /** + * URL for omise upa dynamic webhook. + */ + const WEBHOOK_URI = 'omise/callback/webhook'; + /** * @var Resolver */ @@ -74,6 +79,8 @@ public function build(array $buildSubject) $upaThemeColor = $this->omiseHelper->getConfig('upa_theme_color', $order->getStoreId()); $upaTextColor = $this->omiseHelper->getConfig('upa_text_color', $order->getStoreId()); + $dynamicWebhookFlag = $this->omiseHelper->getConfig('dynamic_webhooks', $order->getStoreId()); + $webhookStatus = $this->omiseHelper->getConfig('webhook_status', $order->getStoreId()); $locale = $this->localeResolver->getLocale(); @@ -111,6 +118,9 @@ public function build(array $buildSubject) "is_upa" => true ]; + $webhookUrl = $store->getBaseUrl() . self::WEBHOOK_URI; + $payload['webhooks'] = [$webhookUrl]; + $locale = substr(strtolower($locale), 0, 2); if (!empty($locale)) { $payload['locale'] = $locale; diff --git a/Helper/OmiseHelper.php b/Helper/OmiseHelper.php index d47b12e1..56daccee 100644 --- a/Helper/OmiseHelper.php +++ b/Helper/OmiseHelper.php @@ -35,6 +35,7 @@ use Omise\Payment\Model\Config\Conveniencestore; use Omise\Payment\Model\Config\WeChatPay; use Magento\Framework\App\Config\ScopeConfigInterface; +use Omise\Payment\Model\Api\CheckoutSession as OmiseCheckoutSession; class OmiseHelper extends AbstractHelper { @@ -219,18 +220,26 @@ class OmiseHelper extends AbstractHelper * @var ScopeConfigInterface */ protected $scopeConfig; + + /** + * @var OmiseCheckoutSession + */ + protected $omiseCheckoutSession; /** * @param Header $header * @param Config $config * @param ScopeConfigInterface $scopeConfig + * @param OmiseCheckoutSession $omiseCheckoutSession */ public function __construct( Config $config, - ScopeConfigInterface $scopeConfig + ScopeConfigInterface $scopeConfig, + OmiseCheckoutSession $omiseCheckoutSession ) { $this->config = $config; $this->scopeConfig = $scopeConfig; + $this->omiseCheckoutSession = $omiseCheckoutSession; $this->omisePaymentMethods = array_merge( $this->offsitePaymentMethods, $this->offlinePaymentMethods, @@ -272,7 +281,7 @@ public function isAllowUpa($methodCode) */ public function checkoutSessionEndpoint() { - return "https://checkout-page.omise.co/"; + return $this->config->checkoutSessionEndpoint(); } /** @@ -400,7 +409,7 @@ public function isOrderOmisePayment($order) $payment = $order->getPayment(); $method = $payment->getMethodInstance(); $methodCode = $method->getCode(); - return strpos($methodCode, "omise") > -1; + return strpos($methodCode, 'omise') !== false; } /** @@ -420,7 +429,26 @@ public function canOrderStatusAutoSync($order) public function getOrderChargeId($order) { if ($this->isOrderOmisePayment($order)) { - return $order->getPayment()->getAdditionalInformation('charge_id'); + if($order->getPayment()->getAdditionalInformation('charge_id')) { + return $order->getPayment()->getAdditionalInformation('charge_id'); + } + if ($order->getPayment()->getAdditionalInformation('session_id')){ + $payment = $order->getPayment(); + $sessionId = $payment->getAdditionalInformation('session_id'); + $this->config->setStoreId($order->getStoreId()); + $checkoutSession = $this->omiseCheckoutSession->getSessionInfo($sessionId); + + if($checkoutSession) { + $sessionPayments = $checkoutSession->payments; + $sessionStatus = $checkoutSession->status; + if (is_array($sessionPayments) && !empty($sessionPayments)) { + $finalPayment = $this->getFinalPayment($sessionPayments, $sessionStatus); + if (!empty($finalPayment) && !empty($finalPayment['charge_id'])) { + return $finalPayment['charge_id']; + } + } + } + } } } @@ -522,4 +550,32 @@ public function hasShopeepayFailed($paymentMethod, $isChargeSuccess) { return $paymentMethod === 'omise_offsite_shopeepay' && !$isChargeSuccess; } + + /** + * @param array $payments + * @param string $sessionStatus + * @return array|null + */ + private function getFinalPayment($payments, $sessionStatus) + { + if (!is_array($payments) || empty($payments)) { + return null; + } + + $paymentsWithChargeId = array_filter($payments, function ($payment) { + return !empty($payment['charge_id']); + }); + + if (empty($paymentsWithChargeId)) { + return null; + } + + foreach ($paymentsWithChargeId as $payment) { + $paymentStatus = strtolower(isset($payment['status']) ? (string) $payment['status'] : ''); + if ($paymentStatus === strtolower((string) $sessionStatus)) { + return $payment; + } + } + return end($paymentsWithChargeId); + } } diff --git a/Model/Api/CheckoutSession.php b/Model/Api/CheckoutSession.php index 1d0e29ba..b3cd061a 100644 --- a/Model/Api/CheckoutSession.php +++ b/Model/Api/CheckoutSession.php @@ -5,7 +5,6 @@ use Exception; use OmiseApiResource; use Omise\Payment\Model\Config\Config; -use Omise\Payment\Helper\OmiseHelper; use Omise\Payment\Helper\RequestHelper; use Magento\Framework\Exception\LocalizedException; @@ -18,26 +17,18 @@ class CheckoutSession extends BaseObject */ private $requestHelper; - /** - * @var OmiseHelper - */ - private $omiseHelper; - /** * Injecting dependencies * * @param Config $config * @param RequestHelper $requestHelper - * @param OmiseHelper $omiseHelper */ public function __construct( Config $config, - RequestHelper $requestHelper, - OmiseHelper $omiseHelper + RequestHelper $requestHelper ) { $this->requestHelper = $requestHelper; $this->config = $config; - $this->omiseHelper = $omiseHelper; } /** @@ -49,7 +40,7 @@ public function __construct( public function createSession($params) { try { - $endpoint = $this->omiseHelper->checkoutSessionEndpoint(); + $endpoint = $this->config->checkoutSessionEndpoint(); $session = $this->requestHelper->sendUpaSessionRequest( $endpoint."api/sessions", OmiseApiResource::REQUEST_POST, @@ -73,7 +64,7 @@ public function createSession($params) public function getSessionInfo($sessionId) { try { - $endpoint = $this->omiseHelper->checkoutSessionEndpoint(); + $endpoint = $this->config->checkoutSessionEndpoint(); $session = $this->requestHelper->sendUpaSessionRequest( $endpoint."api/sessions/".$sessionId, OmiseApiResource::REQUEST_GET, diff --git a/Model/Config/Config.php b/Model/Config/Config.php index fdc9e279..1c19e4eb 100644 --- a/Model/Config/Config.php +++ b/Model/Config/Config.php @@ -64,6 +64,14 @@ public function canInitialize() return $this->canInitialize; } + /** + * @return string + */ + public function checkoutSessionEndpoint() + { + return 'https://checkout-page.omise.co/'; + } + /** * Change the store ID from the default store to fetch store specific values * diff --git a/Model/SyncStatus.php b/Model/SyncStatus.php index d4504672..0038be43 100644 --- a/Model/SyncStatus.php +++ b/Model/SyncStatus.php @@ -123,6 +123,11 @@ private function markPaymentSuccessful($order, $charge) $this->helper->createInvoiceAndMarkAsPaid($order, $charge['id']); $this->emailHelper->sendInvoiceAndConfirmationEmails($order); + $payment = $order->getPayment(); + $payment->setTransactionId($charge['id']); + $payment->setLastTransId($charge['id']); + $payment->setAdditionalInformation('charge_id', $charge['id']); + $order->addStatusHistoryComment( __( 'Omise: Payment successful.
An amount %1 %2 has been paid (manual sync).', diff --git a/Observer/WebhookObserver/WebhookCompleteObserver.php b/Observer/WebhookObserver/WebhookCompleteObserver.php index 256b7554..d04014c2 100644 --- a/Observer/WebhookObserver/WebhookCompleteObserver.php +++ b/Observer/WebhookObserver/WebhookCompleteObserver.php @@ -102,6 +102,13 @@ private function processOrder($isCaptured = true) $isCaptured ); + // Set the charge transaction IDs for UPA payments created through a checkout session. + if ($this->payment->getAdditionalInformation('session_id')) { + $this->payment->setTransactionId($this->charge->id); + $this->payment->setLastTransId($this->charge->id); + $this->payment->setAdditionalInformation('charge_id', $this->charge->id); + } + $this->emailHelper->sendInvoiceAndConfirmationEmails($this->orderData); // addTransactionCommentsToOrder with message for authorise or capture diff --git a/Plugin/ConfigSectionPaymentPlugin.php b/Plugin/ConfigSectionPaymentPlugin.php index 0282955a..10499036 100644 --- a/Plugin/ConfigSectionPaymentPlugin.php +++ b/Plugin/ConfigSectionPaymentPlugin.php @@ -84,6 +84,14 @@ public function beforeSave(CoreConfig $coreConfig) //on the admin setting page the setting value will inherit from website scope $this->parentScopeType = $this->retrieveParentScope($coreConfig); $omiseConfigData = $coreConfig->toArray()['groups']['omise']; + if (!empty($omiseConfigData['fields']['is_upa_feature_flag_enabled']['value'])) { + $groups = $coreConfig->getGroups(); + $groups['omise']['fields']['webhook_status'] = ['value' => 1]; + $groups['omise']['fields']['dynamic_webhooks'] = ['value' => 1]; + $coreConfig->setData('groups', $groups); + $omiseConfigData['fields']['webhook_status'] = ['value' => 1]; + $omiseConfigData['fields']['dynamic_webhooks'] = ['value' => 1]; + } $keys = $this->getKeys($omiseConfigData); // if both keys are empty then we ignore the check. diff --git a/Test/Unit/Controller/Callback/UPACallbackTest.php b/Test/Unit/Controller/Callback/UPACallbackTest.php index 239d2a5f..0ad9ad82 100644 --- a/Test/Unit/Controller/Callback/UPACallbackTest.php +++ b/Test/Unit/Controller/Callback/UPACallbackTest.php @@ -4,18 +4,11 @@ use Magento\Checkout\Model\Session; use Magento\Framework\App\Action\Context; -use Magento\Framework\App\Request\Http; use Magento\Framework\Controller\Result\Redirect; use Magento\Framework\Message\ManagerInterface; use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Payment; -use Magento\Sales\Model\Order\Payment\Transaction; use Omise\Payment\Controller\Callback\UPACallback; -use Omise\Payment\Helper\OmiseEmailHelper; -use Omise\Payment\Helper\OmiseHelper; -use Omise\Payment\Model\Api\Charge; -use Omise\Payment\Model\Api\CheckoutSession; -use Omise\Payment\Model\Config\Cc; use Omise\Payment\Model\Omise; use PHPUnit\Framework\TestCase; @@ -27,1050 +20,126 @@ class UPACallbackTest extends TestCase private $context; private $session; private $omise; - private $charge; - private $helper; - private $emailHelper; - private $config; - private $checkoutSession; - private $request; - private $omiseCheckoutSession; private $messageManager; - private const ORDER_ID = 1; - private const SESSION_ID = 'session_123'; - private const CHARGE_ID = 'chrg_test_123'; protected function setUp(): void { $this->context = $this->createMock(Context::class); - $this->session = $this->createMock(Session::class); $this->omise = $this->createMock(Omise::class); - $this->charge = $this->createMock(Charge::class); - $this->helper = $this->createMock(OmiseHelper::class); - $this->emailHelper = $this->createMock(OmiseEmailHelper::class); - $this->config = $this->createMock(Cc::class); - $this->request = $this->createMock(Http::class); - - $this->checkoutSession = $this->createMock( - \Magento\Checkout\Model\Session::class - ); - - $this->omiseCheckoutSession = $this->createMock( - \Omise\Payment\Model\Api\CheckoutSession::class - ); - $this->messageManager = $this->createMock(ManagerInterface::class); $this->context->method('getMessageManager') ->willReturn($this->messageManager); - - $this->omise->method('defineUserAgent'); - $this->omise->method('defineApiVersion'); - $this->omise->method('defineApiKeys'); } private function getController() { - $controller = $this->getMockBuilder(UPACallback::class) - ->setConstructorArgs( - [ - $this->context, - $this->session, - $this->omise, - $this->charge, - $this->helper, - $this->emailHelper, - $this->config, - $this->checkoutSession, - $this->request, - $this->omiseCheckoutSession - ] - ) - ->onlyMethods(['_redirect', 'getRequest']) - ->getMock(); - - $controller->method('getRequest') - ->willReturn($this->request); - - return $controller; - } - - /** - * @param Payment|null $payment - * @param int|null $id - * @param string $state - * @return Order - */ - private function createOrder( - ?Payment $payment = null, - ?int $id = null, - bool $allowstatus = false, - string $state = Order::STATE_PENDING_PAYMENT - ): Order { - if ($allowstatus) { - return $this->createConfiguredMock(Order::class, [ - 'getId' => $id, - 'getPayment' => $payment, - 'getState' => $state, - ]); - } - return $this->createConfiguredMock(Order::class, [ - 'getId' => $id, - 'getPayment' => $payment - ]); - } - - /** - * @param string $sessionId - * @param string $method - * @return Payment - */ - private function createPayment( - string $sessionId = '', - bool $allowMethod = false, - string $method = 'omise_promptpay' - ): Payment { - if ($allowMethod) { - return $this->createConfiguredMock(Payment::class, [ - 'getAdditionalInformation' => $sessionId, - 'getMethod' => $method - ]); - } - return $this->createConfiguredMock(Payment::class, [ - 'getAdditionalInformation' => $sessionId, - ]); - } - - /** - * @param array $payments - * @return \stdClass - */ - private function createSessionInfo( - array $payments, - string $status = '' - ): \stdClass { - $sessionInfo = new \stdClass(); - $sessionInfo->payments = $payments; - $sessionInfo->status = $status; - - return $sessionInfo; - } - - /** - * Invoke the private payment selection method. - * - * @param array $payments - * @param string $sessionStatus - * @return array|null - */ - private function getFinalPayment(array $payments, string $sessionStatus = '') - { - $controller = $this->getController(); - $method = new \ReflectionMethod(UPACallback::class, 'getFinalPayment'); - $method->setAccessible(true); - - return $method->invoke($controller, $payments, $sessionStatus); - } - - /** - * @covers \Omise\Payment\Controller\Callback\UPACallback::getFinalPayment - * @uses \Omise\Payment\Controller\Callback\UPACallback::__construct - */ - public function testGetFinalPaymentReturnsNullWhenAllPaymentsLackChargeId() - { - $payments = [ - ['status' => 'pending'], - ['status' => 'failed'], - ]; - - $this->assertNull( - $this->getFinalPayment($payments, 'failed') - ); - } - - /** - * @covers \Omise\Payment\Controller\Callback\UPACallback::getFinalPayment - * @uses \Omise\Payment\Controller\Callback\UPACallback::__construct - */ - public function testGetFinalPaymentReturnsPaymentMatchingSessionStatus() - { - $payments = [ - ['status' => 'pending', 'charge_id' => 'chrg_pending'], - ['status' => 'SUCCESSFUL', 'charge_id' => self::CHARGE_ID], - ['status' => 'successful'], - ]; - - $this->assertSame( - $payments[1], - $this->getFinalPayment($payments, 'successful') - ); - } - - /** - * @covers \Omise\Payment\Controller\Callback\UPACallback::getFinalPayment - * @uses \Omise\Payment\Controller\Callback\UPACallback::__construct - */ - public function testGetFinalPaymentReturnsLastPaymentWithChargeIdAsFallback() - { - $payments = [ - ['status' => 'pending', 'charge_id' => 'chrg_pending'], - ['status' => 'failed'], - ['status' => 'processing', 'charge_id' => self::CHARGE_ID], - ]; - - $this->assertSame( - $payments[2], - $this->getFinalPayment($payments, 'successful') - ); - } - - /** - * @covers \Omise\Payment\Controller\Callback\UPACallback - */ - public function testExecuteWithSuccessfulCharge() - { - $payment = $this->createPayment(self::SESSION_ID, true, 'omise_promptpay'); - $order = $this->createOrder($payment, self::ORDER_ID, true, Order::STATE_PENDING_PAYMENT); - - $invoice = $this->getMockBuilder( - \Magento\Sales\Model\Order\Invoice::class - ) - ->disableOriginalConstructor() - ->getMock(); - - $transaction = $this->createMock(Transaction::class); - - $orderConfig = $this->createMock( - \Magento\Sales\Model\Order\Config::class - ); - - $currency = $this->createMock( - \Magento\Directory\Model\Currency::class - ); - - $this->session->method('getLastRealOrder') - ->willReturn($order); - - $this->request->method('getParam') - ->with('chargeId') - ->willReturn(self::CHARGE_ID); - - $sessionInfo = $this->createSessionInfo([ - [ - 'charge_id' => self::CHARGE_ID, - 'status' => 'successful' - ] - ], 'successful'); - - $this->omiseCheckoutSession - ->expects($this->once()) - ->method('getSessionInfo') - ->with(self::SESSION_ID) - ->willReturn($sessionInfo); - - $charge = $this->getMockBuilder( - \Omise\Payment\Model\Api\BaseObject::class - ) - ->disableOriginalConstructor() - ->addMethods(['isFailed', 'isSuccessful']) - ->getMock(); - - $charge->id = self::CHARGE_ID; - - $charge->method('isFailed') - ->willReturn(false); - - $charge->method('isSuccessful') - ->willReturn(true); - - $charge->capture = true; - - $this->charge->expects($this->once()) - ->method('find') - ->with(self::CHARGE_ID) - ->willReturn($charge); - - $payment->expects($this->once()) - ->method('setTransactionId') - ->with(self::CHARGE_ID) - ->willReturnSelf(); - - $payment->expects($this->once()) - ->method('setLastTransId') - ->with(self::CHARGE_ID) - ->willReturnSelf(); - - $this->config->method('isWebhookEnabled') - ->willReturn(false); - - $order->expects($this->once()) - ->method('setState') - ->with(Order::STATE_PROCESSING) - ->willReturnSelf(); - - $order->method('getConfig') - ->willReturn($orderConfig); - - $orderConfig->method('getStateDefaultStatus') - ->with(Order::STATE_PROCESSING) - ->willReturn('processing'); - - $order->expects($this->once()) - ->method('setStatus') - ->with('processing') - ->willReturnSelf(); - - $this->helper->expects($this->once()) - ->method('createInvoiceAndMarkAsPaid') - ->with($order, self::CHARGE_ID) - ->willReturn($invoice); - - $this->emailHelper->expects($this->once()) - ->method('sendInvoiceAndConfirmationEmails') - ->with($order); - - $invoice->method('getBaseGrandTotal') - ->willReturn(100); - - $order->method('getBaseCurrency') - ->willReturn($currency); - - $currency->method('formatTxt') - ->with(100) - ->willReturn('100.00'); - - $payment->expects($this->once()) - ->method('addTransaction') - ->with(Transaction::TYPE_PAYMENT, $invoice) - ->willReturn($transaction); - - $expectedMessage = __('Amount of %1 has been paid via Omise Gateway.', '100.00'); - $payment->expects($this->once()) - ->method('addTransactionCommentsToOrder') - ->with( - $transaction, - $expectedMessage - ) - ->willReturnSelf(); - - $order->expects($this->once()) - ->method('save') - ->willReturnSelf(); - - $controller = $this->getController(); - - $redirectResult = $this->createMock(Redirect::class); - - $controller->expects($this->once()) - ->method('_redirect') - ->with( - 'checkout/onepage/success', - ['_secure' => true] - ) - ->willReturn($redirectResult); - - $result = $controller->execute(); - $this->assertSame($redirectResult, $result); - } - - /** - * @covers \Omise\Payment\Controller\Callback\UPACallback - */ - public function testExecuteWithSuccessfulAuthorizedCharge() - { - $payment = $this->createPayment( - self::SESSION_ID, - true, - 'omise_creditcard' - ); - - $order = $this->createOrder( - $payment, - self::ORDER_ID, - true, - Order::STATE_PENDING_PAYMENT - ); - - $orderConfig = $this->createMock( - \Magento\Sales\Model\Order\Config::class - ); - - $this->session->method('getLastRealOrder') - ->willReturn($order); - - $this->request->method('getParam') - ->with('chargeId') - ->willReturn(self::CHARGE_ID); - - $sessionInfo = $this->createSessionInfo([ - [ - 'charge_id' => self::CHARGE_ID, - 'status' => 'successful' - ] - ], 'successful'); - - $this->omiseCheckoutSession - ->expects($this->once()) - ->method('getSessionInfo') - ->with(self::SESSION_ID) - ->willReturn($sessionInfo); - - $charge = $this->getMockBuilder( - \Omise\Payment\Model\Api\BaseObject::class - ) - ->disableOriginalConstructor() - ->addMethods(['isFailed', 'isSuccessful']) + return $this->getMockBuilder(UPACallback::class) + ->setConstructorArgs([$this->context, $this->session, $this->omise]) + ->onlyMethods(['_redirect']) ->getMock(); - - $charge->id = self::CHARGE_ID; - $charge->capture = false; - - $charge->method('isFailed') - ->willReturn(false); - - $charge->method('isSuccessful') - ->willReturn(true); - - $this->charge->method('find') - ->willReturn($charge); - - $payment->expects($this->once()) - ->method('setTransactionId') - ->with(self::CHARGE_ID) - ->willReturnSelf(); - - $payment->expects($this->once()) - ->method('setLastTransId') - ->with(self::CHARGE_ID) - ->willReturnSelf(); - - $this->config->method('isWebhookEnabled') - ->willReturn(false); - - $order->expects($this->once()) - ->method('setState') - ->with(Order::STATE_PROCESSING) - ->willReturnSelf(); - - $order->method('getConfig') - ->willReturn($orderConfig); - - $orderConfig->method('getStateDefaultStatus') - ->with(Order::STATE_PROCESSING) - ->willReturn('processing'); - - $order->expects($this->once()) - ->method('setStatus') - ->with('processing') - ->willReturnSelf(); - - $this->helper->expects($this->once()) - ->method('createInvoiceAndMarkAsPaid') - ->with($order, self::CHARGE_ID, false); - - $this->emailHelper->expects($this->once()) - ->method('sendInvoiceAndConfirmationEmails') - ->with($order); - - // No transaction/message is created for authorized charge. - $payment->expects($this->never()) - ->method('prependMessage'); - - $payment->expects($this->never()) - ->method('addTransaction'); - - $payment->expects($this->never()) - ->method('addTransactionCommentsToOrder'); - - $order->expects($this->once()) - ->method('save') - ->willReturnSelf(); - - $controller = $this->getController(); - - $redirectResult = $this->createMock(Redirect::class); - - $controller->expects($this->once()) - ->method('_redirect') - ->with( - 'checkout/onepage/success', - ['_secure' => true] - ) - ->willReturn($redirectResult); - - $this->assertSame( - $redirectResult, - $controller->execute() - ); } /** - * @covers \Omise\Payment\Controller\Callback\UPACallback + * @covers ::execute + * @uses ::__construct + * @uses ::isValid + * @uses ::redirect */ - public function testExecuteWithPendingCharge() + public function testExecuteRedirectsValidPendingOrderToSuccess(): void { - $payment = $this->createPayment(self::SESSION_ID); - $order = $this->createOrder($payment, self::ORDER_ID, true, Order::STATE_PENDING_PAYMENT); - $transaction = $this->createMock(Transaction::class); - - $orderConfig = $this->createMock(\Magento\Sales\Model\Order\Config::class); - - $this->session->method('getLastRealOrder') - ->willReturn($order); - - $this->request->method('getParam') - ->with('chargeId') - ->willReturn(self::CHARGE_ID); - - $sessionInfo = $this->createSessionInfo([ - ['charge_id' => self::CHARGE_ID] + $payment = $this->createMock(Payment::class); + $order = $this->createConfiguredMock(Order::class, [ + 'getId' => 10, + 'getPayment' => $payment, + 'getState' => Order::STATE_PENDING_PAYMENT, ]); - - $this->omiseCheckoutSession - ->expects($this->once()) - ->method('getSessionInfo') - ->with(self::SESSION_ID) - ->willReturn($sessionInfo); - - $charge = $this->getMockBuilder( - \Omise\Payment\Model\Api\BaseObject::class - ) - ->disableOriginalConstructor() - ->addMethods(['isFailed', 'isSuccessful']) - ->getMock(); - - $charge->id = self::CHARGE_ID; - - $charge->method('isFailed') - ->willReturn(false); - - $charge->method('isSuccessful') - ->willReturn(false); - - $this->charge->expects($this->once()) - ->method('find') - ->with(self::CHARGE_ID) - ->willReturn($charge); - - $payment->expects($this->once()) - ->method('setTransactionId') - ->with(self::CHARGE_ID) - ->willReturnSelf(); - - $payment->expects($this->once()) - ->method('setLastTransId') - ->with(self::CHARGE_ID) - ->willReturnSelf(); - - $this->config->method('isWebhookEnabled') - ->willReturn(false); - - // handlePending assertions - $order->expects($this->once()) - ->method('setState') - ->with(Order::STATE_PAYMENT_REVIEW) - ->willReturnSelf(); - - $order->method('getConfig') - ->willReturn($orderConfig); - - $orderConfig->expects($this->once()) - ->method('getStateDefaultStatus') - ->with(Order::STATE_PAYMENT_REVIEW) - ->willReturn('payment_review'); - - $order->expects($this->once()) - ->method('setStatus') - ->with('payment_review') - ->willReturnSelf(); - - $payment->expects($this->once()) - ->method('addTransaction') - ->with(Transaction::TYPE_PAYMENT) - ->willReturn($transaction); - - $transaction->expects($this->once()) - ->method('setIsClosed') - ->with(false) - ->willReturnSelf(); - - $payment->expects($this->once()) - ->method('addTransactionCommentsToOrder') - ->with( - $transaction, - $this->anything() - ) - ->willReturnSelf(); - - $order->expects($this->once()) - ->method('save') - ->willReturnSelf(); - - $controller = $this->getController(); - - $redirectResult = $this->createMock(Redirect::class); - - $controller->expects($this->once()) - ->method('_redirect') - ->with( - 'checkout/onepage/success', - ['_secure' => true] - ) - ->willReturn($redirectResult); - - $result = $controller->execute(); - $this->assertSame($redirectResult, $result); - } - - /** - * @covers \Omise\Payment\Controller\Callback\UPACallback - */ - public function testExecuteWithChargeFailure() - { - $payment = $this->createPayment(self::SESSION_ID); - $order = $this->createOrder($payment, self::ORDER_ID, true, Order::STATE_PENDING_PAYMENT); - - $this->session->method('getLastRealOrder') - ->willReturn($order); - - $this->request->method('getParam') - ->with('chargeId') - ->willReturn(self::CHARGE_ID); - - $sessionInfo = $this->createSessionInfo([ - [ - 'charge_id' => self::CHARGE_ID, - 'status' => 'pending' - ] - ], 'pending'); - - $this->omiseCheckoutSession - ->expects($this->once()) - ->method('getSessionInfo') - ->with(self::SESSION_ID) - ->willReturn($sessionInfo); - - $charge = $this->getMockBuilder( - \Omise\Payment\Model\Api\BaseObject::class - ) - ->disableOriginalConstructor() - ->addMethods(['isFailed', 'isSuccessful']) - ->getMock(); - - $charge->id = self::CHARGE_ID; - $charge->failure_message = 'bank rejected'; - - $charge->method('isFailed') - ->willReturn(true); - - $this->charge->expects($this->once()) - ->method('find') - ->with(self::CHARGE_ID) - ->willReturn($charge); - - $this->checkoutSession->expects($this->once()) - ->method('restoreQuote'); - - $order->expects($this->once()) - ->method('hasInvoices') - ->willReturn(false); - - $order->expects($this->once()) - ->method('registerCancellation') - ->willReturnSelf(); - - $order->expects($this->once()) - ->method('save') - ->willReturnSelf(); - - $this->messageManager->expects($this->once()) - ->method('addErrorMessage'); + $this->session->method('getLastRealOrder')->willReturn($order); + $redirect = $this->createMock(Redirect::class); $controller = $this->getController(); - - $controller->expects($this->once()) - ->method('_redirect') - ->with('checkout/cart', ['_secure' => true]); - - $controller->execute(); - } - - /** - * @covers \Omise\Payment\Controller\Callback\UPACallback - */ - public function testExecuteWithWebhookEnabledBuildsTransactionAndRedirectsToSuccess() - { - $payment = $this->createPayment(self::SESSION_ID); - $order = $this->createOrder($payment, self::ORDER_ID, true, Order::STATE_PENDING_PAYMENT); - - $this->session->method('getLastRealOrder') - ->willReturn($order); - - $this->request->method('getParam') - ->with('chargeId') - ->willReturn(self::CHARGE_ID); - - $sessionInfo = $this->createSessionInfo([ - [ - 'charge_id' => self::CHARGE_ID, - 'status' => 'failed' - ] - ], 'failed'); - - $this->omiseCheckoutSession - ->expects($this->once()) - ->method('getSessionInfo') - ->with(self::SESSION_ID) - ->willReturn($sessionInfo); - - $charge = $this->getMockBuilder( - \Omise\Payment\Model\Api\BaseObject::class - ) - ->disableOriginalConstructor() - ->addMethods(['isFailed', 'isSuccessful']) - ->getMock(); - - $charge->id = self::CHARGE_ID; - $charge->status = 'pending'; - - $charge->method('isFailed') - ->willReturn(false); - - $charge->method('isSuccessful') - ->willReturn(false); - - $this->charge->method('find') - ->with(self::CHARGE_ID) - ->willReturn($charge); - - $this->config->method('isWebhookEnabled') - ->willReturn(true); - - $controller = $this->getController(); - - $redirectResult = $this->createMock(Redirect::class); - $controller->expects($this->once()) ->method('_redirect') ->with('checkout/onepage/success', ['_secure' => true]) - ->willReturn($redirectResult); - - $result = $controller->execute(); - - $this->assertInstanceOf(Redirect::class, $result); - $this->assertSame($redirectResult, $result); - } - - /** - * @covers \Omise\Payment\Controller\Callback\UPACallback - */ - public function testExecuteProcessingOrderRedirectsToSuccess() - { - $payment = $this->createPayment(self::SESSION_ID); - $order = $this->createOrder($payment, self::ORDER_ID); - - $this->session->method('getLastRealOrder') - ->willReturn($order); - - $this->request->method('getParam') - ->willReturn(self::CHARGE_ID); - - $order->method('getState') - ->willReturn(Order::STATE_PROCESSING); - - $controller = $this->getController(); - - $controller->expects($this->once()) - ->method('_redirect') - ->with( - 'checkout/onepage/success', - ['_secure' => true] - ) - ->willReturn($this->createMock(Redirect::class)); - - $controller->execute(); - } - - /** - * @covers \Omise\Payment\Controller\Callback\UPACallback - */ - public function testExecuteWithMissingPayment() - { - $order = $this->createOrder(null, self::ORDER_ID); - - $this->session->method('getLastRealOrder') - ->willReturn($order); - - $controller = $this->getController(); - - $controller->expects($this->once()) - ->method('_redirect') - ->with('checkout/cart', ['_secure' => true]); - - $controller->execute(); - } - - /** - * @covers \Omise\Payment\Controller\Callback\UPACallback - */ - public function testExecuteWithInvalidOrderState() - { - $payment = $this->createPayment(self::SESSION_ID); - $order = $this->createOrder($payment, self::ORDER_ID, true, Order::STATE_COMPLETE); - - $this->session->method('getLastRealOrder') - ->willReturn($order); - - $this->request->method('getParam') - ->willReturn(self::CHARGE_ID); - - $controller = $this->getController(); - - $controller->expects($this->once()) - ->method('_redirect'); + ->willReturn($redirect); - $controller->execute(); + $this->assertSame($redirect, $controller->execute()); } /** - * @covers \Omise\Payment\Controller\Callback\UPACallback + * @covers ::execute + * @uses ::__construct + * @uses ::isValid + * @uses ::redirect */ - public function testExecuteWithoutSessionId() + public function testExecuteRedirectsProcessingOrderToSuccess(): void { - $payment = $this->createPayment(); - $order = $this->createOrder($payment, self::ORDER_ID, true, Order::STATE_PENDING_PAYMENT); - - $this->session->method('getLastRealOrder') - ->willReturn($order); - - $this->checkoutSession->expects($this->once()) - ->method('restoreQuote'); - - $order->expects($this->once()) - ->method('hasInvoices') - ->willReturn(false); - - $order->expects($this->once()) - ->method('registerCancellation') - ->willReturnSelf(); - - $order->expects($this->once()) - ->method('save') - ->willReturnSelf(); - - $this->messageManager->expects($this->once()) - ->method('addErrorMessage'); + $payment = $this->createMock(Payment::class); + $order = $this->createConfiguredMock(Order::class, [ + 'getId' => 10, + 'getPayment' => $payment, + 'getState' => Order::STATE_PROCESSING, + ]); + $this->session->method('getLastRealOrder')->willReturn($order); + $redirect = $this->createMock(Redirect::class); $controller = $this->getController(); - $controller->expects($this->once()) ->method('_redirect') - ->with('checkout/cart', ['_secure' => true]); - $this->assertSame('', $payment->getAdditionalInformation('session_id')); - $controller->execute(); - } - - /** - * @covers \Omise\Payment\Controller\Callback\UPACallback - */ - public function testExecuteWithInvalidPaymentsArray() - { - $payment = $this->createPayment(self::SESSION_ID); - $order = $this->createOrder($payment, self::ORDER_ID, true, Order::STATE_PENDING_PAYMENT); - - $this->session->method('getLastRealOrder') - ->willReturn($order); - - $sessionInfo = new \stdClass(); - $sessionInfo->payments = null; - $sessionInfo->status = ''; - - $this->omiseCheckoutSession - ->expects($this->once()) - ->method('getSessionInfo') - ->with(self::SESSION_ID) - ->willReturn($sessionInfo); - - $controller = $this->getController(); - - $controller->expects($this->once()) - ->method('_redirect'); - - $controller->execute(); - } - - /** - * @covers \Omise\Payment\Controller\Callback\UPACallback - */ - public function testExecuteWithInvalidPaymentsData() - { - $payment = $this->createPayment(self::SESSION_ID); - - $order = $this->createOrder($payment, self::ORDER_ID, true, Order::STATE_PENDING_PAYMENT); - - $this->session->method('getLastRealOrder') - ->willReturn($order); - - $sessionInfo = $this->createSessionInfo([ - [ - 'status' => 'pending' - ] - ], 'pending'); - - $this->omiseCheckoutSession - ->expects($this->once()) - ->method('getSessionInfo') - ->with(self::SESSION_ID) - ->willReturn($sessionInfo); - - $controller = $this->getController(); - - $controller->expects($this->once()) - ->method('_redirect'); + ->with('checkout/onepage/success', ['_secure' => true]) + ->willReturn($redirect); - $controller->execute(); + $this->assertSame($redirect, $controller->execute()); } - - /** - * @covers \Omise\Payment\Controller\Callback\UPACallback - */ - public function testExecuteInvalidOrderRedirectsToCart() - { - $order = $this->createOrder(); - - $this->session->method('getLastRealOrder') - ->willReturn($order); - - $this->request->method('getParam') - ->with('chargeId') - ->willReturn(self::CHARGE_ID); - - $controller = $this->getController(); - $controller->expects($this->once()) - ->method('_redirect') - ->with('checkout/cart', ['_secure' => true]) - ->willReturn($this->createMock(Redirect::class)); - - $controller->execute(); - } - /** - * @covers \Omise\Payment\Controller\Callback\UPACallback + * @covers ::execute + * @uses ::__construct + * @uses ::isValid + * @uses ::redirect */ - public function testExecuteChargeFindThrowsException() + public function testExecuteRedirectsToCartWhenOrderIsMissing(): void { - $payment = $this->createPayment(self::SESSION_ID); - $order = $this->createOrder($payment, self::ORDER_ID, true, Order::STATE_PENDING_PAYMENT); - - $this->session->method('getLastRealOrder') - ->willReturn($order); - - $this->request->method('getParam') - ->willReturn(self::CHARGE_ID); - - $sessionInfo = $this->createSessionInfo([ - [ - 'charge_id' => self::CHARGE_ID, - 'status' => 'successful' - ] - ], 'successful'); - - $this->omiseCheckoutSession - ->expects($this->once()) - ->method('getSessionInfo') - ->with(self::SESSION_ID) - ->willReturn($sessionInfo); - - $this->charge->expects($this->once()) - ->method('find') - ->willThrowException(new \Exception('API Error')); - - $order->expects($this->once()) - ->method('hasInvoices') - ->willReturn(false); - - $order->expects($this->once()) - ->method('registerCancellation') - ->with('API Error') - ->willReturnSelf(); - - $order->expects($this->once()) - ->method('save') - ->willReturnSelf(); - - $this->messageManager->expects($this->once()) - ->method('addErrorMessage'); + $order = $this->createConfiguredMock(Order::class, ['getId' => null]); + $this->session->method('getLastRealOrder')->willReturn($order); + $redirect = $this->createMock(Redirect::class); + $this->messageManager->expects($this->once())->method('addErrorMessage'); $controller = $this->getController(); - $controller->expects($this->once()) ->method('_redirect') ->with('checkout/cart', ['_secure' => true]) - ->willReturn($this->createMock(Redirect::class)); + ->willReturn($redirect); - $controller->execute(); + $this->assertSame($redirect, $controller->execute()); } /** - * @covers \Omise\Payment\Controller\Callback\UPACallback - * @uses \Omise\Payment\Model\Api\Error + * @covers ::execute + * @uses ::__construct + * @uses ::invalid + * @uses ::isValid + * @uses ::redirect */ - public function testExecuteChargeErrorRestoresQuoteAndRedirectsToCart() + public function testExecuteRedirectsToCartWhenPaymentIsMissing(): void { - $payment = $this->createPayment(self::SESSION_ID); - $order = $this->createOrder($payment, self::ORDER_ID, true, Order::STATE_PENDING_PAYMENT); - - $this->session->method('getLastRealOrder') - ->willReturn($order); - - $this->request->method('getParam') - ->with('chargeId') - ->willReturn(self::CHARGE_ID); - - $sessionInfo = $this->createSessionInfo([ - [ - 'charge_id' => self::CHARGE_ID, - 'status' => 'successful' - ] - ], 'successful'); - - $this->omiseCheckoutSession - ->expects($this->once()) - ->method('getSessionInfo') - ->with(self::SESSION_ID) - ->willReturn($sessionInfo); - - $this->checkoutSession->expects($this->once()) - ->method('restoreQuote'); - - $this->charge->method('find') - ->with(self::CHARGE_ID) - ->willReturn(new \Omise\Payment\Model\Api\Error(['message' => 'Failed message'])); - - $order->expects($this->once()) - ->method('hasInvoices') - ->willReturn(false); - - $order->expects($this->once()) - ->method('registerCancellation') - ->with('Failed message') - ->willReturnSelf(); - - $order->expects($this->once()) - ->method('save') - ->willReturnSelf(); - - $this->messageManager->expects($this->once()) - ->method('addErrorMessage'); + $order = $this->createConfiguredMock(Order::class, [ + 'getId' => 10, + 'getPayment' => null, + ]); + $this->session->method('getLastRealOrder')->willReturn($order); + $redirect = $this->createMock(Redirect::class); + $order->expects($this->once())->method('addStatusHistoryComment'); + $order->expects($this->once())->method('save'); + $this->messageManager->expects($this->once())->method('addErrorMessage'); $controller = $this->getController(); - $controller->expects($this->once()) ->method('_redirect') ->with('checkout/cart', ['_secure' => true]) - ->willReturn($this->createMock(Redirect::class)); + ->willReturn($redirect); - $controller->execute(); + $this->assertSame($redirect, $controller->execute()); } } diff --git a/Test/Unit/Gateway/Request/UPAPaymentDataBuilderTest.php b/Test/Unit/Gateway/Request/UPAPaymentDataBuilderTest.php index 4ff9b43d..d76ce512 100644 --- a/Test/Unit/Gateway/Request/UPAPaymentDataBuilderTest.php +++ b/Test/Unit/Gateway/Request/UPAPaymentDataBuilderTest.php @@ -5,7 +5,7 @@ use Magento\Framework\Locale\Resolver; use Magento\Framework\UrlInterface; use Magento\Payment\Gateway\Data\OrderAdapterInterface; -use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; use Omise\Payment\Gateway\Request\UPAPaymentDataBuilder; use Omise\Payment\Helper\OmiseHelper; @@ -47,7 +47,7 @@ public function testBuildReturnsPayload(): void { $paymentDO = $this->createMock(PaymentDataObject::class); $payment = $this->getMockBuilder(InfoMock::class)->getMock(); - $store = $this->createMock(StoreInterface::class); + $store = $this->createMock(Store::class); $order = $this->createConfiguredMock( OrderAdapterInterface::class, [ @@ -68,6 +68,8 @@ public function testBuildReturnsPayload(): void $store->method('getName') ->willReturn('Default Store'); + $store->method('getBaseUrl') + ->willReturn('https://example.com/'); $this->storeManager->expects($this->once()) ->method('getStore') @@ -99,11 +101,13 @@ public function testBuildReturnsPayload(): void 'https://example.com/cancel' ); - $this->omiseHelper->expects($this->exactly(2)) + $this->omiseHelper->expects($this->exactly(4)) ->method('getConfig') ->willReturnMap([ ['upa_theme_color', 1, '#000000'], ['upa_text_color', 1, '#FFFFFF'], + ['dynamic_webhooks', 1, '1'], + ['webhook_status', 1, '1'], ]); $result = $this->builder->build([ @@ -129,6 +133,7 @@ public function testBuildReturnsPayload(): void 'theme_color' => '#000000', 'text_color' => '#FFFFFF' ], + 'webhooks' => ['https://example.com/omise/callback/webhook'], 'is_upa' => true, 'locale' => 'en' ], $result); @@ -141,7 +146,7 @@ public function testBuildWithoutLocale(): void { $paymentDO = $this->createMock(PaymentDataObject::class); $payment = $this->createMock(InfoMock::class); - $store = $this->createMock(StoreInterface::class); + $store = $this->createMock(Store::class); $order = $this->createConfiguredMock( OrderAdapterInterface::class, [ @@ -162,6 +167,8 @@ public function testBuildWithoutLocale(): void $store->method('getName') ->willReturn('Default Store'); + $store->method('getBaseUrl') + ->willReturn('https://example.com/'); $this->storeManager->method('getStore') ->willReturn($store); @@ -173,6 +180,8 @@ public function testBuildWithoutLocale(): void ->willReturnMap([ ['upa_theme_color', 1, '#000000'], ['upa_text_color', 1, '#FFFFFF'], + ['dynamic_webhooks', 1, '1'], + ['webhook_status', 1, '1'], ]); $this->localeResolver->method('getLocale') @@ -213,7 +222,7 @@ public function testBuildReturnsEmptyArrayWhenMethodIdIsEmpty(): void $this->storeManager->method('getStore') ->willReturn( - $this->createMock(StoreInterface::class) + $this->createMock(Store::class) ); $this->omiseHelper->expects($this->once()) diff --git a/Test/Unit/Helper/OmiseHelperTest.php b/Test/Unit/Helper/OmiseHelperTest.php index 0da6ee7a..fceba8e4 100644 --- a/Test/Unit/Helper/OmiseHelperTest.php +++ b/Test/Unit/Helper/OmiseHelperTest.php @@ -7,6 +7,7 @@ use Omise\Payment\Model\Config\Paynow; use Omise\Payment\Model\Config\CcGooglePay; use Omise\Payment\Model\Config\Conveniencestore; +use Omise\Payment\Model\Api\CheckoutSession; class OmiseHelperTest extends \PHPUnit\Framework\TestCase { @@ -16,6 +17,8 @@ class OmiseHelperTest extends \PHPUnit\Framework\TestCase protected $scopeConfig; + protected $checkoutSession; + protected $model; private $authorizeUri = 'https://somefakeuri.com/redirect'; @@ -29,7 +32,8 @@ public function setUp(): void { $this->configMock = $this->createMock('Omise\Payment\Model\Config\Config'); $this->scopeConfig = $this->createMock('Magento\Framework\App\Config\ScopeConfigInterface'); - $this->model = new OmiseHelper($this->configMock, $this->scopeConfig); + $this->checkoutSession = $this->createMock(CheckoutSession::class); + $this->model = new OmiseHelper($this->configMock, $this->scopeConfig, $this->checkoutSession); } /** @@ -268,6 +272,10 @@ public function isAllowUpaReturnsFalseForUnsupportedMethod() */ public function checkoutSessionEndpointReturnsCorrectUrl() { + $this->configMock->expects($this->once()) + ->method('checkoutSessionEndpoint') + ->willReturn('https://checkout-page.omise.co/'); + $this->assertEquals( 'https://checkout-page.omise.co/', $this->model->checkoutSessionEndpoint() diff --git a/Test/Unit/Model/Api/CheckoutSessionTest.php b/Test/Unit/Model/Api/CheckoutSessionTest.php index ae9dca14..c365da36 100644 --- a/Test/Unit/Model/Api/CheckoutSessionTest.php +++ b/Test/Unit/Model/Api/CheckoutSessionTest.php @@ -8,7 +8,6 @@ use Omise\Payment\Model\Api\CheckoutSession; use Omise\Payment\Model\Config\Config; use Omise\Payment\Helper\RequestHelper; -use Omise\Payment\Helper\OmiseHelper; use OmiseApiResource; class CheckoutSessionTest extends TestCase @@ -23,16 +22,10 @@ class CheckoutSessionTest extends TestCase */ private $requestHelper; - /** - * @var OmiseHelper|\PHPUnit\Framework\MockObject\MockObject - */ - private $omiseHelper; - protected function setUp(): void { $this->config = $this->createMock(Config::class); $this->requestHelper = $this->createMock(RequestHelper::class); - $this->omiseHelper = $this->createMock(OmiseHelper::class); } /** @@ -42,8 +35,7 @@ public function testConstruct() { $model = new CheckoutSession( $this->config, - $this->requestHelper, - $this->omiseHelper + $this->requestHelper ); $this->assertInstanceOf( @@ -70,13 +62,12 @@ public function testCreateSessionSuccess() $model = new CheckoutSession( $this->config, - $this->requestHelper, - $this->omiseHelper + $this->requestHelper ); - $this->omiseHelper->expects($this->once()) + $this->config->expects($this->once()) ->method('checkoutSessionEndpoint') - ->willReturn('https://api.omise.co/'); + ->willReturn('https://checkout-page.omise.co/'); $this->config->expects($this->once()) ->method('getSecretKey') @@ -85,7 +76,7 @@ public function testCreateSessionSuccess() $this->requestHelper->expects($this->once()) ->method('sendUpaSessionRequest') ->with( - 'https://api.omise.co/api/sessions', + 'https://checkout-page.omise.co/api/sessions', OmiseApiResource::REQUEST_POST, 'sk_test', $params, @@ -115,13 +106,12 @@ public function testGetSessionInfoSuccess() $model = new CheckoutSession( $this->config, - $this->requestHelper, - $this->omiseHelper + $this->requestHelper ); - $this->omiseHelper->expects($this->once()) + $this->config->expects($this->once()) ->method('checkoutSessionEndpoint') - ->willReturn('https://api.omise.co/'); + ->willReturn('https://checkout-page.omise.co/'); $this->config->expects($this->once()) ->method('getSecretKey') @@ -130,7 +120,7 @@ public function testGetSessionInfoSuccess() $this->requestHelper->expects($this->once()) ->method('sendUpaSessionRequest') ->with( - 'https://api.omise.co/api/sessions/' . $sessionId, + 'https://checkout-page.omise.co/api/sessions/' . $sessionId, OmiseApiResource::REQUEST_GET, 'sk_test' ) @@ -154,13 +144,12 @@ public function testCreateSessionThrowsLocalizedException() $model = new CheckoutSession( $this->config, - $this->requestHelper, - $this->omiseHelper + $this->requestHelper ); - $this->omiseHelper->expects($this->once()) + $this->config->expects($this->once()) ->method('checkoutSessionEndpoint') - ->willReturn('https://api.omise.co/'); + ->willReturn('https://checkout-page.omise.co/'); $this->requestHelper->expects($this->once()) ->method('sendUpaSessionRequest') @@ -182,13 +171,12 @@ public function testGetSessionInfoThrowsLocalizedException() $model = new CheckoutSession( $this->config, - $this->requestHelper, - $this->omiseHelper + $this->requestHelper ); - $this->omiseHelper->expects($this->once()) + $this->config->expects($this->once()) ->method('checkoutSessionEndpoint') - ->willReturn('https://api.omise.co/'); + ->willReturn('https://checkout-page.omise.co/'); $this->requestHelper->expects($this->once()) ->method('sendUpaSessionRequest') diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index a2435603..ce508c6f 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -51,6 +51,7 @@ Magento\Config\Model\Config\Source\Yesno + Omise\Payment\Block\Adminhtml\System\Config\Form\Field\UpaWebhookSetting our webhooks documentation.

Unless dynamic webhooks are enabled, you must add the URL below as a new endpoint on your Omise Payments dashboard (HTTPS only). @@ -60,6 +61,7 @@ Magento\Config\Model\Config\Source\Yesno + Omise\Payment\Block\Adminhtml\System\Config\Form\Field\UpaWebhookSetting 1 diff --git a/view/adminhtml/web/js/upa-webhook-setting.js b/view/adminhtml/web/js/upa-webhook-setting.js new file mode 100644 index 00000000..5a5ba7bf --- /dev/null +++ b/view/adminhtml/web/js/upa-webhook-setting.js @@ -0,0 +1,49 @@ +define([], function () { + 'use strict'; + + return function () { + var upaFlag = document.querySelector('[name="groups[omise][fields][is_upa_feature_flag_enabled][value]"]'); + if (!upaFlag || upaFlag.dataset.webhookToggleBound) { + return; + } + + upaFlag.dataset.webhookToggleBound = '1'; + + var webhookFieldNames = [ + 'groups[omise][fields][webhook_status][value]', + 'groups[omise][fields][dynamic_webhooks][value]' + ]; + + function updateWebhookFields() { + var enabled = upaFlag.value === '1'; + + webhookFieldNames.forEach(function (fieldName) { + var field = document.querySelector('[name="' + fieldName + '"]'); + if (!field || field.type === 'hidden') { + return; + } + + var hidden = document.querySelector('input[type="hidden"][name="' + fieldName + '"]'); + if (enabled) { + field.value = '1'; + field.disabled = true; + if (!hidden) { + hidden = document.createElement('input'); + hidden.type = 'hidden'; + hidden.name = fieldName; + hidden.value = '1'; + field.parentNode.appendChild(hidden); + } + } else { + field.disabled = false; + if (hidden) { + hidden.remove(); + } + } + }); + } + + upaFlag.addEventListener('change', updateWebhookFields); + updateWebhookFields(); + }; +});