diff --git a/Api/CheckoutSessionManagementInterface.php b/Api/CheckoutSessionManagementInterface.php
index 4d42b102..c5e0d04e 100755
--- a/Api/CheckoutSessionManagementInterface.php
+++ b/Api/CheckoutSessionManagementInterface.php
@@ -21,9 +21,10 @@
interface CheckoutSessionManagementInterface
{
/**
+ * @param mixed|null $cartId
* @return mixed
*/
- public function getConfig();
+ public function getConfig($cartId = null);
/**
* @param mixed $amazonSessionId
@@ -45,13 +46,15 @@ public function getPaymentDescriptor($amazonSessionId);
/**
* @param mixed $amazonSessionId
+ * @param mixed|null $cartId
* @return string
*/
- public function updateCheckoutSession($amazonSessionId);
+ public function updateCheckoutSession($amazonSessionId, $cartId = null);
/**
* @param mixed $amazonSessionId
+ * @param mixed|null $cartId
* @return int
*/
- public function completeCheckoutSession($amazonSessionId);
+ public function completeCheckoutSession($amazonSessionId, $cartId = null);
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 250e2d3c..43178434 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
# Change Log
+## 5.7.1
+* Fixed issue when phone number not required and entered in Magento
+* Updated API calls to take in a masked cart ID so they can be used without relying on Magento sessions
+* Updated logging to sanitize some data
+
## 5.7.0
* Changed the response of completeCheckoutSession API call to include both increment ID and order ID
* Fixed issue with logging in when a customer has an empty password hash (thanks @rafczow!)
@@ -8,7 +13,6 @@
* Fixed issue where using Amazon Pay in the Payment Methods section did not work on one step checkouts
* Fixed issue where using Amazon Pay in the Payment Methods section could bypass agreeing to Terms and Conditions
* Removed usage of isPlaceOrderActionAllowed in js components
-* Updated API calls to take in a masked cart ID so they can be used without relying on Magento sessions
* Updated response validators to look for specific response code and states
## 5.6.0
diff --git a/CustomerData/CheckoutSession.php b/CustomerData/CheckoutSession.php
index 91bb3004..25ec4ef8 100755
--- a/CustomerData/CheckoutSession.php
+++ b/CustomerData/CheckoutSession.php
@@ -48,6 +48,6 @@ public function __construct(
*/
public function getConfig()
{
- return $this->checkoutSessionManagement->getConfig($this->session->getQuote());
+ return $this->checkoutSessionManagement->getConfig();
}
}
diff --git a/Model/Adapter/AmazonPayAdapter.php b/Model/Adapter/AmazonPayAdapter.php
index 2d38e814..426d668f 100755
--- a/Model/Adapter/AmazonPayAdapter.php
+++ b/Model/Adapter/AmazonPayAdapter.php
@@ -459,18 +459,36 @@ protected function processResponse($clientResponse, $functionName)
// Log
$isError = !in_array($response['status'], [200, 201]);
if ($isError || $this->amazonConfig->isLoggingEnabled()) {
- $debugBackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 2);
- $this->logger->debug($functionName . ' <- ', $debugBackTrace[1]['args']);
- if ($isError) {
- $this->logger->error($functionName . ' -> ', $response);
- } else {
- $this->logger->debug($functionName . ' -> ', $response);
- }
+ $this->logSanitized($functionName, $response, $isError);
}
return $response;
}
+ protected function logSanitized($functionName, $response, $isError)
+ {
+ $debugBackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 3);
+ $buyerKeys = ['buyerId' => '', 'primeMembershipTypes' => '', 'status' => ''];
+
+ if ($functionName == 'getBuyer') {
+ $response = array_intersect_key($response, $buyerKeys);
+ $debugBackTrace[2]['args'] = [];
+ }
+
+ if (isset($response['buyer'])) {
+ $response['buyer'] = array_intersect_key($response['buyer'], $buyerKeys);
+ }
+
+ unset($response['shippingAddress'], $response['billingAddress']);
+
+ $this->logger->debug($functionName . ' <- ', $debugBackTrace[2]['args']);
+ if ($isError) {
+ $this->logger->error($functionName . ' -> ', $response);
+ } else {
+ $this->logger->debug($functionName . ' -> ', $response);
+ }
+ }
+
/**
* Generate idempotency header
*
@@ -568,7 +586,15 @@ public function generatePayNowButtonPayload(Quote $quote, $paymentIntent = Payme
$addressData[$addressKey] = $streetLine;
}
- $addressData = array_filter($addressData);
+ // Remove empty fields, or ones that contain only "-"
+ $addressData = array_filter($addressData, function ($val) {
+ return !empty($val) && $val != "-";
+ });
+
+ // Make sure phone number is set for PayNow button
+ if (!array_key_exists('phoneNumber', $addressData)) {
+ $addressData['phoneNumber'] = "0";
+ }
$payload['addressDetails'] = $addressData;
}
diff --git a/Model/CheckoutSessionManagement.php b/Model/CheckoutSessionManagement.php
index 11afe738..f68d6e54 100755
--- a/Model/CheckoutSessionManagement.php
+++ b/Model/CheckoutSessionManagement.php
@@ -22,12 +22,15 @@
use Amazon\Pay\Model\Config\Source\PaymentAction;
use Amazon\Pay\Model\AsyncManagement;
use http\Exception\UnexpectedValueException;
+use Magento\Framework\Exception\NoSuchEntityException;
+use Magento\Framework\Exception\NotFoundException;
use Magento\Quote\Api\Data\CartInterface;
use Magento\Framework\Validator\Exception as ValidatorException;
use Magento\Framework\Webapi\Exception as WebapiException;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\Order\Invoice;
use Magento\Sales\Model\Order\Payment;
+use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface;
use Magento\Sales\Api\Data\TransactionInterface as Transaction;
class CheckoutSessionManagement implements \Amazon\Pay\Api\CheckoutSessionManagementInterface
@@ -137,6 +140,11 @@ class CheckoutSessionManagement implements \Amazon\Pay\Api\CheckoutSessionManage
*/
private $orderCollectionFactory;
+ /**
+ * @var MaskedQuoteIdToQuoteIdInterface
+ */
+ private $maskedQuoteIdConverter;
+
/**
* CheckoutSessionManagement constructor.
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
@@ -158,6 +166,7 @@ class CheckoutSessionManagement implements \Amazon\Pay\Api\CheckoutSessionManage
* @param \Magento\Sales\Api\TransactionRepositoryInterface $transactionRepository
* @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder
* @param \Magento\Sales\Model\ResourceModel\Order\CollectionFactory $orderCollectionFactory
+ * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdConverter
*/
public function __construct(
\Magento\Store\Model\StoreManagerInterface $storeManager,
@@ -178,7 +187,8 @@ public function __construct(
\Amazon\Pay\Model\AsyncManagement\Charge $asyncCharge,
\Magento\Sales\Api\TransactionRepositoryInterface $transactionRepository,
\Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder,
- \Magento\Sales\Model\ResourceModel\Order\CollectionFactory $orderCollectionFactory
+ \Magento\Sales\Model\ResourceModel\Order\CollectionFactory $orderCollectionFactory,
+ MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdConverter
) {
$this->storeManager = $storeManager;
$this->quoteIdMaskFactory = $quoteIdMaskFactory;
@@ -199,6 +209,7 @@ public function __construct(
$this->transactionRepository = $transactionRepository;
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
$this->orderCollectionFactory = $orderCollectionFactory;
+ $this->maskedQuoteIdConverter = $maskedQuoteIdConverter;
}
/**
@@ -219,10 +230,10 @@ protected function getAmazonSession($amazonSessionId)
/**
* @return bool
*/
- protected function canCheckoutWithAmazon()
+ protected function canCheckoutWithAmazon($quote)
{
return $this->amazonConfig->isEnabled() &&
- !$this->amazonHelper->hasRestrictedProducts($this->magentoCheckoutSession->getQuote());
+ !$this->amazonHelper->hasRestrictedProducts($quote);
}
/**
@@ -303,22 +314,47 @@ protected function convertToMagentoAddress(array $address, $isShippingAddress =
return [$this->addressHelper->convertToArray($magentoAddress)];
}
+ /**
+ * Load quote from provided masked quote ID or falls back to loading from the session
+ * @param $cartId null|string
+ * @return false|CartInterface|\Magento\Quote\Model\Quote
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ private function getQuoteFromIdOrSession($cartId = null)
+ {
+ try {
+ if (empty($cartId)) {
+ $quote = $this->magentoCheckoutSession->getQuote();
+ } else {
+ $quoteId = $this->maskedQuoteIdConverter->execute($cartId);
+ $quote = $this->cartRepository->get($quoteId);
+ }
+ } catch (NoSuchEntityException $e) {
+ return false;
+ }
+
+ return $quote;
+ }
+
/**
* {@inheritdoc}
*/
- public function getConfig()
+ public function getConfig($cartId = null)
{
+ if (!$quote = $this->getQuoteFromIdOrSession($cartId)) {
+ return [];
+ }
+
// Ensure the totals are up to date, in case the checkout does something to update qty or shipping without
// collecting totals
- $this->magentoCheckoutSession->getQuote()->collectTotals();
+ $quote->collectTotals();
$result = [];
- if ($this->canCheckoutWithAmazon()) {
- $magentoQuote = $this->magentoCheckoutSession->getQuote();
+ if ($this->canCheckoutWithAmazon($quote)) {
$loginButtonPayload = $this->amazonAdapter->generateLoginButtonPayload();
$checkoutButtonPayload = $this->amazonAdapter->generateCheckoutButtonPayload();
$payNowButtonPayload = $this->amazonAdapter->generatePayNowButtonPayload(
- $magentoQuote,
+ $quote,
$this->amazonConfig->getPaymentAction()
);
@@ -327,7 +363,7 @@ public function getConfig()
'currency' => $this->amazonConfig->getCurrencyCode(),
'button_color' => $this->amazonConfig->getButtonColor(),
'language' => $this->amazonConfig->getLanguage(),
- 'pay_only' => $this->amazonHelper->isPayOnly($magentoQuote),
+ 'pay_only' => $this->amazonHelper->isPayOnly($quote),
'sandbox' => $this->amazonConfig->isSandboxEnabled(),
'login_payload' => $loginButtonPayload,
'login_signature' => $this->amazonAdapter->signButton($loginButtonPayload),
@@ -346,13 +382,9 @@ public function getConfig()
*/
public function getShippingAddress($amazonSessionId)
{
- $result = false;
-
- if ($this->canCheckoutWithAmazon()) {
- $result = $this->fetchAddress($amazonSessionId, true, function ($session) {
- return $session['shippingAddress'] ?? [];
- });
- }
+ $result = $this->fetchAddress($amazonSessionId, true, function ($session) {
+ return $session['shippingAddress'] ?? [];
+ });
return $result;
}
@@ -362,13 +394,9 @@ public function getShippingAddress($amazonSessionId)
*/
public function getBillingAddress($amazonSessionId)
{
- $result = false;
-
- if ($this->canCheckoutWithAmazon()) {
- $result = $this->fetchAddress($amazonSessionId, false, function ($session) {
- return $session['billingAddress'] ?? [];
- });
- }
+ $result = $this->fetchAddress($amazonSessionId, false, function ($session) {
+ return $session['billingAddress'] ?? [];
+ });
return $result;
}
@@ -385,13 +413,16 @@ public function getPaymentDescriptor($amazonSessionId)
/**
* {@inheritdoc}
*/
- public function updateCheckoutSession($amazonCheckoutSessionId)
+ public function updateCheckoutSession($amazonCheckoutSessionId, $cartId = null)
{
- $quote = $this->magentoCheckoutSession->getQuote();
+ if (!$quote = $this->getQuoteFromIdOrSession($cartId)) {
+ return [];
+ }
+
$result = null;
$paymentIntent = Adapter\AmazonPayAdapter::PAYMENT_INTENT_AUTHORIZE;
- if ($this->canCheckoutWithAmazon()) {
+ if ($this->canCheckoutWithAmazon($quote)) {
$response = $this->amazonAdapter->updateCheckoutSession(
$quote,
$amazonCheckoutSessionId,
@@ -399,6 +430,8 @@ public function updateCheckoutSession($amazonCheckoutSessionId)
);
if (!empty($response['webCheckoutDetails']['amazonPayRedirectUrl'])) {
$result = $response['webCheckoutDetails']['amazonPayRedirectUrl'];
+ } elseif (!empty($response) && $response['status'] == 404) {
+ $result = ['status' => $response['reasonCode']];
}
}
return $result;
@@ -487,13 +520,13 @@ protected function setProcessing($payment)
* Add capture comment to order
*
* @param Payment $payment
- * @param $cart
+ * @param $quote
* @param $chargeId
*/
- protected function addCaptureComment($payment, $cart, $chargeId)
+ protected function addCaptureComment($payment, $quote, $chargeId)
{
$order = $payment->getOrder();
- $formattedAmount = $order->getBaseCurrency()->formatTxt($cart->getBaseGrandTotal());
+ $formattedAmount = $order->getBaseCurrency()->formatTxt($quote->getBaseGrandTotal());
if ($order->getBaseCurrencyCode() != $order->getOrderCurrencyCode()) {
$formattedAmount = $formattedAmount . ' [' . $order->formatPriceTxt($payment->getAmountOrdered()) . ']';
}
@@ -538,24 +571,26 @@ private function cancelOrder($order)
/**
* {@inheritdoc}
*/
- public function completeCheckoutSession($amazonSessionId)
+ public function completeCheckoutSession($amazonSessionId, $cartId = null)
{
- $cart = $this->magentoCheckoutSession->getQuote();
+ if (!$quote = $this->getQuoteFromIdOrSession($cartId)) {
+ return ['success' => false];
+ }
- if (empty($amazonSessionId) || !$this->canCheckoutWithAmazon() || !$this->canSubmitQuote($cart)) {
+ if (empty($amazonSessionId) || !$this->canCheckoutWithAmazon($quote) || !$this->canSubmitQuote($quote)) {
return [
'success' => false,
'message' => __("Unable to complete Amazon Pay checkout"),
];
}
try {
- if (!$cart->getCustomer()->getId()) {
- $cart->setCheckoutMethod(\Magento\Quote\Api\CartManagementInterface::METHOD_GUEST);
+ if (!$quote->getCustomer()->getId()) {
+ $quote->setCheckoutMethod(\Magento\Quote\Api\CartManagementInterface::METHOD_GUEST);
}
// check the Amazon session one last time before placing the order
$amazonSession = $this->amazonAdapter->getCheckoutSession(
- $cart->getStoreId(),
+ $quote->getStoreId(),
$amazonSessionId
);
if ($amazonSession['statusDetails']['state'] == 'Canceled') {
@@ -578,15 +613,15 @@ public function completeCheckoutSession($amazonSessionId)
$amazonAddress = $this->amazonAddressFactory->create(['address' => $address]);
$customerAddress = $this->addressHelper->convertToMagentoEntity($amazonAddress);
- $cart->getBillingAddress()->importCustomerAddressData($customerAddress);
- if (empty($cart->getCustomerEmail())) {
- $cart->setCustomerEmail($amazonSession['buyer']['email']);
+ $quote->getBillingAddress()->importCustomerAddressData($customerAddress);
+ if (empty($quote->getCustomerEmail())) {
+ $quote->setCustomerEmail($amazonSession['buyer']['email']);
}
}
// get payment to load it in the session, so that a salesrule that relies on payment method conditions
// can work as expected
- $payment = $this->magentoCheckoutSession->getQuote()->getPayment();
+ $payment = $quote->getPayment();
// Some checkout flows (especially 3rd party) could get to this point without setting payment method
if (empty($payment->getMethod())) {
@@ -598,9 +633,9 @@ public function completeCheckoutSession($amazonSessionId)
// collect quote totals before placing order (needed for 2.3.0 and lower)
// https://github.com/amzn/amazon-payments-magento-2-plugin/issues/992
- $this->magentoCheckoutSession->getQuote()->collectTotals();
+ $quote->collectTotals();
- $orderId = $this->cartManagement->placeOrder($cart->getId());
+ $orderId = $this->cartManagement->placeOrder($quote->getId());
$order = $this->orderRepository->get($orderId);
$result = [
'success' => true,
@@ -609,10 +644,10 @@ public function completeCheckoutSession($amazonSessionId)
];
$amazonCompleteCheckoutResult = $this->amazonAdapter->completeCheckoutSession(
- $cart->getStoreId(),
+ $quote->getStoreId(),
$amazonSessionId,
- $cart->getGrandTotal(),
- $cart->getQuoteCurrencyCode()
+ $quote->getGrandTotal(),
+ $quote->getQuoteCurrencyCode()
);
$completeCheckoutStatus = $amazonCompleteCheckoutResult['status'] ?? '404';
if (!preg_match('/^2\d\d$/', $completeCheckoutStatus)) {
@@ -620,12 +655,12 @@ public function completeCheckoutSession($amazonSessionId)
$this->cancelOrder($order);
$session = $this->amazonAdapter->getCheckoutSession(
- $cart->getStoreId(),
+ $quote->getStoreId(),
$amazonSessionId
);
if (isset($session['chargePermissionId'])) {
$this->amazonAdapter->closeChargePermission(
- $cart->getStoreId(),
+ $quote->getStoreId(),
$session['chargePermissionId'],
'Canceled due to checkout session failed to complete',
true
@@ -648,15 +683,15 @@ public function completeCheckoutSession($amazonSessionId)
$this->amazonConfig->getPaymentAction() == PaymentAction::AUTHORIZE_AND_CAPTURE) {
// capture on Amazon Pay
$this->amazonAdapter->captureCharge(
- $cart->getStoreId(),
+ $quote->getStoreId(),
$chargeId,
- $cart->getGrandTotal(),
- $cart->getQuoteCurrencyCode()
+ $quote->getGrandTotal(),
+ $quote->getQuoteCurrencyCode()
);
// capture and invoice on the Magento side
- $this->asyncCharge->capture($order, $chargeId, $cart->getGrandTotal());
+ $this->asyncCharge->capture($order, $chargeId, $quote->getGrandTotal());
}
- $amazonCharge = $this->amazonAdapter->getCharge($cart->getStoreId(), $chargeId);
+ $amazonCharge = $this->amazonAdapter->getCharge($quote->getStoreId(), $chargeId);
//Send merchantReferenceId to Amazon
$this->amazonAdapter->updateChargePermission(
@@ -677,7 +712,7 @@ public function completeCheckoutSession($amazonSessionId)
case 'Authorized':
if ($this->amazonConfig->getAuthorizationMode() == AuthorizationMode::SYNC_THEN_ASYNC) {
$this->setProcessing($payment);
- $this->addCaptureComment($payment, $cart, $amazonCharge['chargePermissionId']);
+ $this->addCaptureComment($payment, $quote, $amazonCharge['chargePermissionId']);
}
break;
case 'Captured':
@@ -686,7 +721,7 @@ public function completeCheckoutSession($amazonSessionId)
if ($this->amazonConfig->getAuthorizationMode() == AuthorizationMode::SYNC_THEN_ASYNC) {
$this->setProcessing($payment);
- $this->addCaptureComment($payment, $cart, $chargeId);
+ $this->addCaptureComment($payment, $quote, $chargeId);
}
break;
}
@@ -700,13 +735,13 @@ public function completeCheckoutSession($amazonSessionId)
} catch (\Exception $e) {
$session = $this->amazonAdapter->getCheckoutSession(
- $cart->getStoreId(),
+ $quote->getStoreId(),
$amazonSessionId
);
if (isset($session['chargePermissionId'])) {
$this->amazonAdapter->closeChargePermission(
- $cart->getStoreId(),
+ $quote->getStoreId(),
$session['chargePermissionId'],
'Canceled due to technical issue: ' . $e->getMessage(),
true
diff --git a/README.md b/README.md
index 4c811151..a4221e8e 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.7.0
+2.3.0 - 2.4.x | [master](https://github.com/amzn/amazon-payments-magento-2-plugin/tree/master) | 5.7.1
## Release Notes
See [CHANGELOG.md](/CHANGELOG.md)
diff --git a/composer.json b/composer.json
index bd24ba35..cf1e4bad 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.7.0",
+ "version": "5.7.1",
"license": [
"Apache-2.0"
],
diff --git a/etc/webapi.xml b/etc/webapi.xml
index 41e54b31..351e305a 100755
--- a/etc/webapi.xml
+++ b/etc/webapi.xml
@@ -22,6 +22,12 @@
+
+
+
+
+
+
@@ -46,10 +52,22 @@
+
+
+
+
+
+
+
+
+
+
+
+