diff --git a/Block/Adminhtml/System/Config/Fieldset/AllowedByCountry.php b/Block/Adminhtml/System/Config/Fieldset/AllowedByCountry.php
new file mode 100644
index 0000000..72ba55c
--- /dev/null
+++ b/Block/Adminhtml/System/Config/Fieldset/AllowedByCountry.php
@@ -0,0 +1,48 @@
+afterpay = $afterpay;
+ $this->config = $config;
+ $this->allowedCountriesConfigPath = $allowedCountriesConfigPath;
+ }
+
+ public function render(\Magento\Framework\Data\Form\Element\AbstractElement $element): string
+ {
+ $allowedMerchantCountries = explode(',', $this->afterpay->getConfigData($this->allowedCountriesConfigPath));
+ if (in_array($this->getMerchantCountry(), $allowedMerchantCountries)) {
+ return parent::render($element);
+ }
+ return '';
+ }
+
+ private function getMerchantCountry(): ?string
+ {
+ /** @var \Magento\Config\Block\System\Config\Form $fieldSetForm */
+ $fieldSetForm = $this->getForm();
+ $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/Controller/Adminhtml/MerchantConfiguration/Update.php b/Controller/Adminhtml/MerchantConfiguration/Update.php
index 6082a47..dd28849 100644
--- a/Controller/Adminhtml/MerchantConfiguration/Update.php
+++ b/Controller/Adminhtml/MerchantConfiguration/Update.php
@@ -40,6 +40,8 @@ public function execute()
(string)__('Afterpay merchant configuration fetching is failed. See logs.')
);
}
- return $this->resultJsonFactory->create();
+ return $this->resultJsonFactory->create()->setData([
+ 'done' => true
+ ]);
}
}
diff --git a/Controller/Express/PlaceOrder.php b/Controller/Express/PlaceOrder.php
index a7bf0bf..1bd037d 100644
--- a/Controller/Express/PlaceOrder.php
+++ b/Controller/Express/PlaceOrder.php
@@ -54,7 +54,11 @@ public function execute()
? $e->getMessage()
: (string)__('Payment is failed');
$this->messageManager->addErrorMessage($errorMessage);
- return $jsonResult;
+ return $jsonResult->setData(['redirectUrl' => $this->url->getUrl(
+ 'checkout/cart',
+ ['_scope' => $quote->getStore()]
+ )]
+ );
}
return $jsonResult->setData(['redirectUrl' => $this->url->getUrl('checkout/onepage/success')]);
diff --git a/Gateway/Command/GetMerchantConfigurationCommandWrapper.php b/Gateway/Command/GetMerchantConfigurationCommandWrapper.php
index 827fbff..f572426 100644
--- a/Gateway/Command/GetMerchantConfigurationCommandWrapper.php
+++ b/Gateway/Command/GetMerchantConfigurationCommandWrapper.php
@@ -47,10 +47,12 @@ public function execute(array $commandSubject)
return null;
}
$this->checkCountry($scope, $websiteId);
+ $this->checkCurrency($scope, $websiteId);
$this->debugLogger->setForceDebug($this->afterpayConfig->getIsDebug($websiteId));
return $this->merchantConfigurationCommand->execute($commandSubject);
} catch (\Magento\Payment\Gateway\Command\CommandException $e) {
$this->eraseMerchantConfiguration($websiteId, $websiteHasOwnConfig);
+ $this->logger->notice($e->getMessage());
throw $e;
} catch (\Exception $e) {
$this->logger->critical($e->getMessage());
@@ -83,7 +85,26 @@ private function checkCountry(string $scope, int $websiteId): void
if (!in_array($merchantCountry, $allowedCountries)) {
throw new \Magento\Payment\Gateway\Command\CommandException(
// @codingStandardsIgnoreLine
- __('Unable to fetch Afterpay merchant configuration due to unsupported merchant country. Supported countries: AU, CA, NZ, US.')
+ __('Unable to fetch Afterpay merchant configuration due to unsupported merchant country. Supported countries: %1.', implode(', ', $allowedCountries))
+ );
+ }
+ }
+
+ /**
+ * @throws \Magento\Payment\Gateway\Command\CommandException
+ */
+ private function checkCurrency(string $scope, int $websiteId): void
+ {
+
+ $merchantCurrency = $this->afterpayConfig->getMerchantCurrency(
+ $scope,
+ $websiteId
+ );
+ $allowedCurrencies = $this->afterpayConfig->getAllowedCurrencies($websiteId);
+ if (!in_array($merchantCurrency, $allowedCurrencies)) {
+ throw new \Magento\Payment\Gateway\Command\CommandException(
+ // @codingStandardsIgnoreLine
+ __('Unable to fetch Afterpay merchant configuration due to unsupported merchant currency. Supported currencies: %1.', implode(', ', $allowedCurrencies))
);
}
}
diff --git a/Gateway/Request/Checkout/CheckoutDataBuilder.php b/Gateway/Request/Checkout/CheckoutDataBuilder.php
index 0ba434b..020c759 100644
--- a/Gateway/Request/Checkout/CheckoutDataBuilder.php
+++ b/Gateway/Request/Checkout/CheckoutDataBuilder.php
@@ -39,7 +39,8 @@ public function build(array $buildSubject): array
'consumer' => [
'givenNames' => $quote->getCustomerFirstname() ?: $billingAddress->getFirstname(),
'surname' => $quote->getCustomerLastname() ?: $billingAddress->getLastname(),
- 'email' => $quote->getCustomerEmail() ?: $billingAddress->getEmail()
+ 'email' => $quote->getCustomerEmail() ?: $billingAddress->getEmail(),
+ 'phoneNumber' => $billingAddress->getTelephone()
],
'billing' => [
'name' => $billingAddress->getFirstname() . ' ' . $billingAddress->getLastname(),
@@ -48,7 +49,8 @@ public function build(array $buildSubject): array
'area1' => $billingAddress->getCity(),
'region' => $billingAddress->getRegion(),
'postcode' => $billingAddress->getPostcode(),
- 'countryCode' => $billingAddress->getCountryId()
+ 'countryCode' => $billingAddress->getCountryId(),
+ 'phoneNumber' => $billingAddress->getTelephone()
],
'items' => $this->getItems($quote),
'merchant' => [
@@ -61,7 +63,8 @@ public function build(array $buildSubject): array
$billingAddress->getBaseTaxAmount() ?: $shippingAddress->getBaseTaxAmount()
),
'currency' => $quote->getBaseCurrencyCode()
- ]
+ ],
+ 'purchaseCountry' => $billingAddress->getCountryId()
];
if ($shippingAddress = $this->getShippingAddress($quote)) {
@@ -135,9 +138,7 @@ protected function getItemsImages(array $items): array
)->create();
$products = $this->productRepository->getList($searchCriteria)->getItems();
- foreach ($items as $item) {
- /** @var \Magento\Catalog\Model\Product $product */
- $product = $products[$item->getProduct()->getId()];
+ foreach ($products as $product) {
$medialGalleryImages = $product->getMediaGalleryImages();
$itemsImages[$product->getId()] = $medialGalleryImages->getFirstItem();
}
@@ -157,7 +158,8 @@ protected function getShippingAddress(\Magento\Quote\Model\Quote $quote): ?array
'area1' => $shippingAddress->getCity(),
'region' => $shippingAddress->getRegion(),
'postcode' => $shippingAddress->getPostcode(),
- 'countryCode' => $shippingAddress->getCountryId()
+ 'countryCode' => $shippingAddress->getCountryId(),
+ 'phoneNumber' => $shippingAddress->getTelephone()
];
}
diff --git a/Gateway/Request/PaymentAction/AuthCaptureDataBuilder.php b/Gateway/Request/PaymentAction/AuthCaptureDataBuilder.php
index b92f4e7..a859fec 100644
--- a/Gateway/Request/PaymentAction/AuthCaptureDataBuilder.php
+++ b/Gateway/Request/PaymentAction/AuthCaptureDataBuilder.php
@@ -24,7 +24,7 @@ public function build(array $buildSubject): array
'orderId' => $afterpayOrderId,
'amount' => [
'amount' => $this->formatPrice(SubjectReader::readAmount($buildSubject)),
- 'currency' => $payment->getOrder()->getOrderCurrencyCode()
+ 'currency' => $payment->getOrder()->getBaseCurrencyCode()
]
];
}
diff --git a/Gateway/Request/PaymentAction/CaptureDataBuilder.php b/Gateway/Request/PaymentAction/CaptureDataBuilder.php
index 6423b96..8af7fdc 100644
--- a/Gateway/Request/PaymentAction/CaptureDataBuilder.php
+++ b/Gateway/Request/PaymentAction/CaptureDataBuilder.php
@@ -18,7 +18,7 @@ public function build(array $buildSubject): array
if ($payment->getAdditionalInformation('afterpay_express')) {
$data['amount'] = [
'amount' => \Magento\Payment\Gateway\Helper\SubjectReader::readAmount($buildSubject),
- 'currency' => $payment->getOrder()->getOrderCurrencyCode()
+ 'currency' => $payment->getOrder()->getBaseCurrencyCode()
];
}
diff --git a/Gateway/Response/Checkout/CheckoutDataToQuoteHandler.php b/Gateway/Response/Checkout/CheckoutDataToQuoteHandler.php
index 0df0930..2a16c17 100644
--- a/Gateway/Response/Checkout/CheckoutDataToQuoteHandler.php
+++ b/Gateway/Response/Checkout/CheckoutDataToQuoteHandler.php
@@ -53,7 +53,9 @@ public function handle(array $handlingSubject, array $response): void
$shippingInformation->setBillingAddress($address);
if (!$quote->isVirtual()) {
- [$carrierCode, $methodCode] = explode('_', $response['shippingOptionIdentifier']);
+ $explodedShippingOption = explode('_', $response['shippingOptionIdentifier']);
+ $carrierCode = array_shift($explodedShippingOption);
+ $methodCode = implode('_', $explodedShippingOption);
$shippingInformation->setShippingAddress($address);
$shippingInformation->setShippingCarrierCode($carrierCode);
$shippingInformation->setShippingMethodCode($methodCode);
diff --git a/Gateway/Response/Checkout/CheckoutItemsAmountValidationHandler.php b/Gateway/Response/Checkout/CheckoutItemsAmountValidationHandler.php
index d74c6b8..7efd432 100644
--- a/Gateway/Response/Checkout/CheckoutItemsAmountValidationHandler.php
+++ b/Gateway/Response/Checkout/CheckoutItemsAmountValidationHandler.php
@@ -11,7 +11,7 @@ public function handle(array $handlingSubject, array $response)
/** @var \Magento\Quote\Model\Quote $quote */
$quote = $paymentDO->getPayment()->getQuote();
- if (round($quote->getBaseGrandTotal(), 2) != round($response['amount']['amount'], 2)) {
+ if (round(1 * $quote->getBaseGrandTotal(), 2) != round(1 * $response['amount']['amount'], 2)) {
throw new \Magento\Framework\Exception\LocalizedException(
__('There are issues when processing your payment. Invalid Amount')
);
diff --git a/Gateway/Validator/StockItemsValidator.php b/Gateway/Validator/StockItemsValidator.php
new file mode 100644
index 0000000..7427a15
--- /dev/null
+++ b/Gateway/Validator/StockItemsValidator.php
@@ -0,0 +1,54 @@
+isSingleSourceMode = $isSingleSourceMode;
+ $this->defaultSourceProvider = $defaultSourceProvider;
+ $this->getItemsToDeductFromShipment = $getItemsToDeductFromShipment;
+ $this->sourceDeductionRequestFromShipmentFactory = $sourceDeductionRequestFromShipmentFactory;
+ $this->sourceValidatorService = $sourceValidatorService;
+ }
+
+ /**
+ * @inheridoc
+ */
+ public function validate(\Magento\Sales\Model\Order\Shipment $shipment): void
+ {
+ if ($shipment->getOrigData('entity_id')) {
+ return;
+ }
+
+ if (!empty($shipment->getExtensionAttributes())
+ && !empty($shipment->getExtensionAttributes()->getSourceCode())) {
+ $sourceCode = $shipment->getExtensionAttributes()->getSourceCode();
+ } elseif ($this->isSingleSourceMode->execute()) {
+ $sourceCode = $this->defaultSourceProvider->getCode();
+ }
+
+ $shipmentItems = $this->getItemsToDeductFromShipment->execute($shipment);
+
+ if (!empty($shipmentItems)) {
+ $sourceDeductionRequest = $this->sourceDeductionRequestFromShipmentFactory->execute(
+ $shipment,
+ $sourceCode,
+ $shipmentItems
+ );
+ $this->sourceValidatorService->execute($sourceDeductionRequest);
+ }
+ }
+}
diff --git a/Model/CheckoutConfigProvider.php b/Model/CheckoutConfigProvider.php
new file mode 100644
index 0000000..b5b8b7f
--- /dev/null
+++ b/Model/CheckoutConfigProvider.php
@@ -0,0 +1,25 @@
+localeResolver = $localeResolver;
+ }
+
+ public function getConfig(): array
+ {
+ return [
+ 'payment' => [
+ 'afterpay' => [
+ 'locale' => $this->localeResolver->getLocale()
+ ]
+ ]
+ ];
+ }
+}
diff --git a/Model/Config.php b/Model/Config.php
index 655a524..05b7247 100644
--- a/Model/Config.php
+++ b/Model/Config.php
@@ -26,6 +26,7 @@ class Config
const XML_PATH_ALLOW_SPECIFIC_COUNTRIES = 'payment/afterpay/allowspecific';
const XML_PATH_SPECIFIC_COUNTRIES = 'payment/afterpay/specificcountry';
const XML_PATH_ALLOWED_MERCHANT_COUNTRIES = 'payment/afterpay/allowed_merchant_countries';
+ const XML_PATH_ALLOWED_MERCHANT_CURRENCIES = 'payment/afterpay/allowed_merchant_currencies';
const XML_PATH_PAYPAL_MERCHANT_COUNTRY = 'paypal/general/merchant_country';
private $scopeConfig;
@@ -241,6 +242,22 @@ public function getAllowedCountries(?int $scopeCode = null): array
return [];
}
+ /**
+ * @return string[]
+ */
+ public function getAllowedCurrencies(?int $scopeCode = null): array
+ {
+ $specificCountries = $this->scopeConfig->getValue(
+ self::XML_PATH_ALLOWED_MERCHANT_CURRENCIES,
+ ScopeInterface::SCOPE_WEBSITE,
+ $scopeCode
+ );
+ if ($specificCountries != null) {
+ return explode(",", $specificCountries);
+ }
+ return [];
+ }
+
/**
* @return string[]
*/
@@ -301,6 +318,17 @@ public function getMerchantCountry(
return null;
}
+ public function getMerchantCurrency(
+ string $scope = ScopeInterface::SCOPE_WEBSITES,
+ ?int $scopeCode = null
+ ): ?string {
+ return $this->scopeConfig->getValue(
+ \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE,
+ $scope,
+ $scopeCode
+ );
+ }
+
public function getByConfigPath(?string $path, ?int $scopeCode = null): ?string
{
if (!$path) {
diff --git a/Model/Order/Shipment/CaptureProcessor.php b/Model/Order/Shipment/CaptureProcessor.php
index 1bdbd68..b353d6f 100644
--- a/Model/Order/Shipment/CaptureProcessor.php
+++ b/Model/Order/Shipment/CaptureProcessor.php
@@ -11,16 +11,19 @@ class CaptureProcessor
private $paymentDataObjectFactory;
private $authExpiryDate;
private $shipmentAmountProcessor;
+ private $stockItemsValidator;
public function __construct(
\Magento\Payment\Gateway\CommandInterface $authCaptureCommand,
\Magento\Payment\Gateway\Data\PaymentDataObjectFactoryInterface $paymentDataObjectFactory,
\Afterpay\Afterpay\Model\Order\Payment\Auth\ExpiryDate $authExpiryDate,
+ \Afterpay\Afterpay\Model\Spi\StockItemsValidatorInterface $stockItemsValidator,
\Afterpay\Afterpay\Model\Payment\AmountProcessor\Shipment $shipmentAmountProcessor
) {
$this->authCaptureCommand = $authCaptureCommand;
$this->paymentDataObjectFactory = $paymentDataObjectFactory;
$this->authExpiryDate = $authExpiryDate;
+ $this->stockItemsValidator = $stockItemsValidator;
$this->shipmentAmountProcessor = $shipmentAmountProcessor;
}
@@ -43,7 +46,7 @@ public function execute(\Magento\Sales\Model\Order\Shipment $shipment): void
if ($amountToCaptureExists && $correctStateForAuthCapture) {
$this->validateAuthExpiryDate($additionalInfo[AdditionalInformationInterface::AFTERPAY_AUTH_EXPIRY_DATE]);
-
+ $this->stockItemsValidator->validate($shipment);
$amountToCapture = $this->shipmentAmountProcessor->process($shipment->getItemsCollection(), $payment);
if ($amountToCapture > 0) {
diff --git a/Model/Payment/AmountProcessor/CreditMemo.php b/Model/Payment/AmountProcessor/CreditMemo.php
index bf53d5b..1a41659 100644
--- a/Model/Payment/AmountProcessor/CreditMemo.php
+++ b/Model/Payment/AmountProcessor/CreditMemo.php
@@ -160,7 +160,7 @@ private function processRolloverDiscountForVoidAmount(
private function calculateItemPrice(\Magento\Sales\Model\Order\Creditmemo\Item $item, float $qty): float
{
$discountPerItem = $item->getBaseDiscountAmount() / $item->getQty();
- $pricePerItem = $item->getBaseRowTotalInclTax() / $item->getQty();
+ $pricePerItem = ($item->getBaseRowTotal() + $item->getBaseTaxAmount()) / $item->getQty();
return $qty * ($pricePerItem - $discountPerItem);
}
diff --git a/Model/Payment/AmountProcessor/Order.php b/Model/Payment/AmountProcessor/Order.php
index 6f29d3e..0588378 100644
--- a/Model/Payment/AmountProcessor/Order.php
+++ b/Model/Payment/AmountProcessor/Order.php
@@ -23,7 +23,7 @@ public function process(array $items, \Magento\Sales\Model\Order\Payment $paymen
protected function calculateItemPrice(\Magento\Sales\Model\Order\Item $item, float $qty): float
{
$discountPerItem = $item->getBaseDiscountAmount() / $item->getQtyOrdered();
- $pricePerItem = $item->getBaseRowTotalInclTax() / $item->getQtyOrdered();
+ $pricePerItem = ($item->getBaseRowTotal() + $item->getBaseTaxAmount()) / $item->getQtyOrdered();
return $qty * ($pricePerItem - $discountPerItem);
}
diff --git a/Model/SourceValidatorService.php b/Model/SourceValidatorService.php
new file mode 100644
index 0000000..ebf4b1c
--- /dev/null
+++ b/Model/SourceValidatorService.php
@@ -0,0 +1,51 @@
+getSourceItemBySourceCodeAndSku = $getSourceItemBySourceCodeAndSku;
+ $this->getStockItemConfiguration = $getStockItemConfiguration;
+ $this->getStockBySalesChannel = $getStockBySalesChannel;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function execute(\Magento\InventorySourceDeductionApi\Model\SourceDeductionRequestInterface $sourceDeductionRequest): void
+ {
+ $sourceCode = $sourceDeductionRequest->getSourceCode();
+ $salesChannel = $sourceDeductionRequest->getSalesChannel();
+
+ $stockId = $this->getStockBySalesChannel->execute($salesChannel)->getStockId();
+ foreach ($sourceDeductionRequest->getItems() as $item) {
+ $itemSku = $item->getSku();
+ $qty = $item->getQty();
+ $stockItemConfiguration = $this->getStockItemConfiguration->execute(
+ $itemSku,
+ $stockId
+ );
+
+ if (!$stockItemConfiguration->isManageStock()) {
+ //We don't need to Manage Stock
+ continue;
+ }
+
+ $sourceItem = $this->getSourceItemBySourceCodeAndSku->execute($sourceCode, $itemSku);
+ if (($sourceItem->getQuantity() - $qty) < 0) {
+ throw new \Magento\Framework\Exception\LocalizedException(
+ __('Not all of your products are available in the requested quantity.')
+ );
+ }
+ }
+ }
+}
diff --git a/Model/Spi/StockItemsValidatorInterface.php b/Model/Spi/StockItemsValidatorInterface.php
new file mode 100644
index 0000000..899e518
--- /dev/null
+++ b/Model/Spi/StockItemsValidatorInterface.php
@@ -0,0 +1,11 @@
+config = $config;
+ }
+
+ protected function buildUrl(): string
+ {
+ return $this->urlBuilder->build(
+ \Afterpay\Afterpay\Model\Url\UrlBuilder::TYPE_WEB_JS_LIB,
+ 'afterpay.js'
+ );
+ }
+}
diff --git a/Model/Url/UrlBuilder.php b/Model/Url/UrlBuilder.php
index 990f886..ca9760d 100644
--- a/Model/Url/UrlBuilder.php
+++ b/Model/Url/UrlBuilder.php
@@ -18,13 +18,13 @@ public function __construct(
public function build(string $type, string $path, array $pathArgs = [], ?int $storeId = null): string
{
- return $this->urlFactory->create($type, $storeId) . $this->replaceArgsInPath($path, $pathArgs);
+ return $this->urlFactory->create($type, $storeId, $pathArgs) . $this->replaceArgsInPath($path, $pathArgs);
}
private function replaceArgsInPath(string $path, array $args): string
{
foreach ($args as $argKey => $argVal) {
- $path = str_replace('{' . $argKey . '}', $argVal, $path);
+ $path = str_replace('{' . $argKey . '}', (string)$argVal, $path);
}
return $path;
}
diff --git a/Model/Url/UrlBuilder/UrlFactory.php b/Model/Url/UrlBuilder/UrlFactory.php
index 77834c1..8f90918 100644
--- a/Model/Url/UrlBuilder/UrlFactory.php
+++ b/Model/Url/UrlBuilder/UrlFactory.php
@@ -18,23 +18,30 @@ public function __construct(
$this->environments = $environments;
}
- public function create(string $type, ?int $storeId = null): string
+ public function create(string $type, ?int $storeId = null, array $pathArgs = []): string
{
- $apiMode = $this->config->getApiMode();
- if (!isset($this->environments[$apiMode][$type]) || !$item = $this->environments[$apiMode][$type]) {
+ $apiMode = $this->config->getApiMode($pathArgs['websiteId'] ?? null);
+ $item = $this->environments[$apiMode][$type] ?? false;
+
+ if (!$item) {
throw new \InvalidArgumentException('Afterpay environment url config is not found');
}
if (is_string($item)) {
return $item;
}
- /** @var \Magento\Store\Model\Store $store */
- $store = $this->storeManager->getStore($storeId);
- $currencyCode = $store->getCurrentCurrencyCode();
-
- if (isset($item[$currencyCode]) && $url = $item[$currencyCode]) {
- return $url;
+ if (isset($pathArgs['websiteId'])) {
+ $currencyCode = $this->config->getMerchantCurrency(
+ \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITES,
+ $pathArgs['websiteId']
+ );
}
- return $item['default'];
+ if (!isset($currencyCode)) {
+ /** @var \Magento\Store\Model\Store $store */
+ $store = $this->storeManager->getStore($storeId);
+ $currencyCode = $store->getCurrentCurrencyCode();
+ }
+
+ return $item[$currencyCode] ?? $item['default'];
}
}
diff --git a/Setup/Patch/Data/AdaptCapturedDiscounts.php b/Setup/Patch/Data/AdaptCapturedDiscounts.php
new file mode 100644
index 0000000..4eab05d
--- /dev/null
+++ b/Setup/Patch/Data/AdaptCapturedDiscounts.php
@@ -0,0 +1,95 @@
+productMetadata = $productMetadata;
+ $this->salesSetup = $salesSetup;
+ $this->json = $json;
+ }
+
+ public static function getDependencies()
+ {
+ return [
+ \Afterpay\Afterpay\Setup\Patch\Data\AdaptPayments::class
+ ];
+ }
+
+ public function getAliases()
+ {
+ return [];
+ }
+
+ public function apply()
+ {
+ if ($this->productMetadata->getEdition() === 'Community') {
+ return;
+ }
+
+ $payments = $this->getAfterpayLegacyPaymentsInfo();
+ $ordersAdditionalInfo = $this->getNewOrdersAdditionalInfo($payments);
+ $this->saveOrdersAdditionalInfo($ordersAdditionalInfo);
+ return $this;
+ }
+
+ private function saveOrdersAdditionalInfo(array $ordersAdditionalInfo): void
+ {
+ foreach ($ordersAdditionalInfo as $orderId => $additionalInfo) {
+ $this->salesSetup->getConnection()->update(
+ $this->salesSetup->getConnection()->getTableName('sales_order_payment'),
+ ['additional_information' => $this->json->serialize($additionalInfo)],
+ ['parent_id = ?' => $orderId]
+ );
+ }
+ }
+
+ private function getNewOrdersAdditionalInfo(array $paymentsInfo): array
+ {
+ $ordersAdditionalInfo = [];
+ foreach ($paymentsInfo as $payment) {
+ /** @var array $additionalInfo */
+ $additionalInfo = $this->json->unserialize($payment['additional_information']);
+ $totalDiscountAmount = ($payment['base_customer_balance_amount'] ?? 0) + ($payment['base_gift_cards_amount'] ?? 0);
+ $additionalInfo[AdditionalInformationInterface::AFTERPAY_CAPTURED_DISCOUNT] = $totalDiscountAmount;
+ if ($additionalInfo[AdditionalInformationInterface::AFTERPAY_PAYMENT_STATE] != PaymentStateInterface::CAPTURED) {
+ $additionalInfo[AdditionalInformationInterface::AFTERPAY_CAPTURED_DISCOUNT] -=
+ $additionalInfo[AdditionalInformationInterface::AFTERPAY_ROLLOVER_DISCOUNT];
+ }
+ $ordersAdditionalInfo[$payment['order_id']] = $additionalInfo;
+ }
+ return $ordersAdditionalInfo;
+ }
+
+ private function getAfterpayLegacyPaymentsInfo(): array
+ {
+ $connection = $this->salesSetup->getConnection();
+ $select = $connection->select()
+ ->from(
+ ['si' => $connection->getTableName('sales_invoice')],
+ ['si.order_id', 'si.base_customer_balance_amount', 'si.base_gift_cards_amount']
+ )->joinInner(
+ ['sop' => $connection->getTableName('sales_order_payment')],
+ 'si.order_id = sop.parent_id AND sop.method = "' . Config::CODE . '"'
+ . ' AND sop.additional_information NOT LIKE "%' . AdditionalInformationInterface::AFTERPAY_CAPTURED_DISCOUNT . '%"',
+ ['sop.additional_information']
+ )->where(
+ 'si.base_customer_balance_amount IS NOT NULL OR si.base_gift_cards_amount IS NOT NULL'
+ );
+ return $connection->fetchAll($select);
+ }
+}
diff --git a/composer.json b/composer.json
index 780b605..0ba9857 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.0.1",
+ "version": "4.0.2",
"require": {
"php": "~7.1.3||~7.2.0||~7.3.0||~7.4.0",
"magento/framework": "^102.0",
diff --git a/etc/adminhtml/di.xml b/etc/adminhtml/di.xml
index 7b8eb30..6f97991 100644
--- a/etc/adminhtml/di.xml
+++ b/etc/adminhtml/di.xml
@@ -6,11 +6,21 @@
Afterpay\Afterpay\Gateway\Config\Config
-
+
Afterpay\Afterpay\Model\Method\MethodFacade
+
+
+ allowed_merchant_countries
+
+
+
+
+ expresscheckout/allowed_merchant_countries
+
+
diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml
index e36ece8..7528669 100644
--- a/etc/adminhtml/system.xml
+++ b/etc/adminhtml/system.xml
@@ -93,6 +93,7 @@
+ Afterpay\Afterpay\Block\Adminhtml\System\Config\Fieldset\ExpressCheckout
Magento\Config\Model\Config\Source\Yesno
diff --git a/etc/config.xml b/etc/config.xml
index d7362a3..7ad7f3b 100644
--- a/etc/config.xml
+++ b/etc/config.xml
@@ -16,10 +16,23 @@
afterpay_token,afterpay_order_id,afterpay_payment_state,afterpay_open_to_capture_amount,afterpay_rollover_discount,afterpay_captured_discount,afterpay_auth_expiry_date
AU,NZ,US,CA
+ AUD,NZD,USD,CAD
1
immediate
shipping,billing,consumer
+
+ AU,NZ,US,CA
+
+
+
+
+
+ 0
+
+
+
+
diff --git a/etc/csp_whitelist.xml b/etc/csp_whitelist.xml
index a554d39..3b7bff3 100644
--- a/etc/csp_whitelist.xml
+++ b/etc/csp_whitelist.xml
@@ -19,6 +19,12 @@
js.sandbox.afterpay.com
js.afterpay.com
+
+
+
+ widgets.sandbox.afterpay.com
+ widgets.sandbox.clearpay.co.uk
+
diff --git a/etc/di.xml b/etc/di.xml
index 97925f1..975085d 100644
--- a/etc/di.xml
+++ b/etc/di.xml
@@ -259,6 +259,11 @@
+
+
+ Afterpay\Afterpay\Model\SourceValidatorService
+
+
Afterpay\Afterpay\Gateway\Config\Config::CODE
@@ -283,6 +288,7 @@
Afterpay\Afterpay\Gateway\Command\AuthCaptureCommand
+ Afterpay\Afterpay\Gateway\Validator\StockItemsValidator
@@ -297,20 +303,12 @@
-
-
-
-
- https://api.us-sandbox.afterpay.com/
- - https://api.us-sandbox.afterpay.com/
- - https://api-sandbox.afterpay.com/
-
+ - https://global-api-sandbox.afterpay.com/
- https://portal.sandbox.afterpay.com/
- https://js.sandbox.afterpay.com/
-
-
-
-
- https://api.us.afterpay.com/
- - https://api.us.afterpay.com/
- - https://api.afterpay.com/
-
+ - https://global-api.afterpay.com/
- https://portal.afterpay.com/
- https://js.afterpay.com/
diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml
index 07f5748..17bfdfe 100644
--- a/etc/frontend/di.xml
+++ b/etc/frontend/di.xml
@@ -27,6 +27,11 @@
Afterpay\Afterpay\Model\Config::XML_PATH_ENABLE_EXPRESS_CHECKOUT_ACTION_PRODUCT
+
+
+ Afterpay\Afterpay\Model\Url\Lib\WidgetCheckoutLibUrlProvider
+
+
Afterpay\Afterpay\Model\Url\Lib\ExpressCheckoutLibUrlProvider
@@ -61,4 +66,11 @@
+
+
+
+ - Afterpay\Afterpay\Model\CheckoutConfigProvider
+
+
+
diff --git a/etc/module.xml b/etc/module.xml
index 14fad89..2cf46ed 100644
--- a/etc/module.xml
+++ b/etc/module.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/i18n/en_US.csv b/i18n/en_US.csv
index ce323f2..c94b04c 100644
--- a/i18n/en_US.csv
+++ b/i18n/en_US.csv
@@ -8,8 +8,7 @@
"4 interest-free instalments of","4 interest-free instalments of"
"Four interest-free payments totalling","Four interest-free payments totalling"
"Continue to Afterpay","Continue to Afterpay"
-"Due today","due yesterday"
-"First instalment","First instalment"
+"First payment","First payment"
"You will be redirected to the Afterpay website to fill out your payment information. You will be redirected back to our site to complete your order.","You will be redirected to the Afterpay website to fill out your payment information. You will be redirected back to our site to complete your order."
"You will be redirected to the Afterpay website when you proceed to checkout.","You will be redirected to the Afterpay website when you proceed to checkout."
"Shipping is unavailable for this address, or all options exceed Afterpay order limit.","Shipping is unavailable for this address, or all options exceed Afterpay order limit."
@@ -23,5 +22,7 @@
"Afterpay merchant configuration fetching is failed. See logs.","Afterpay merchant configuration fetching is failed. See logs."
"Afterpay merchant configuration fetching is success.","Afterpay merchant configuration fetching is success."
"Unable to fetch Afterpay merchant configuration due to unsupported merchant country. Supported countries: AU, CA, NZ, US.","Unable to fetch Afterpay merchant configuration due to unsupported merchant country. Supported countries: AU, CA, NZ, US."
+"Unable to fetch Afterpay merchant configuration due to unsupported merchant currency. Supported countries: %1.","Unable to fetch Afterpay merchant configuration due to unsupported merchant currency. Supported countries: %1."
"Update Merchant Configuration","Update Merchant Configuration"
Update limit configuration and specific countries from Afterpay API.,Update limit configuration and specific countries from Afterpay API.
+"Terms & Conditions","Terms & Conditions"
diff --git a/view/frontend/layout/checkout_index_index.xml b/view/frontend/layout/checkout_index_index.xml
index 6e0ef94..da76703 100644
--- a/view/frontend/layout/checkout_index_index.xml
+++ b/view/frontend/layout/checkout_index_index.xml
@@ -5,6 +5,15 @@
+
+
+
+
+
+ Afterpay\Afterpay\ViewModel\WidgetCheckout\Lib
+
+
+
diff --git a/view/frontend/web/css/afterpay-checkout.less b/view/frontend/web/css/afterpay-checkout.less
index acb3fe9..dbad1cf 100644
--- a/view/frontend/web/css/afterpay-checkout.less
+++ b/view/frontend/web/css/afterpay-checkout.less
@@ -1,4 +1,4 @@
-#afterpay {
+.afterpay.payment-method {
.payment-method-note {
&.afterpay-checkout-note {
font-size: 1.2rem;
@@ -77,7 +77,7 @@
}
@media only screen and (max-width : 1150px) {
- #afterpay {
+ .afterpay.payment-method {
.actions-toolbar-wraper {
.terms-conditions {
width: 57%;
@@ -87,7 +87,7 @@
}
@media only screen and (max-width : 993px) {
- #afterpay {
+ .afterpay.payment-method {
.actions-toolbar-wraper {
.terms-conditions {
width: 100%;
diff --git a/view/frontend/web/js/service/container/cart/abstract-data-retriever.js b/view/frontend/web/js/service/container/cart/abstract-data-retriever.js
index a68e749..593a0c0 100644
--- a/view/frontend/web/js/service/container/cart/abstract-data-retriever.js
+++ b/view/frontend/web/js/service/container/cart/abstract-data-retriever.js
@@ -19,7 +19,7 @@ define([
},
_updateContainerModel: function (modelWithPrice) {
const containerModel = containerModelHolder.getModel(this.modelContainerId);
- if (modelWithPrice[this.priceKey] && parseInt(modelWithPrice[this.priceKey]) > 0) {
+ if (modelWithPrice[this.priceKey] && parseFloat(modelWithPrice[this.priceKey]) > 0) {
containerModel.setPrice(modelWithPrice[this.priceKey]);
} else {
containerModel.setPrice(0);
diff --git a/view/frontend/web/js/view/container/express-checkout/button.js b/view/frontend/web/js/view/container/express-checkout/button.js
index addca9e..36205c7 100644
--- a/view/frontend/web/js/view/container/express-checkout/button.js
+++ b/view/frontend/web/js/view/container/express-checkout/button.js
@@ -80,9 +80,9 @@ define([
).done(function (response) {
if (response && response.redirectUrl) {
$.mage.redirect(response.redirectUrl);
+ } else {
+ $(document.body).trigger('processStop');
}
- }).always(function () {
- $(document.body).trigger('processStop');
});
}
},
@@ -96,7 +96,8 @@ define([
_getIsVisible: function () {
const floatMaxOrderTotal = parseFloat(this.maxOrderTotal);
const floatMinOrderTotal = parseFloat(this.minOrderTotal);
- return (window.AfterPay !== undefined && this.isProductAllowed() &&
+
+ return (this.countryCode && window.AfterPay !== undefined && this.isProductAllowed() &&
!(this.currentPrice() > floatMaxOrderTotal || this.currentPrice() < floatMinOrderTotal) &&
!this._getIsVirtual()) && this._super();
}
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 ebaebbc..8eff7a3 100644
--- a/view/frontend/web/js/view/payment/info/checkout-note.js
+++ b/view/frontend/web/js/view/payment/info/checkout-note.js
@@ -9,53 +9,30 @@ define([
quote,
priceUtils,
totals,
- $t
) {
'use strict';
return Component.extend({
- getFirstInstalmentText: function () {
- let afterpayFirstInstalmentText = '';
-
- switch(totals.totals().quote_currency_code){
- case 'USD':
- case 'CAD':
- afterpayFirstInstalmentText = $t('Due today');
- break;
- default:
- afterpayFirstInstalmentText = $t('First instalment');
-
- }
- return afterpayFirstInstalmentText;
+ initWidget: function () {
+ window.afterpayWidget = new AfterPay.Widgets.PaymentSchedule({
+ target: '#afterpay-widget-container',
+ locale: window.checkoutConfig.payment.afterpay.locale.replace('_', '-'),
+ amount: this._getWidgetAmount(totals.totals()),
+ onError: (event) => console.log(event.data.error)
+ })
+ totals.totals.subscribe((totals) => {
+ if (afterpayWidget) {
+ afterpayWidget.update({
+ amount: this._getWidgetAmount(totals),
+ })
+ }
+ });
},
- getCheckoutText: function() {
- let afterpayCheckoutText = '';
- switch(totals.totals().quote_currency_code){
- case 'USD':
- afterpayCheckoutText = $t('4 interest-free installments of');
- break;
- case 'CAD':
- afterpayCheckoutText = $t('4 interest-free instalments of');
- break;
- default:
- afterpayCheckoutText = $t('Four interest-free payments totalling');
+ _getWidgetAmount: function (totals) {
+ return {
+ amount: parseFloat(totals.base_grand_total).toFixed(2),
+ currency: totals.base_currency_code
}
- return afterpayCheckoutText;
- },
- getAfterpayTotalAmount: function () {
- let amount = totals.totals().base_grand_total;
- switch(totals.totals().quote_currency_code){
- case 'USD':
- case 'CAD':
- amount = totals.totals().base_grand_total / 4;
- }
- return priceUtils.formatPrice(amount.toFixed(2), quote.getPriceFormat());
- },
- getInstallmentAmount: function () {
- return priceUtils.formatPrice((totals.totals().base_grand_total / 4).toFixed(2), quote.getPriceFormat());
- },
- getLastInstallmentAmount: function () {
- return priceUtils.formatPrice(totals.totals().base_grand_total - 3 * (totals.totals().base_grand_total / 4).toFixed(2), quote.getPriceFormat());
}
});
});
diff --git a/view/frontend/web/template/payment/afterpay.html b/view/frontend/web/template/payment/afterpay.html
index 4316e5c..d762cbf 100644
--- a/view/frontend/web/template/payment/afterpay.html
+++ b/view/frontend/web/template/payment/afterpay.html
@@ -1,4 +1,4 @@
-
+
-
-
-
-
+
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
-
-