diff --git a/Block/Adminhtml/System/Config/Fieldset/AllowedByCountry.php b/Block/Adminhtml/System/Config/Fieldset/AllowedByCountry.php index 72ba55c..9bff446 100644 --- a/Block/Adminhtml/System/Config/Fieldset/AllowedByCountry.php +++ b/Block/Adminhtml/System/Config/Fieldset/AllowedByCountry.php @@ -39,10 +39,6 @@ private function getMerchantCountry(): ?string $scope = $fieldSetForm->getScope(); $scopeCode = $fieldSetForm->getScopeCode(); - if ($countryCode = $this->getRequest()->getParam('paypal_country')) { - return $countryCode; - } - return $this->config->getMerchantCountry($scope, (int)$scopeCode); } } diff --git a/Block/Adminhtml/System/Config/Form/Field/CBTAvailableCurrencies.php b/Block/Adminhtml/System/Config/Form/Field/CBTAvailableCurrencies.php index 5b24446..e8917c4 100644 --- a/Block/Adminhtml/System/Config/Form/Field/CBTAvailableCurrencies.php +++ b/Block/Adminhtml/System/Config/Form/Field/CBTAvailableCurrencies.php @@ -27,17 +27,19 @@ public function __construct( protected function _renderValue(\Magento\Framework\Data\Form\Element\AbstractElement $element) { try { - $CbtAvailableCurrencies = $this->serializer->unserialize($element->getValue()); - $newValue = ''; - if (!$CbtAvailableCurrencies) { - return parent::_renderValue($element); + if (!empty($element->getValue())) { + $CbtAvailableCurrencies = $this->serializer->unserialize($element->getValue()); + $newValue = ''; + if (!$CbtAvailableCurrencies) { + return parent::_renderValue($element); + } + + foreach ($CbtAvailableCurrencies as $currencyCode => $currency) { + $min = $currency['minimumAmount']['amount'] ?? '0'; + $newValue .= $currencyCode . '(min:' . $min . ',max:' . $currency['maximumAmount']['amount'] . ') '; + } + $element->setValue($newValue); } - - foreach ($CbtAvailableCurrencies as $currencyCode => $currency) { - $min = $currency['minimumAmount']['amount'] ?? '0'; - $newValue .= $currencyCode . '(min:' . $min . ',max:' . $currency['maximumAmount']['amount'] . ') '; - } - $element->setValue($newValue); } catch (\Exception $e) { $this->logger->critical($e); } diff --git a/Gateway/Command/GetMerchantConfigurationCommandWrapper.php b/Gateway/Command/GetMerchantConfigurationCommandWrapper.php index 2cc6489..2e60886 100644 --- a/Gateway/Command/GetMerchantConfigurationCommandWrapper.php +++ b/Gateway/Command/GetMerchantConfigurationCommandWrapper.php @@ -44,16 +44,21 @@ public function execute(array $commandSubject) try { if (!$websiteHasOwnConfig) { $this->eraseMerchantConfiguration($websiteId, $websiteHasOwnConfig); + return null; } - $this->checkCountry($scope, $websiteId); - $this->checkCurrency($scope, $websiteId); - // Disable Cash App Pay if Afterpay is disabled - if($this->afterpayConfig->getIsPaymentActive($websiteId)===false){ - $this->afterpayConfig->setCashAppPayActive(0,$websiteId); + + if ($this->afterpayConfig->getIsPaymentActive($websiteId) === true) { + $this->checkCountry($scope, $websiteId); + $this->checkCurrency($scope, $websiteId); + $this->debugLogger->setForceDebug($this->afterpayConfig->getIsDebug($websiteId)); + + return $this->merchantConfigurationCommand->execute($commandSubject); } - $this->debugLogger->setForceDebug($this->afterpayConfig->getIsDebug($websiteId)); - return $this->merchantConfigurationCommand->execute($commandSubject); + // Disable Cash App Pay if Afterpay is disabled + $this->afterpayConfig->setCashAppPayActive(0, $websiteId); + + return null; } catch (\Magento\Payment\Gateway\Command\CommandException $e) { $this->eraseMerchantConfiguration($websiteId, $websiteHasOwnConfig); $this->logger->notice($e->getMessage()); diff --git a/Gateway/ErrorMessageMapper/CaptureErrorMessageMapper.php b/Gateway/ErrorMessageMapper/CaptureErrorMessageMapper.php index 219ffa3..cbaf7d3 100644 --- a/Gateway/ErrorMessageMapper/CaptureErrorMessageMapper.php +++ b/Gateway/ErrorMessageMapper/CaptureErrorMessageMapper.php @@ -7,7 +7,7 @@ class CaptureErrorMessageMapper implements ErrorMessageMapperInterface { - public const STATUS_DECLINED_ERROR_MESSAGE = 'Aftepay payment declined. Please select an alternative payment method.'; // @codingStandardsIgnoreLine + public const STATUS_DECLINED_ERROR_MESSAGE = 'Afterpay payment declined. Please select an alternative payment method.'; // @codingStandardsIgnoreLine public function getMessage(string $code) { diff --git a/Gateway/Response/Checkout/CheckoutDataToQuoteHandler.php b/Gateway/Response/Checkout/CheckoutDataToQuoteHandler.php index 2a16c17..ada26d9 100644 --- a/Gateway/Response/Checkout/CheckoutDataToQuoteHandler.php +++ b/Gateway/Response/Checkout/CheckoutDataToQuoteHandler.php @@ -25,20 +25,36 @@ public function handle(array $handlingSubject, array $response): void /** @var \Magento\Quote\Model\Quote $quote */ $quote = $paymentDO->getPayment()->getQuote(); + $consumerEmail = $response['consumer']['email']; + $consumerName = $response['consumer']['givenNames']; + $consumerLastname = $response['consumer']['surname']; if (!$quote->getCustomerId()) { - $quote->setCustomerEmail($response['consumer']['email']); - $quote->setCustomerFirstname($response['consumer']['givenNames']); - $quote->setCustomerLastname($response['consumer']['surname']); + $quote->setCustomerEmail($consumerEmail); + $quote->setCustomerFirstname($consumerName); + $quote->setCustomerLastname($consumerLastname); } /** @var \Magento\Checkout\Api\Data\ShippingInformationInterface $shippingInformation */ $shippingInformation = $this->shippingInformationFactory->create(); + if (!empty($response['shipping']['name'])) { + $nameArray = explode(' ', $response['shipping']['name']); + $firstname = $nameArray[0] ?? $consumerName; + if (!empty($nameArray[1])) { + $lastname = implode(' ', array_slice($nameArray, 1)); + } else { + $lastname = $firstname; + } + } else { + $firstname = $consumerName; + $lastname = $consumerLastname; + } + /** @var \Magento\Quote\Api\Data\AddressInterface $address */ $address = $this->addressInterfaceFactory->create(); - $address->setEmail($response['consumer']['email']) - ->setFirstname($response['consumer']['givenNames']) - ->setLastname($response['consumer']['surname']) + $address->setEmail($consumerEmail) + ->setFirstname($firstname) + ->setLastname($lastname) ->setTelephone($response['shipping']['phoneNumber'] ?? $response['consumer']['phoneNumber']) ->setCity($response['shipping']['area1']) ->setCountryId($response['shipping']['countryCode']) diff --git a/Gateway/Validator/StockItemsValidator.php b/Gateway/Validator/StockItemsValidator.php index b6288d6..954aa7b 100644 --- a/Gateway/Validator/StockItemsValidator.php +++ b/Gateway/Validator/StockItemsValidator.php @@ -16,20 +16,20 @@ class StockItemsValidator implements \Afterpay\Afterpay\Model\Spi\StockItemsVali * @param \Magento\InventoryCatalogApi\Model\IsSingleSourceModeInterface $isSingleSourceMode * @param \Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface $defaultSourceProvider * @param \Magento\InventoryShipping\Model\GetItemsToDeductFromShipment $getItemsToDeductFromShipment - * @param \Magento\InventoryShipping\Model\SourceDeductionRequestFromShipmentFactory $sourceDeductionRequestFromShipmentFactory + * @param \Magento\InventoryShipping\Model\SourceDeductionRequestFromShipmentFactory $shipmentFactory * @param \Afterpay\Afterpay\Model\Spi\SourceValidatorServiceInterface $sourceValidatorService */ public function __construct( $isSingleSourceMode, $defaultSourceProvider, $getItemsToDeductFromShipment, - $sourceDeductionRequestFromShipmentFactory, + $shipmentFactory, $sourceValidatorService ) { $this->isSingleSourceMode = $isSingleSourceMode; $this->defaultSourceProvider = $defaultSourceProvider; $this->getItemsToDeductFromShipment = $getItemsToDeductFromShipment; - $this->sourceDeductionRequestFromShipmentFactory = $sourceDeductionRequestFromShipmentFactory; + $this->shipmentFactory = $shipmentFactory; $this->sourceValidatorService = $sourceValidatorService; } @@ -52,7 +52,7 @@ public function validate(\Magento\Sales\Model\Order\Shipment $shipment): void $shipmentItems = $this->getItemsToDeductFromShipment->execute($shipment); if (!empty($shipmentItems)) { - $sourceDeductionRequest = $this->sourceDeductionRequestFromShipmentFactory->execute( + $sourceDeductionRequest = $this->shipmentFactory->execute( $shipment, $sourceCode, $shipmentItems diff --git a/Model/Config.php b/Model/Config.php index c471a44..48ea1bc 100644 --- a/Model/Config.php +++ b/Model/Config.php @@ -362,13 +362,6 @@ public function getMerchantCountry( string $scope = ScopeInterface::SCOPE_WEBSITES, ?int $scopeCode = null ): ?string { - if ($countryCode = $this->scopeConfig->getValue( - self::XML_PATH_PAYPAL_MERCHANT_COUNTRY, - $scope, - $scopeCode - )) { - return $countryCode; - } if ($countryCode = $this->scopeConfig->getValue( \Magento\Directory\Helper\Data::XML_PATH_DEFAULT_COUNTRY, $scope, diff --git a/Model/Order/CreditMemo/CreditMemoProcessor.php b/Model/Order/CreditMemo/CreditMemoProcessor.php index 0b2a4c1..8b5d672 100644 --- a/Model/Order/CreditMemo/CreditMemoProcessor.php +++ b/Model/Order/CreditMemo/CreditMemoProcessor.php @@ -30,10 +30,8 @@ public function __construct( public function processOrder(\Magento\Sales\Model\Order $order): void { $additionalInformation = $order->getData('additional_information'); - $expireDate = $additionalInformation[ - AdditionalInformationInterface::AFTERPAY_AUTH_EXPIRY_DATE - ]; - if (!$this->expiryDate->isExpired($expireDate)) { + $expireDate = $additionalInformation[AdditionalInformationInterface::AFTERPAY_AUTH_EXPIRY_DATE] ?? null; + if (!$expireDate || !$this->expiryDate->isExpired($expireDate)) { return; } /** @var \Magento\Payment\Model\InfoInterface $payment */ @@ -45,7 +43,8 @@ public function processOrder(\Magento\Sales\Model\Order $order): void ]; if ($paymentState !== PaymentStateInterface::CAPTURED && $paymentState !== PaymentStateInterface::PARTIALLY_CAPTURED && - $paymentState !== PaymentStateInterface::VOIDED) { + $paymentState !== PaymentStateInterface::VOIDED && + $paymentState !== PaymentStateInterface::EXPIRED) { return; } $creditmemo = $this->creditMemoInitiator->init($order); diff --git a/Model/Order/CreditMemo/OrdersRetriever.php b/Model/Order/CreditMemo/OrdersRetriever.php index 19f4d7f..e47794f 100644 --- a/Model/Order/CreditMemo/OrdersRetriever.php +++ b/Model/Order/CreditMemo/OrdersRetriever.php @@ -3,14 +3,17 @@ namespace Afterpay\Afterpay\Model\Order\CreditMemo; use Afterpay\Afterpay\Api\Data\TokenInterface; +use Afterpay\Afterpay\Model\Payment\AdditionalInformationInterface; use Afterpay\Afterpay\Model\ResourceModel\Token\CollectionFactory; use Magento\Framework\App\ResourceConnection; +use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\Stdlib\DateTime\DateTime; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\Data\OrderPaymentInterface; use Magento\Sales\Model\Order; use Magento\Sales\Model\ResourceModel\Order\Collection; use Magento\Sales\Model\ResourceModel\Order\CollectionFactory as OrderCollectionFactory; +use Psr\Log\LoggerInterface; class OrdersRetriever { @@ -18,17 +21,23 @@ class OrdersRetriever private $resourceConnection; private $tokensCollectionFactory; private $dateTime; + private $serializer; + private $logger; public function __construct( OrderCollectionFactory $orderCollectionFactory, ResourceConnection $resourceConnection, CollectionFactory $tokensCollectionFactory, - DateTime $dateTime + DateTime $dateTime, + Json $serializer, + LoggerInterface $logger ) { $this->orderCollectionFactory = $orderCollectionFactory; $this->resourceConnection = $resourceConnection; $this->tokensCollectionFactory = $tokensCollectionFactory; $this->dateTime = $dateTime; + $this->serializer = $serializer; + $this->logger = $logger; } /** @@ -59,7 +68,42 @@ public function getAfterpayOrders(): array ); $orderCollection = $this->joinAfterpayPaymentAdditionalInfo($orderCollection); - return $orderCollection->getItems(); + return $this->getItemsWithAdditionalInfo($orderCollection->getItems()); + } + + /** + * @param Order[] $items + * + * @return array + */ + private function getItemsWithAdditionalInfo(array $items): array + { + $itemsWithJsonAdditionalInfo = []; + foreach ($items as $item) { + $additionalInformation = $item->getData( + OrderPaymentInterface::ADDITIONAL_INFORMATION + ); + try { + $unserializedInfo = !empty($additionalInformation) ? $this->serializer->unserialize($additionalInformation) : null; + if (!is_array($unserializedInfo)) { + continue; + } + + $item->setData(OrderPaymentInterface::ADDITIONAL_INFORMATION, $unserializedInfo); + if (isset( + $unserializedInfo[AdditionalInformationInterface::AFTERPAY_PAYMENT_STATE], + $unserializedInfo[AdditionalInformationInterface::AFTERPAY_OPEN_TO_CAPTURE_AMOUNT], + $unserializedInfo[AdditionalInformationInterface::AFTERPAY_AUTH_EXPIRY_DATE] + )) { + $itemsWithJsonAdditionalInfo[] = $item; + } + } catch (\InvalidArgumentException $e) { + $this->logger->warning('Error during ansync offline credit memo processing for Order #' . $item->getIncrementId()); + $this->logger->warning($e->getMessage()); + } + } + + return $itemsWithJsonAdditionalInfo; } private function joinAfterpayPaymentAdditionalInfo( diff --git a/Model/PaymentStateInterface.php b/Model/PaymentStateInterface.php index ee81a21..c6daf4b 100644 --- a/Model/PaymentStateInterface.php +++ b/Model/PaymentStateInterface.php @@ -8,4 +8,5 @@ interface PaymentStateInterface public const PARTIALLY_CAPTURED = 'PARTIALLY_CAPTURED'; public const CAPTURED = 'CAPTURED'; public const VOIDED = 'VOIDED'; + public const EXPIRED = 'EXPIRED'; } diff --git a/Model/StockItemsValidator/StockItemsValidatorProxy.php b/Model/StockItemsValidator/StockItemsValidatorProxy.php index c1ef8aa..259a3a3 100644 --- a/Model/StockItemsValidator/StockItemsValidatorProxy.php +++ b/Model/StockItemsValidator/StockItemsValidatorProxy.php @@ -69,11 +69,11 @@ private function getStockItemValidator(): StockItemsValidatorInterface 'getStockBySalesChannel' => $objectManager->create(GetStockBySalesChannelInterface::class), ]); $this->subject = $this->stockItemValidatorFactory->create([ - 'isSingleSourceMode' => $objectManager->create(IsSingleSourceModeInterface::class), - 'defaultSourceProvider' => $objectManager->create(DefaultSourceProviderInterface::class), - 'getItemsToDeductFromShipment' => $objectManager->create(GetItemsToDeductFromShipment::class), - 'sourceDeductionRequestFromShipmentFactory' => $objectManager->create(SourceDeductionRequestFactory::class), - 'sourceValidatorService' => $sourceValidatorService, + 'isSingleSourceMode' => $objectManager->create(IsSingleSourceModeInterface::class), + 'defaultSourceProvider' => $objectManager->create(DefaultSourceProviderInterface::class), + 'getItemsToDeductFromShipment' => $objectManager->create(GetItemsToDeductFromShipment::class), + 'shipmentFactory' => $objectManager->create(SourceDeductionRequestFactory::class), + 'sourceValidatorService' => $sourceValidatorService, ]); } diff --git a/Observer/Adminhtml/ConfigSaveAfter.php b/Observer/Adminhtml/ConfigSaveAfter.php index 39f94cf..4d9c9d9 100644 --- a/Observer/Adminhtml/ConfigSaveAfter.php +++ b/Observer/Adminhtml/ConfigSaveAfter.php @@ -14,8 +14,7 @@ class ConfigSaveAfter implements \Magento\Framework\Event\ObserverInterface ]; public const CONFIGS_PATHS_TO_TRACK = [ \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE, - \Magento\Directory\Helper\Data::XML_PATH_DEFAULT_COUNTRY, - \Afterpay\Afterpay\Model\Config::XML_PATH_PAYPAL_MERCHANT_COUNTRY + \Magento\Directory\Helper\Data::XML_PATH_DEFAULT_COUNTRY ]; public function __construct( diff --git a/Plugin/Sales/Model/Service/CreditmemoService/AdjustmentAmountValidation.php b/Plugin/Sales/Model/Service/CreditmemoService/AdjustmentAmountValidation.php new file mode 100644 index 0000000..613e15b --- /dev/null +++ b/Plugin/Sales/Model/Service/CreditmemoService/AdjustmentAmountValidation.php @@ -0,0 +1,39 @@ +getOrder(); + if (($creditmemo->getBaseAdjustmentPositive() != 0 || $creditmemo->getBaseAdjustmentNegative() != 0) + && $order->getPayment()->getMethod() === Config::CODE + && !in_array( + $order->getPayment()->getAdditionalInformation(AdditionalInformationInterface::AFTERPAY_PAYMENT_STATE), + self::ALLOWED_PAYMENT_STATES + )) { + throw new LocalizedException(__( + 'You cannot use adjustments for a payment with a status' + . ' that does not equal "CAPTURED" or "PARTIALLY_CAPTURED" for the current payment method.' + )); + } + + return [$creditmemo, $offlineRequested]; + } +} diff --git a/composer.json b/composer.json index 073f0fe..61574c9 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "license": "Apache-2.0", "type": "magento2-module", "description": "Magento 2 Afterpay Payment Module", - "version": "4.3.1", + "version": "4.3.2", "require": { "php": "~7.1.3||~7.2.0||~7.3.0||~7.4.0", "magento/framework": "^102.0", diff --git a/etc/di.xml b/etc/di.xml index fd06ec6..4326146 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -382,6 +382,10 @@ + + + diff --git a/etc/module.xml b/etc/module.xml index 3a96f0d..97346da 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,7 +1,7 @@ - + diff --git a/view/frontend/web/js/view/payment/info/checkout-note.js b/view/frontend/web/js/view/payment/info/checkout-note.js index 145f52c..91b25c7 100644 --- a/view/frontend/web/js/view/payment/info/checkout-note.js +++ b/view/frontend/web/js/view/payment/info/checkout-note.js @@ -50,7 +50,7 @@ define([ }, _getWidgetAmount: function (totals) { let amount = window.checkoutConfig.payment.afterpay.isCBTCurrency - ? totals.grand_total + totals.tax_amount + ? totals.grand_total : totals.base_grand_total; let currency = window.checkoutConfig.payment.afterpay.isCBTCurrency ? totals.quote_currency_code diff --git a/view/frontend/web/js/view/payment/method-renderer/afterpay.js b/view/frontend/web/js/view/payment/method-renderer/afterpay.js index 2858e2a..d0b2f27 100755 --- a/view/frontend/web/js/view/payment/method-renderer/afterpay.js +++ b/view/frontend/web/js/view/payment/method-renderer/afterpay.js @@ -83,31 +83,31 @@ define([ }, _getCheckoutUrl: function (checkoutUrl) { - let deviceData=$.mage.cookies.get("apt_pixel"); - let args=""; + let deviceData = $.mage.cookies.get("apt_pixel"); + let args = ""; // Append params from the cookie - if (deviceData !== undefined && deviceData !=null && deviceData.length>0) { + if (deviceData !== undefined && deviceData !== null && deviceData.length > 0) { - let queryParams=checkoutUrl.split("?")[1]; + let queryParams = checkoutUrl.split("?")[1]; const searchParams = new URLSearchParams(queryParams); - let device=JSON.parse(atob(deviceData)); + let device = JSON.parse(atob(deviceData)); if (device.hasOwnProperty('deviceId') && (/^[0-9a-z-]*$/i).test(device.deviceId) && - searchParams.has('device_id')===false) { - args="&device_id="+device.deviceId; + searchParams.has('device_id') === false) { + args = "&device_id=" + device.deviceId; } - if (device.hasOwnProperty('checkout') ) { - for (var prop in device.checkout){ - let val=device.checkout[prop]; - if ((/^[0-9a-z]+$/i).test(prop) && (/^[0-9a-z-]*$/i).test(val) && searchParams.has(prop)===false) { - args+="&"+prop+"="+val; + if (device.hasOwnProperty('checkout')) { + for (let prop in device.checkout) { + let val = device.checkout[prop]; + if ((/^[0-9a-z]+$/i).test(prop) && (/^[0-9a-z-]*$/i).test(val) && searchParams.has(prop) === false) { + args += "&" + prop + "=" + val; } } } } - return checkoutUrl+args; + return checkoutUrl + args; }, }); });