From 6574b8eab3a6bd079144f1943135962f8fb543ac Mon Sep 17 00:00:00 2001 From: Spencer Gabhart Date: Mon, 22 Aug 2022 13:58:08 -0400 Subject: [PATCH 1/3] ASD-943 - Add estimated order amount to button render/updates, remove decoupled render/checkout --- Api/CheckoutSessionManagementInterface.php | 9 +- Controller/Checkout/Config.php | 3 +- CustomerData/CheckoutSession.php | 4 +- Model/CheckoutSessionManagement.php | 87 ++------- Plugin/CustomerData/Cart.php | 40 ---- etc/di.xml | 3 - etc/webapi.xml | 12 -- .../checkout-session-button-payload-load.js | 32 ---- .../js/action/checkout-session-config-load.js | 40 ++-- view/frontend/web/js/amazon-button.js | 181 +++++++++--------- 10 files changed, 130 insertions(+), 281 deletions(-) delete mode 100644 Plugin/CustomerData/Cart.php delete mode 100644 view/frontend/web/js/action/checkout-session-button-payload-load.js diff --git a/Api/CheckoutSessionManagementInterface.php b/Api/CheckoutSessionManagementInterface.php index 2a542e54a..580bbd553 100755 --- a/Api/CheckoutSessionManagementInterface.php +++ b/Api/CheckoutSessionManagementInterface.php @@ -22,16 +22,9 @@ interface CheckoutSessionManagementInterface { /** * @param string|null $cartId - * @param boolean $omitPayloads * @return mixed */ - public function getConfig($cartId = null, $omitPayloads = false); - - /** - * @param string $payloadType - * @return mixed - */ - public function getButtonPayload($payloadType = 'checkout'); + public function getConfig($cartId = null); /** * @param mixed $amazonSessionId diff --git a/Controller/Checkout/Config.php b/Controller/Checkout/Config.php index fc6294b14..2efbc919e 100644 --- a/Controller/Checkout/Config.php +++ b/Controller/Checkout/Config.php @@ -46,8 +46,7 @@ public function __construct( */ public function execute() { - $omitPayloads = filter_var($this->getRequest()->getParams()['omit_payloads'], FILTER_VALIDATE_BOOLEAN); - $data = $this->amazonCheckoutSession->getConfig($omitPayloads); + $data = $this->amazonCheckoutSession->getConfig(); return $this->resultJsonFactory->create()->setData($data); } } diff --git a/CustomerData/CheckoutSession.php b/CustomerData/CheckoutSession.php index 4e4ab1f1e..92dec5b4b 100755 --- a/CustomerData/CheckoutSession.php +++ b/CustomerData/CheckoutSession.php @@ -46,9 +46,9 @@ public function __construct( /** * @return array */ - public function getConfig($omitPayloads) + public function getConfig() { - $data = $this->checkoutSessionManagement->getConfig(null, $omitPayloads); + $data = $this->checkoutSessionManagement->getConfig(); if (count($data) > 0) { $data = $data[0]; } diff --git a/Model/CheckoutSessionManagement.php b/Model/CheckoutSessionManagement.php index 598361094..4ca891e0d 100755 --- a/Model/CheckoutSessionManagement.php +++ b/Model/CheckoutSessionManagement.php @@ -413,33 +413,39 @@ protected function convertToMagentoAddress(array $address, $isShippingAddress = /** * {@inheritdoc} */ - public function getConfig($cartId = null, $omitPayloads = true) + public function getConfig($cartId = null) { $result = []; $quote = $this->session->getQuoteFromIdOrSession($cartId); if ($this->canCheckoutWithAmazon($quote)) { + $loginButtonPayload = $this->amazonAdapter->generateLoginButtonPayload(); + $checkoutButtonPayload = $this->amazonAdapter->generateCheckoutButtonPayload(); $config = [ 'merchant_id' => $this->amazonConfig->getMerchantId(), 'currency' => $this->amazonConfig->getCurrencyCode(), 'button_color' => $this->amazonConfig->getButtonColor(), 'language' => $this->amazonConfig->getLanguage(), 'sandbox' => $this->amazonConfig->isSandboxEnabled(), + 'login_payload' => $loginButtonPayload, + 'login_signature' => $this->amazonAdapter->signButton($loginButtonPayload), + 'checkout_payload' => $checkoutButtonPayload, + 'checkout_signature' => $this->amazonAdapter->signButton($checkoutButtonPayload), 'public_key_id' => $this->amazonConfig->getPublicKeyId(), ]; - if (!$omitPayloads) { - $config = array_merge($config, $this->getLoginButtonPayload(), $this->getCheckoutButtonPayload()); - } - if ($quote) { // Ensure the totals are up to date, in case the checkout does something to update qty or shipping // without collecting totals $quote->collectTotals(); + $config['pay_only'] = $this->amazonHelper->isPayOnly($quote); - if (!$omitPayloads) { - $config = array_merge($config, $this->getPayNowButtonPayload($quote)); - } + $payNowButtonPayload = $this->amazonAdapter->generatePayNowButtonPayload( + $quote, + $this->amazonConfig->getPaymentAction() + ); + $config['paynow_payload'] = $payNowButtonPayload; + $config['paynow_signature'] = $this->amazonAdapter->signButton($payNowButtonPayload); } $result[] = $config; @@ -448,71 +454,6 @@ public function getConfig($cartId = null, $omitPayloads = true) return $result; } - public function getButtonPayload($payloadType = 'checkout') - { - switch ($payloadType) { - case 'paynow': - $quote = $this->session->getQuoteFromIdOrSession(); - return $this->getPayNowButtonPayload($quote); - case 'login': - return $this->getLoginButtonPayload(); - default: - return $this->getCheckoutButtonPayload(); - } - } - - /** - * Generate login button payload/signature pair. - * - * @return mixed - */ - private function getLoginButtonPayload() - { - $loginButtonPayload = $this->amazonAdapter->generateLoginButtonPayload(); - $result = [ - 'login_payload' => $loginButtonPayload, - 'login_signature' => $this->amazonAdapter->signButton($loginButtonPayload) - ]; - - return $result; - } - - /** - * Generate checkout button payload/signature pair. - * - * @return mixed - */ - private function getCheckoutButtonPayload() - { - $checkoutButtonPayload = $this->amazonAdapter->generateCheckoutButtonPayload(); - $result = [ - 'checkout_payload' => $checkoutButtonPayload, - 'checkout_signature' => $this->amazonAdapter->signButton($checkoutButtonPayload) - ]; - - return $result; - } - - /** - * Generate paynow payload/signature pair. - * - * @param CartInterface $quote - * @return mixed - */ - private function getPayNowButtonPayload($quote) - { - $payNowButtonPayload = $this->amazonAdapter->generatePayNowButtonPayload( - $quote, - $this->amazonConfig->getPaymentAction() - ); - $result = [ - 'paynow_payload' => $payNowButtonPayload, - 'paynow_signature' => $this->amazonAdapter->signButton($payNowButtonPayload) - ]; - - return $result; - } - /** * {@inheritdoc} */ diff --git a/Plugin/CustomerData/Cart.php b/Plugin/CustomerData/Cart.php deleted file mode 100644 index 4c991b219..000000000 --- a/Plugin/CustomerData/Cart.php +++ /dev/null @@ -1,40 +0,0 @@ -checkoutSession = $checkoutSession; - } - - /** - * @param \Magento\Checkout\CustomerData\Cart $subject - * @param $result - * @return mixed - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException - * - * Adds virtual cart flag to the cart local storage for button rendering - */ - public function afterGetSectionData( - \Magento\Checkout\CustomerData\Cart $subject, - $result - ) { - $result['amzn_pay_only'] = $this->checkoutSession->getQuote()->isVirtual(); - - return $result; - } -} diff --git a/etc/di.xml b/etc/di.xml index b58e9b207..258c6658a 100755 --- a/etc/di.xml +++ b/etc/di.xml @@ -379,7 +379,4 @@ - - - diff --git a/etc/webapi.xml b/etc/webapi.xml index b7ff50faf..ac23bb8ae 100755 --- a/etc/webapi.xml +++ b/etc/webapi.xml @@ -21,24 +21,12 @@ - - %omit_payloads% - - - %omit_payloads% - - - - - - - diff --git a/view/frontend/web/js/action/checkout-session-button-payload-load.js b/view/frontend/web/js/action/checkout-session-button-payload-load.js deleted file mode 100644 index 3e788c6e9..000000000 --- a/view/frontend/web/js/action/checkout-session-button-payload-load.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright © Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - - define([ - 'jquery', - 'underscore', - 'mage/storage', - 'mage/url', - 'Magento_Customer/js/customer-data' -], function ($, _, remoteStorage, url, customerData) { - 'use strict'; - - return function (callback, payloadType) { - var serviceUrl = url.build(`rest/V1/amazon-checkout-session/button-payload/${payloadType}`); - - remoteStorage.get(serviceUrl).done(function (payload) { - callback(payload); - }); - }; -}); diff --git a/view/frontend/web/js/action/checkout-session-config-load.js b/view/frontend/web/js/action/checkout-session-config-load.js index 8989bfa4a..b75ea1099 100644 --- a/view/frontend/web/js/action/checkout-session-config-load.js +++ b/view/frontend/web/js/action/checkout-session-config-load.js @@ -13,7 +13,7 @@ * permissions and limitations under the License. */ -define([ + define([ 'jquery', 'underscore', 'mage/storage', @@ -22,31 +22,33 @@ define([ ], function ($, _, remoteStorage, url, customerData) { 'use strict'; - const storageKey = 'amzn-checkout-session-config'; - $('.switcher-option').on('click', function () { - $.localStorage.remove(storageKey); - }); - + var callbacks = []; var localStorage = null; var getLocalStorage = function () { if (localStorage === null) { - localStorage = $.initNamespaceStorage(storageKey).localStorage; + localStorage = $.initNamespaceStorage('amzn-checkout-session-config').localStorage; } return localStorage; - }; - return function (callback, omitPayloads = true) { + }; + return function (callback, forceReload = false) { var cartId = customerData.get('cart')()['data_id'] || window.checkout.storeId; var config = getLocalStorage().get('config') || false; - if (!config) { - remoteStorage.get(url.build(`amazon_pay/checkout/config?omit_payloads=${omitPayloads}`)).done(function (config) { - getLocalStorage().set('cart_id', cartId); - getLocalStorage().set('config', config); - - callback(getLocalStorage().get('config')); - }); - } - else { + if (forceReload + || cartId !== getLocalStorage().get('cart_id') + || typeof config.checkout_payload === 'undefined' + || !config.checkout_payload.includes(document.URL.slice(0, -1))) { + callbacks.push(callback); + if (callbacks.length == 1) { + remoteStorage.get(url.build('amazon_pay/checkout/config')).done(function (config) { + getLocalStorage().set('cart_id', cartId); + getLocalStorage().set('config', config); + do { + callbacks.shift()(config); + } while (callbacks.length); + }); + } + } else { callback(getLocalStorage().get('config')); } - }; + }; }); diff --git a/view/frontend/web/js/amazon-button.js b/view/frontend/web/js/amazon-button.js index 578533cbe..3dd003258 100755 --- a/view/frontend/web/js/amazon-button.js +++ b/view/frontend/web/js/amazon-button.js @@ -16,7 +16,6 @@ define([ 'ko', 'jquery', 'Amazon_Pay/js/action/checkout-session-config-load', - 'Amazon_Pay/js/action/checkout-session-button-payload-load', 'Amazon_Pay/js/model/storage', 'mage/url', 'Amazon_Pay/js/amazon-checkout', @@ -29,7 +28,6 @@ define([ ko, $, checkoutSessionConfigLoad, - buttonPayloadLoad, amazonStorage, url, amazonCheckout, @@ -52,20 +50,43 @@ define([ drawing: false, amazonPayButton: null, + currencyCode: null, - _loadButtonConfig: function (callback) { + _loadButtonConfig: function (callback, forceReload = false) { checkoutSessionConfigLoad(function (checkoutSessionConfig) { if (!$.isEmptyObject(checkoutSessionConfig)) { - callback({ + var payload = checkoutSessionConfig['checkout_payload']; + var signature = checkoutSessionConfig['checkout_signature']; + + if (this.buttonType === 'PayNow') { + payload = checkoutSessionConfig['paynow_payload']; + signature = checkoutSessionConfig['paynow_signature']; + } + + self.currencyCode = checkoutSessionConfig['currency']; + + var buttonConfig = { merchantId: checkoutSessionConfig['merchant_id'], publicKeyId: checkoutSessionConfig['public_key_id'], - ledgerCurrency: checkoutSessionConfig['currency'], + ledgerCurrency: self.currencyCode, sandbox: checkoutSessionConfig['sandbox'], checkoutLanguage: checkoutSessionConfig['language'], - productType: this._isPayOnly() ? 'PayOnly' : 'PayAndShip', + productType: this._isPayOnly(checkoutSessionConfig['pay_only']) ? 'PayOnly' : 'PayAndShip', placement: this.options.placement, - buttonColor: checkoutSessionConfig['button_color'] - }); + buttonColor: checkoutSessionConfig['button_color'], + createCheckoutSessionConfig: { + payloadJSON: payload, + signature: signature, + publicKeyId: checkoutSessionConfig['public_key_id'], + } + }; + + if (this._shouldUseEstimatedAmount()) + { + buttonConfig.estimatedOrderAmount = this._getEstimatedAmount(); + } + + callback(buttonConfig); if (this.options.placement !== "Checkout") { $(this.options.hideIfUnavailable).show(); @@ -73,68 +94,33 @@ define([ } else { $(this.options.hideIfUnavailable).hide(); } - }.bind(this)); - }, - - _loadInitCheckoutPayload: function (callback, payloadType) { - checkoutSessionConfigLoad(function (checkoutSessionConfig) { - var self = this; - buttonPayloadLoad(function (buttonPayload) { - var initCheckoutPayload = { - createCheckoutSessionConfig: { - payloadJSON: buttonPayload[0], - signature: buttonPayload[1], - publicKeyId: checkoutSessionConfig['public_key_id'] - } - }; - - if (payloadType !== 'paynow' - && !amazonStorage.isMulticurrencyEnabled - && !JSON.parse(buttonPayload[0]).recurringMetadata) - { - initCheckoutPayload.estimatedOrderAmount = self._getEstimatedAmount(); - } - callback(initCheckoutPayload); - }, payloadType); - }.bind(this)); + }.bind(this), forceReload); }, _getEstimatedAmount: function () { - var currencyCode; - var subtotal = parseFloat(customerData.get('cart')().subtotalAmount).toFixed(2); + var subtotal = (parseFloat(customerData.get('cart')().subtotalAmount) || 0).toFixed(2); - checkoutSessionConfigLoad(function (checkoutSessionConfig) { - currencyCode = checkoutSessionConfig['currency']; - }); - - if (currencyCode === 'JPY') { + if (self.currencyCode === 'JPY') { subtotal = parseFloat(subtotal).toFixed(0); } return { amount: subtotal, - currencyCode: currencyCode + currencyCode: self.currencyCode }; }, /** + * @param {boolean} isCheckoutSessionPayOnly * @returns {boolean} * @private */ - _isPayOnly: function () { - var cartData = customerData.get('cart'); - - // No cart data yet or cart is empty, for the pdp button - if (typeof cartData().amzn_pay_only === 'undefined' || parseInt(cartData().summary_count) === 0) { - return this.options.payOnly + _isPayOnly: function (isCheckoutSessionPayOnly) { + var result = isCheckoutSessionPayOnly; + if (result && this.options.payOnly !== null) { + result = this.options.payOnly; } - - // Check if cart has items and it's the pdp button - if (parseInt(cartData().summary_count) > 0 && this.options.payOnly !== null) { - return cartData().amzn_pay_only && this.options.payOnly; - } - - return cartData().amzn_pay_only; + return result; }, /** @@ -167,39 +153,48 @@ define([ $buttonContainer.empty().append($buttonRoot); this._loadButtonConfig(function (buttonConfig) { + // do not use session config for decoupled button + if (self.buttonType === 'PayNow') { + delete buttonConfig.createCheckoutSessionConfig; + } + try { self.amazonPayButton = amazon.Pay.renderButton('#' + $buttonRoot.empty().removeUniqueId().uniqueId().attr('id'), buttonConfig); } catch (e) { console.log('Amazon Pay button render error: ' + e); return; } - self.amazonPayButton.onClick(function() { - if (self.buttonType === 'PayNow' && !additionalValidators.validate()) { - return false; - } - //This is for compatibility with Iosc. We need to update the customer's Magento session before getting the final config and payload - if (self.buttonType === 'PayNow' && self.options.isIosc()) { - storage.post( - 'checkout/onepage/update', - "{}", - false - ).done( - function (response) { - if (!response.error) { - self._initCheckout(); - } else { + + // If onClick is available on the amazonPayButton, then checkout is decoupled from render, indicating this is an APB button + if (self.amazonPayButton.onClick) { + self.amazonPayButton.onClick(function() { + if (!additionalValidators.validate()) { + return false; + } + //This is for compatibility with Iosc. We need to update the customer's Magento session before getting the final config and payload + if (self.options.isIosc()) { + storage.post( + 'checkout/onepage/update', + "{}", + false + ).done( + function (response) { + if (!response.error) { + self._initCheckout(); + } else { + errorProcessor.process(response); + } + } + ).fail( + function (response) { errorProcessor.process(response); } - } - ).fail( - function (response) { - errorProcessor.process(response); - } - ); - }else{ - self._initCheckout(); - } - }); + ); + }else{ + self._initCheckout(); + } + }); + } $('.amazon-button-container .field-tooltip').fadeIn(); self.drawing = false; @@ -229,17 +224,15 @@ define([ } } - var payloadType = this.buttonType ? - 'paynow' : - 'checkout'; - this._loadInitCheckoutPayload(function (initCheckoutPayload) { - self.amazonPayButton.initCheckout(initCheckoutPayload); - }, payloadType); + this._loadButtonConfig(function (buttonConfig) { + var initConfig = {createCheckoutSessionConfig: buttonConfig.createCheckoutSessionConfig}; + self.amazonPayButton.initCheckout(initConfig); + }, true); customerData.invalidate('*'); }, /** - * Redraw button if needed + * Update button if needed **/ _subscribeToCartUpdates: function () { var self = this; @@ -247,17 +240,25 @@ define([ amazonCheckout.withAmazonCheckout(function (amazon, args) { var cartData = customerData.get('cart'); cartData.subscribe(function (updatedCart) { - if (!$(self.options.hideIfUnavailable).first().is(':visible')) { - self._draw(); + if (self.options.placement === 'Cart') { + delete self.amazonPayButton; } - - if (self.amazonPayButton && self.buttonType !== 'PayNow') { - self.amazonPayButton.updateButtonInfo(self._getEstimatedAmount()); + if (self.amazonPayButton && self._shouldUseEstimatedAmount()) + { + if (updatedCart.summary_count !== 0) { + self.amazonPayButton.updateButtonInfo(self._getEstimatedAmount()); + } } }); }); }, + _shouldUseEstimatedAmount: function () { + return this.options.buttonType !== 'PayNow' + && this.options.placement !== 'Product' + && !amazonStorage.isMulticurrencyEnabled; + }, + click: function () { this.element.children().first().trigger('click'); } From e4717327ca787ff93c83ba3e45880ebb6dd114eb Mon Sep 17 00:00:00 2001 From: Spencer Gabhart Date: Wed, 24 Aug 2022 08:50:50 -0400 Subject: [PATCH 2/3] Fix phpcs warnings --- Block/Adminhtml/System/Config/AutoKeyExchangeAdmin.php | 4 +++- .../templates/system/config/autokeyexchange_admin.phtml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Block/Adminhtml/System/Config/AutoKeyExchangeAdmin.php b/Block/Adminhtml/System/Config/AutoKeyExchangeAdmin.php index 11d14d10b..b63a930e4 100644 --- a/Block/Adminhtml/System/Config/AutoKeyExchangeAdmin.php +++ b/Block/Adminhtml/System/Config/AutoKeyExchangeAdmin.php @@ -59,7 +59,9 @@ public function getRegion() public function getCurrency() { $currency = $this->autokeyexchange->getCurrency(); - if($currency) $currency = strtoupper($currency); + if ($currency) { + $currency = strtoupper($currency); + } return $currency; } } diff --git a/view/adminhtml/templates/system/config/autokeyexchange_admin.phtml b/view/adminhtml/templates/system/config/autokeyexchange_admin.phtml index 61ac19985..b5a73e8ef 100644 --- a/view/adminhtml/templates/system/config/autokeyexchange_admin.phtml +++ b/view/adminhtml/templates/system/config/autokeyexchange_admin.phtml @@ -34,7 +34,7 @@ $currency = $block->getCurrency(); - +
From 3392cbb0bfb1c7158138293ede920a0bcb255e56 Mon Sep 17 00:00:00 2001 From: Spencer Gabhart Date: Wed, 24 Aug 2022 09:20:28 -0400 Subject: [PATCH 3/3] Version bump to 5.14.1 and update changelog --- CHANGELOG.md | 4 ++++ README.md | 2 +- composer.json | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e8b8ccfe..e69905d24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 5.14.1 +* Changed how buttons are rendered for compatibility with estimated order amount feature +* Removed estimated order amount from PDP button + ## 5.14.0 * Added configurable options for checkout and signin cancel return urls * Added estimated order amount to the button payload diff --git a/README.md b/README.md index 1abf9f8ce..672a2590f 100755 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ The following table provides an overview on which Git branch is compatible to wh Magento Version | Github Branch | Latest release ---|---|--- 2.2.6 - 2.2.11 (EOL) | [V2checkout-1.2.x](https://github.com/amzn/amazon-payments-magento-2-plugin/tree/V2checkout-1.2.x) | 1.20.0 (EOL) -2.3.0 - 2.4.x | [master](https://github.com/amzn/amazon-payments-magento-2-plugin/tree/master) | 5.14.0 +2.3.0 - 2.4.x | [master](https://github.com/amzn/amazon-payments-magento-2-plugin/tree/master) | 5.14.1 ## Release Notes See [CHANGELOG.md](/CHANGELOG.md) diff --git a/composer.json b/composer.json index c0748464f..547bfc7e7 100755 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "amzn/amazon-pay-magento-2-module", "description": "Official Magento2 Plugin to integrate with Amazon Pay", "type": "magento2-module", - "version": "5.14.0", + "version": "5.14.1", "license": [ "Apache-2.0" ],