From e83692997077576852b863fcbafc90b8c460170f Mon Sep 17 00:00:00 2001
From: afterpayplugins <53248085+afterpayplugins@users.noreply.github.com>
Date: Wed, 18 Oct 2023 17:17:15 +1100
Subject: [PATCH] Afterpay Release v5.2.1
---
Api/Data/TokenInterface.php | 10 ++
Controller/Express/PlaceOrder.php | 7 +-
Controller/Payment/Capture.php | 44 ++++---
Model/Checks/PaymentMethod.php | 2 +-
Model/Checks/PaymentMethodInterface.php | 8 +-
Model/Config.php | 6 +-
Model/GraphQl/Resolver/AfterpayConfig.php | 2 +
Model/Order/CreditMemo/OrdersRetriever.php | 114 +++++++-----------
Model/Order/Payment/Auth/TokenSaver.php | 25 ++++
Model/Order/Payment/Auth/TokenValidator.php | 23 ++--
Model/Payment/Capture/PlaceOrderProcessor.php | 33 ++++-
.../NotAllowedProductsProvider.php | 15 +--
Model/ResourceModel/Token.php | 41 +++++++
Model/ResourceModel/Token/Collection.php | 18 +++
Model/Token.php | 18 +++
Plugin/Quote/CheckoutManagement.php | 39 ++++++
Setup/Patch/Data/AdaptCapturedDiscounts.php | 6 +-
Setup/Patch/Data/MigrateTokens.php | 73 +++++++++++
composer.json | 2 +-
etc/db_schema.xml | 22 ++++
etc/db_schema_whitelist.json | 16 +++
etc/di.xml | 4 +
etc/module.xml | 2 +-
etc/schema.graphqls | 1 +
.../frontend/web/js/view/container/cta/cta.js | 3 +-
.../view/container/express-checkout/button.js | 49 ++++++--
26 files changed, 453 insertions(+), 130 deletions(-)
create mode 100644 Api/Data/TokenInterface.php
create mode 100644 Model/Order/Payment/Auth/TokenSaver.php
create mode 100644 Model/ResourceModel/Token.php
create mode 100644 Model/ResourceModel/Token/Collection.php
create mode 100644 Model/Token.php
create mode 100644 Plugin/Quote/CheckoutManagement.php
create mode 100644 Setup/Patch/Data/MigrateTokens.php
create mode 100644 etc/db_schema.xml
create mode 100644 etc/db_schema_whitelist.json
diff --git a/Api/Data/TokenInterface.php b/Api/Data/TokenInterface.php
new file mode 100644
index 0000000..0c7f4e9
--- /dev/null
+++ b/Api/Data/TokenInterface.php
@@ -0,0 +1,10 @@
+request = $request;
$this->checkoutSession = $checkoutSession;
@@ -36,6 +38,7 @@ public function __construct(
$this->placeOrderProcessor = $placeOrderProcessor;
$this->syncCheckoutDataCommand = $syncCheckoutDataCommand;
$this->storeManager = $storeManager;
+ $this->messageManager = $messageManager;
}
public function execute()
@@ -74,6 +77,8 @@ public function execute()
]);
}
+ $this->messageManager->addSuccessMessage((string)__('Afterpay Transaction Completed.'));
+
return $jsonResult->setData(['redirectUrl' => $this->storeManager->getStore()->getUrl('checkout/onepage/success')]);
}
}
diff --git a/Controller/Payment/Capture.php b/Controller/Payment/Capture.php
index 4d94991..079e4db 100644
--- a/Controller/Payment/Capture.php
+++ b/Controller/Payment/Capture.php
@@ -2,25 +2,33 @@
namespace Afterpay\Afterpay\Controller\Payment;
-class Capture implements \Magento\Framework\App\Action\HttpGetActionInterface
+use Afterpay\Afterpay\Model\Payment\Capture\PlaceOrderProcessor;
+use Magento\Checkout\Model\Session;
+use Magento\Framework\App\Action\HttpGetActionInterface;
+use Magento\Framework\App\RequestInterface;
+use Magento\Framework\Controller\Result\RedirectFactory;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\Message\ManagerInterface;
+use Magento\Payment\Gateway\CommandInterface;
+
+class Capture implements HttpGetActionInterface
{
const CHECKOUT_STATUS_CANCELLED = 'CANCELLED';
const CHECKOUT_STATUS_SUCCESS = 'SUCCESS';
-
- private \Magento\Framework\App\RequestInterface $request;
- private \Magento\Checkout\Model\Session $session;
- private \Magento\Framework\Controller\Result\RedirectFactory $redirectFactory;
- private \Magento\Framework\Message\ManagerInterface $messageManager;
- private \Afterpay\Afterpay\Model\Payment\Capture\PlaceOrderProcessor $placeOrderProcessor;
- private \Magento\Payment\Gateway\CommandInterface $validateCheckoutDataCommand;
+ private RequestInterface $request;
+ private Session $session;
+ private RedirectFactory $redirectFactory;
+ private ManagerInterface $messageManager;
+ private PlaceOrderProcessor $placeOrderProcessor;
+ private CommandInterface $validateCheckoutDataCommand;
public function __construct(
- \Magento\Framework\App\RequestInterface $request,
- \Magento\Checkout\Model\Session $session,
- \Magento\Framework\Controller\Result\RedirectFactory $redirectFactory,
- \Magento\Framework\Message\ManagerInterface $messageManager,
- \Afterpay\Afterpay\Model\Payment\Capture\PlaceOrderProcessor $placeOrderProcessor,
- \Magento\Payment\Gateway\CommandInterface $validateCheckoutDataCommand
+ RequestInterface $request,
+ Session $session,
+ RedirectFactory $redirectFactory,
+ ManagerInterface $messageManager,
+ PlaceOrderProcessor $placeOrderProcessor,
+ CommandInterface $validateCheckoutDataCommand
) {
$this->request = $request;
$this->session = $session;
@@ -36,12 +44,14 @@ public function execute()
$this->messageManager->addErrorMessage(
(string)__('You have cancelled your Afterpay payment. Please select an alternative payment method.')
);
+
return $this->redirectFactory->create()->setPath('checkout/cart');
}
if ($this->request->getParam('status') != self::CHECKOUT_STATUS_SUCCESS) {
$this->messageManager->addErrorMessage(
(string)__('Afterpay payment is declined. Please select an alternative payment method.')
);
+
return $this->redirectFactory->create()->setPath('checkout/cart');
}
@@ -50,14 +60,16 @@ public function execute()
$afterpayOrderToken = $this->request->getParam('orderToken');
$this->placeOrderProcessor->execute($quote, $this->validateCheckoutDataCommand, $afterpayOrderToken);
} catch (\Throwable $e) {
- $errorMessage = $e instanceof \Magento\Framework\Exception\LocalizedException
+ $errorMessage = $e instanceof LocalizedException
? $e->getMessage()
: (string)__('Afterpay payment is declined. Please select an alternative payment method.');
$this->messageManager->addErrorMessage($errorMessage);
+
return $this->redirectFactory->create()->setPath('checkout/cart');
}
- $this->messageManager->addSuccessMessage((string)__('Afterpay Transaction Completed'));
+ $this->messageManager->addSuccessMessage((string)__('Afterpay Transaction Completed.'));
+
return $this->redirectFactory->create()->setPath('checkout/onepage/success');
}
}
diff --git a/Model/Checks/PaymentMethod.php b/Model/Checks/PaymentMethod.php
index 5dc328e..94c5cff 100644
--- a/Model/Checks/PaymentMethod.php
+++ b/Model/Checks/PaymentMethod.php
@@ -4,7 +4,7 @@
class PaymentMethod implements PaymentMethodInterface
{
- public function isAfterPayMethod(\Magento\Sales\Model\Order\Payment $payment): bool
+ public function isAfterPayMethod($payment): bool
{
return $payment->getMethod() == \Afterpay\Afterpay\Gateway\Config\Config::CODE;
}
diff --git a/Model/Checks/PaymentMethodInterface.php b/Model/Checks/PaymentMethodInterface.php
index d52399f..ab3a22d 100644
--- a/Model/Checks/PaymentMethodInterface.php
+++ b/Model/Checks/PaymentMethodInterface.php
@@ -1,8 +1,12 @@
resourceConnection->getConnection();
- $coreConfigData = $connection->getTableName('core_config_data');
+ $coreConfigData = $this->resourceConnection->getTableName('core_config_data');
$configsExistToCheck = array_merge(
\Afterpay\Afterpay\Observer\Adminhtml\ConfigSaveAfter::AFTERPAY_CONFIGS,
\Afterpay\Afterpay\Observer\Adminhtml\ConfigSaveAfter::CONFIGS_PATHS_TO_TRACK
@@ -555,9 +555,9 @@ public function setConsumerLendingMinAmount(string $value, int $scopeId = 0): se
return $this;
}
- public function getConsumerLendingMinAmount(?int $scopeCode = null): bool
+ public function getConsumerLendingMinAmount(?int $scopeCode = null): float
{
- return (bool)$this->scopeConfig->getValue(
+ return (float)$this->scopeConfig->getValue(
self::XML_PATH_CONSUMER_LENDING_MIN_AMOUNT,
ScopeInterface::SCOPE_WEBSITE,
$scopeCode
diff --git a/Model/GraphQl/Resolver/AfterpayConfig.php b/Model/GraphQl/Resolver/AfterpayConfig.php
index 85a9611..8e8845d 100644
--- a/Model/GraphQl/Resolver/AfterpayConfig.php
+++ b/Model/GraphQl/Resolver/AfterpayConfig.php
@@ -39,6 +39,7 @@ public function resolve(
$isEnabledCtaMinicart = $this->config->getIsEnableCtaMiniCart((int)$websiteId);
$isEnabledCtaCheckout = $this->config->getIsEnableCtaCartPage((int)$websiteId);
$publicId = $this->config->getPublicId((int)$websiteId);
+ $apiMode = $this->config->getApiMode((int)$websiteId);
return [
'max_amount' => $maxAmount,
@@ -48,6 +49,7 @@ public function resolve(
'is_enabled_cta_pdp' => $isEnabledCtaProductPage,
'is_enabled_cta_minicart' => $isEnabledCtaMinicart,
'is_enabled_cta_checkout' => $isEnabledCtaCheckout,
+ 'api_mode' => $apiMode,
'mpid' => $publicId,
];
}
diff --git a/Model/Order/CreditMemo/OrdersRetriever.php b/Model/Order/CreditMemo/OrdersRetriever.php
index e55090b..be981f1 100644
--- a/Model/Order/CreditMemo/OrdersRetriever.php
+++ b/Model/Order/CreditMemo/OrdersRetriever.php
@@ -2,100 +2,76 @@
namespace Afterpay\Afterpay\Model\Order\CreditMemo;
-use Afterpay\Afterpay\Model\Payment\AdditionalInformationInterface;
+use Afterpay\Afterpay\Api\Data\TokenInterface;
+use Afterpay\Afterpay\Model\ResourceModel\Token\CollectionFactory;
+use Magento\Framework\App\ResourceConnection;
+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;
class OrdersRetriever
{
- private \Magento\Sales\Model\ResourceModel\Order\CollectionFactory $orderCollectionFactory;
- private \Magento\Framework\App\ResourceConnection $resourceConnection;
- private \Magento\Framework\Serialize\SerializerInterface $serializer;
- private \Psr\Log\LoggerInterface $logger;
+ private OrderCollectionFactory $orderCollectionFactory;
+ private ResourceConnection $resourceConnection;
+ private CollectionFactory $tokensCollectionFactory;
+ private DateTime $dateTime;
public function __construct(
- \Magento\Framework\Serialize\SerializerInterface $serializer,
- \Magento\Sales\Model\ResourceModel\Order\CollectionFactory $orderCollectionFactory,
- \Magento\Framework\App\ResourceConnection $resourceConnection,
- \Psr\Log\LoggerInterface $logger
+ OrderCollectionFactory $orderCollectionFactory,
+ ResourceConnection $resourceConnection,
+ CollectionFactory $tokensCollectionFactory,
+ DateTime $dateTime
) {
- $this->serializer = $serializer;
$this->orderCollectionFactory = $orderCollectionFactory;
$this->resourceConnection = $resourceConnection;
- $this->logger = $logger;
+ $this->tokensCollectionFactory = $tokensCollectionFactory;
+ $this->dateTime = $dateTime;
}
/**
- * @return \Magento\Sales\Model\Order[]
+ * @return Order[]
*/
public function getAfterpayOrders(): array
{
- $orderCollection = $this->orderCollectionFactory->create();
- $orderCollection
+ $tokensCollection = $this->tokensCollectionFactory->create()
+ ->addFieldToSelect(TokenInterface::ORDER_ID_FIELD)
+ ->addFieldToFilter(TokenInterface::EXPIRATION_DATE_FIELD, ['notnull' => true])
->addFieldToFilter(
- 'state',
- ['eq' => \Magento\Sales\Model\Order::STATE_PROCESSING]
+ TokenInterface::EXPIRATION_DATE_FIELD,
+ [
+ 'date' => true,
+ 'from' => $this->dateTime->date('Y-m-d H:i:s', '-90 days'),
+ 'to' => $this->dateTime->date('Y-m-d H:i:s')
+ ]
);
+ $ids = $tokensCollection->getColumnValues(TokenInterface::ORDER_ID_FIELD);
+
+ $orderCollection = $this->orderCollectionFactory->create();
+ $orderCollection->addFieldToFilter(
+ OrderInterface::ENTITY_ID,
+ ['in' => $ids]
+ )->addFieldToFilter(
+ OrderInterface::STATE,
+ ['eq' => Order::STATE_PROCESSING]
+ );
$orderCollection = $this->joinAfterpayPaymentAdditionalInfo($orderCollection);
- /** @var \Magento\Sales\Model\Order[] $items */
- $items = $orderCollection->getItems();
- $items = $this->getItemsWithAdditionalInfo($items);
- return $items;
- }
- /**
- * @var \Magento\Sales\Model\Order[] $items
- * @return \Magento\Sales\Model\Order[]
- */
- private function getItemsWithAdditionalInfo(array $items): array
- {
- $itemsWithJsonAdditionalInfo = [];
- foreach ($items as $item) {
- $additionalInformation = $item->getData(
- \Magento\Sales\Api\Data\OrderPaymentInterface::ADDITIONAL_INFORMATION
- );
- try {
- $unserializedInfo = $this->serializer->unserialize($additionalInformation);
- if (!is_array($unserializedInfo)) {
- continue;
- }
- /** @var array $unserializedInfo */
- $item->setData(
- \Magento\Sales\Api\Data\OrderPaymentInterface::ADDITIONAL_INFORMATION,
- $unserializedInfo
- );
- $isAdditionalInfoFull =
- isset($unserializedInfo[AdditionalInformationInterface::AFTERPAY_PAYMENT_STATE]) &&
- isset($unserializedInfo[AdditionalInformationInterface::AFTERPAY_OPEN_TO_CAPTURE_AMOUNT]) &&
- isset($unserializedInfo[AdditionalInformationInterface::AFTERPAY_AUTH_EXPIRY_DATE]);
- if ($isAdditionalInfoFull) {
- $itemsWithJsonAdditionalInfo[] = $item;
- }
- } catch (\InvalidArgumentException $e) {
- $this->logger->warning($e->getMessage());
- }
- }
- return $itemsWithJsonAdditionalInfo;
+ return $orderCollection->getItems();
}
private function joinAfterpayPaymentAdditionalInfo(
- \Magento\Sales\Model\ResourceModel\Order\Collection $orderCollection
- ): \Magento\Sales\Model\ResourceModel\Order\Collection {
- $salesOrderPaymentTable = $this->resourceConnection->getConnection()->getTableName('sales_order_payment');
+ Collection $orderCollection
+ ): Collection {
+ $salesOrderPaymentTable = $this->resourceConnection->getTableName('sales_order_payment');
$orderCollection->join(
['sop' => $salesOrderPaymentTable],
'sop.parent_id = main_table.entity_id',
- \Magento\Sales\Api\Data\OrderPaymentInterface::ADDITIONAL_INFORMATION
+ OrderPaymentInterface::ADDITIONAL_INFORMATION
);
- $selectSql = $orderCollection->getSelectSql();
- /** @var \Magento\Framework\DB\Select $selectSql */
- $selectSql
- ->where(
- 'sop.method = ?',
- \Afterpay\Afterpay\Gateway\Config\Config::CODE
- )
- ->where(
- 'sop.additional_information like ?',
- '%' . AdditionalInformationInterface::AFTERPAY_AUTH_EXPIRY_DATE . '%'
- );
+
return $orderCollection;
}
}
diff --git a/Model/Order/Payment/Auth/TokenSaver.php b/Model/Order/Payment/Auth/TokenSaver.php
new file mode 100644
index 0000000..1c59e17
--- /dev/null
+++ b/Model/Order/Payment/Auth/TokenSaver.php
@@ -0,0 +1,25 @@
+tokensResource = $tokensResource;
+ $this->dateTime = $dateTime;
+ }
+
+ public function execute(int $orderId, string $token, ?string $expiryDate): bool
+ {
+ return (bool)$this->tokensResource->insertNewToken($orderId, $token, $this->dateTime->formatDate($expiryDate));
+ }
+}
diff --git a/Model/Order/Payment/Auth/TokenValidator.php b/Model/Order/Payment/Auth/TokenValidator.php
index 743b58a..60b6c8c 100644
--- a/Model/Order/Payment/Auth/TokenValidator.php
+++ b/Model/Order/Payment/Auth/TokenValidator.php
@@ -2,29 +2,24 @@
namespace Afterpay\Afterpay\Model\Order\Payment\Auth;
-use Afterpay\Afterpay\Api\Data\CheckoutInterface;
-use Afterpay\Afterpay\Gateway\Config\Config;
-use Magento\Framework\App\ResourceConnection;
+use Afterpay\Afterpay\Model\ResourceModel\Token;
class TokenValidator
{
- private ResourceConnection $resourceConnection;
+ private array $results = [];
+ private Token $tokensResource;
- public function __construct(ResourceConnection $resourceConnection)
+ public function __construct(Token $tokensResource)
{
- $this->resourceConnection = $resourceConnection;
+ $this->tokensResource = $tokensResource;
}
public function checkIsUsed(string $token): bool
{
- $salesOrderPaymentTable = $this->resourceConnection->getConnection()->getTableName('sales_order_payment');
- $checkSelect = $this->resourceConnection->getConnection()->select()
- ->from($salesOrderPaymentTable)
- ->where('method = ?', Config::CODE)
- ->where('base_amount_paid_online IS NOT NULL')
- ->where('last_trans_id IS NOT NULL')
- ->where('additional_information like ?', '%"' . CheckoutInterface::AFTERPAY_TOKEN . '":"' . $token . '%');
+ if (!isset($this->results[$token])) {
+ $this->results[$token] = (bool)$this->tokensResource->selectByToken($token);
+ }
- return (bool)$this->resourceConnection->getConnection()->fetchOne($checkSelect);
+ return $this->results[$token];
}
}
diff --git a/Model/Payment/Capture/PlaceOrderProcessor.php b/Model/Payment/Capture/PlaceOrderProcessor.php
index 9aa400c..42cab1f 100644
--- a/Model/Payment/Capture/PlaceOrderProcessor.php
+++ b/Model/Payment/Capture/PlaceOrderProcessor.php
@@ -4,13 +4,17 @@
use Afterpay\Afterpay\Api\Data\CheckoutInterface;
use Afterpay\Afterpay\Model\CBT\CheckCBTCurrencyAvailabilityInterface;
+use Afterpay\Afterpay\Model\Order\Payment\Auth\TokenSaver;
use Afterpay\Afterpay\Model\Order\Payment\Auth\TokenValidator;
+use Afterpay\Afterpay\Model\Payment\AdditionalInformationInterface;
use Afterpay\Afterpay\Model\Payment\PaymentErrorProcessor;
+use Magento\Checkout\Model\Session;
use Magento\Customer\Api\Data\GroupInterface;
use Magento\Payment\Gateway\CommandInterface;
use Magento\Payment\Gateway\Data\PaymentDataObjectFactoryInterface;
use Magento\Quote\Api\CartManagementInterface;
use Magento\Quote\Model\Quote;
+use Magento\Sales\Api\OrderRepositoryInterface;
class PlaceOrderProcessor
{
@@ -19,19 +23,28 @@ class PlaceOrderProcessor
private CheckCBTCurrencyAvailabilityInterface $checkCBTCurrencyAvailability;
private PaymentErrorProcessor $paymentErrorProcessor;
private TokenValidator $tokenValidator;
+ private TokenSaver $tokenSaver;
+ private OrderRepositoryInterface $orderRepository;
+ private Session $checkoutSession;
public function __construct(
CartManagementInterface $cartManagement,
PaymentDataObjectFactoryInterface $paymentDataObjectFactory,
CheckCBTCurrencyAvailabilityInterface $checkCBTCurrencyAvailability,
PaymentErrorProcessor $paymentErrorProcessor,
- TokenValidator $tokenValidator
+ TokenValidator $tokenValidator,
+ TokenSaver $tokenSaver,
+ OrderRepositoryInterface $orderRepository,
+ Session $checkoutSession
) {
$this->cartManagement = $cartManagement;
$this->paymentDataObjectFactory = $paymentDataObjectFactory;
$this->checkCBTCurrencyAvailability = $checkCBTCurrencyAvailability;
$this->paymentErrorProcessor = $paymentErrorProcessor;
$this->tokenValidator = $tokenValidator;
+ $this->tokenSaver = $tokenSaver;
+ $this->orderRepository = $orderRepository;
+ $this->checkoutSession = $checkoutSession;
}
public function execute(Quote $quote, CommandInterface $checkoutDataCommand, string $afterpayOrderToken): int
@@ -54,9 +67,23 @@ public function execute(Quote $quote, CommandInterface $checkoutDataCommand, str
}
$checkoutDataCommand->execute(['payment' => $this->paymentDataObjectFactory->create($payment)]);
- return (int)$this->cartManagement->placeOrder($quote->getId());
+ $this->checkoutSession->setAfterpayRedirect(true);
+
+ $orderId = (int)$this->cartManagement->placeOrder($quote->getId());
} catch (\Throwable $e) {
- return $this->paymentErrorProcessor->execute($quote, $e, $payment);
+ $orderId = $this->paymentErrorProcessor->execute($quote, $e, $payment);
}
+
+ $order = $this->orderRepository->get($orderId);
+ /** @var \Magento\Payment\Model\InfoInterface $orderPayment */
+ $orderPayment = $order->getPayment();
+ $this->tokenSaver->execute(
+ $orderId,
+ $afterpayOrderToken,
+ $orderPayment->getAdditionalInformation(AdditionalInformationInterface::AFTERPAY_AUTH_EXPIRY_DATE)
+ );
+ $this->checkoutSession->setAfterpayRedirect(false);
+
+ return $orderId;
}
}
diff --git a/Model/ResourceModel/NotAllowedProductsProvider.php b/Model/ResourceModel/NotAllowedProductsProvider.php
index 2cdb0cd..147f12b 100644
--- a/Model/ResourceModel/NotAllowedProductsProvider.php
+++ b/Model/ResourceModel/NotAllowedProductsProvider.php
@@ -5,14 +5,14 @@
class NotAllowedProductsProvider
{
private \Afterpay\Afterpay\Model\Config $config;
- private \Magento\Framework\DB\Adapter\AdapterInterface $connection;
+ private \Magento\Framework\App\ResourceConnection $resourceConnection;
public function __construct(
- \Afterpay\Afterpay\Model\Config $config,
+ \Afterpay\Afterpay\Model\Config $config,
\Magento\Framework\App\ResourceConnection $resourceConnection
) {
$this->config = $config;
- $this->connection = $resourceConnection->getConnection();
+ $this->resourceConnection = $resourceConnection;
}
public function provideIds(?int $storeId = null): array
@@ -22,11 +22,12 @@ public function provideIds(?int $storeId = null): array
return [];
}
- $select = $this->connection->select()->from(
- ['cat' => $this->connection->getTableName('catalog_category_product')],
+ $connection = $this->resourceConnection->getConnection();
+ $select = $connection->select()->from(
+ ['cat' => $this->resourceConnection->getTableName('catalog_category_product')],
'cat.product_id'
- )->where($this->connection->prepareSqlCondition('cat.category_id', ['in' => $excludedCategoriesIds]));
+ )->where($connection->prepareSqlCondition('cat.category_id', ['in' => $excludedCategoriesIds]));
- return $this->connection->fetchCol($select);
+ return $connection->fetchCol($select);
}
}
diff --git a/Model/ResourceModel/Token.php b/Model/ResourceModel/Token.php
new file mode 100644
index 0000000..a7353b6
--- /dev/null
+++ b/Model/ResourceModel/Token.php
@@ -0,0 +1,41 @@
+_init('afterpay_tokens_log', TokenInterface::LOG_ID_FIELD);
+ $this->_useIsObjectNew = true;
+ }
+
+ public function selectByToken(string $token): string
+ {
+ $connection = $this->getConnection();
+ $select = $connection->select()
+ ->from($this->getMainTable())
+ ->where(TokenInterface::TOKEN_FIELD . ' = ?', $token);
+
+ $result = $connection->fetchOne($select);
+
+ return is_string($result) ? $result : '';
+ }
+
+ public function insertNewToken(int $orderId, string $token, ?string $expiryDate): int
+ {
+ return $this->getConnection()->insert(
+ $this->getMainTable(),
+ [
+ TokenInterface::ORDER_ID_FIELD => $orderId,
+ TokenInterface::TOKEN_FIELD => $token,
+ TokenInterface::EXPIRATION_DATE_FIELD => $expiryDate,
+ ]);
+ }
+}
diff --git a/Model/ResourceModel/Token/Collection.php b/Model/ResourceModel/Token/Collection.php
new file mode 100644
index 0000000..4e7f410
--- /dev/null
+++ b/Model/ResourceModel/Token/Collection.php
@@ -0,0 +1,18 @@
+_init(Model::class, ResourceModel::class);
+ }
+}
diff --git a/Model/Token.php b/Model/Token.php
new file mode 100644
index 0000000..3a2c5cb
--- /dev/null
+++ b/Model/Token.php
@@ -0,0 +1,18 @@
+_init(ResourceModel::class);
+ }
+}
diff --git a/Plugin/Quote/CheckoutManagement.php b/Plugin/Quote/CheckoutManagement.php
new file mode 100644
index 0000000..875d801
--- /dev/null
+++ b/Plugin/Quote/CheckoutManagement.php
@@ -0,0 +1,39 @@
+quoteRepository = $quoteRepository;
+ $this->checkoutSession = $checkoutSession;
+ $this->paymentMethodChecker = $paymentMethodChecker;
+ }
+
+ public function beforePlaceOrder(CartManagementInterface $subject, $cartId, PaymentInterface $paymentMethod = null)
+ {
+ $quote = $this->quoteRepository->getActive($cartId);
+ $payment = $quote->getPayment();
+ if ($this->paymentMethodChecker->isAfterPayMethod($payment) && !$this->checkoutSession->getAfterpayRedirect()) {
+ throw new LocalizedException(__('You cannot use the chosen payment method.'));
+ }
+
+ return [$cartId, $paymentMethod];
+ }
+}
diff --git a/Setup/Patch/Data/AdaptCapturedDiscounts.php b/Setup/Patch/Data/AdaptCapturedDiscounts.php
index b98b617..293bb02 100644
--- a/Setup/Patch/Data/AdaptCapturedDiscounts.php
+++ b/Setup/Patch/Data/AdaptCapturedDiscounts.php
@@ -51,7 +51,7 @@ private function saveOrdersAdditionalInfo(array $ordersAdditionalInfo): void
{
foreach ($ordersAdditionalInfo as $orderId => $additionalInfo) {
$this->salesSetup->getConnection()->update(
- $this->salesSetup->getConnection()->getTableName('sales_order_payment'),
+ $this->salesSetup->getTable('sales_order_payment'),
['additional_information' => $this->json->serialize($additionalInfo)],
['parent_id = ?' => $orderId]
);
@@ -80,10 +80,10 @@ private function getAfterpayLegacyPaymentsInfo(): array
$connection = $this->salesSetup->getConnection();
$select = $connection->select()
->from(
- ['si' => $connection->getTableName('sales_invoice')],
+ ['si' => $this->salesSetup->getTable('sales_invoice')],
['si.order_id', 'si.base_customer_balance_amount', 'si.base_gift_cards_amount']
)->joinInner(
- ['sop' => $connection->getTableName('sales_order_payment')],
+ ['sop' => $this->salesSetup->getTable('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']
diff --git a/Setup/Patch/Data/MigrateTokens.php b/Setup/Patch/Data/MigrateTokens.php
new file mode 100644
index 0000000..37c06a7
--- /dev/null
+++ b/Setup/Patch/Data/MigrateTokens.php
@@ -0,0 +1,73 @@
+tokensResource = $tokensResource;
+ $this->serializer = $serializer;
+ $this->dateTime = $dateTime;
+ }
+
+ public function getAliases(): array
+ {
+ return [];
+ }
+
+ public static function getDependencies(): array
+ {
+ return [AdaptPayments::class];
+ }
+
+ public function apply(): self
+ {
+ $paymentsSelect = $this->tokensResource->getConnection()
+ ->select()
+ ->from($this->tokensResource->getTable('sales_order_payment'))
+ ->where(OrderPaymentInterface::METHOD . ' = ?', $this->paymentCode);
+
+ $payments = $this->tokensResource->getConnection()->fetchAll($paymentsSelect);
+ $tokenEntries = [];
+ foreach ($payments as $payment) {
+ if (!empty($payment[OrderPaymentInterface::ADDITIONAL_INFORMATION])) {
+ $additionalInfo = $this->serializer->unserialize($payment[OrderPaymentInterface::ADDITIONAL_INFORMATION]);
+ $token = $additionalInfo[CheckoutInterface::AFTERPAY_TOKEN];
+ $expiration = $additionalInfo[AdditionalInformationInterface::AFTERPAY_AUTH_EXPIRY_DATE] ?? null;
+ if ($expiration) {
+ $expiration = $this->dateTime->formatDate($expiration);
+ }
+ $tokenEntries[] = [
+ TokenInterface::ORDER_ID_FIELD => $payment['parent_id'],
+ TokenInterface::TOKEN_FIELD => $token,
+ TokenInterface::EXPIRATION_DATE_FIELD => $expiration
+ ];
+ }
+ }
+
+ if (!empty($tokenEntries)) {
+ $this->tokensResource->getConnection()->insertOnDuplicate($this->tokensResource->getMainTable(), $tokenEntries);
+ }
+
+ return $this;
+ }
+}
diff --git a/composer.json b/composer.json
index aeb9f26..be90132 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": "5.2.0",
+ "version": "5.2.1",
"require": {
"php": ">=7.4.0",
"magento/framework": "^103.0",
diff --git a/etc/db_schema.xml b/etc/db_schema.xml
new file mode 100644
index 0000000..9ed21a6
--- /dev/null
+++ b/etc/db_schema.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/etc/db_schema_whitelist.json b/etc/db_schema_whitelist.json
new file mode 100644
index 0000000..0dd3ab8
--- /dev/null
+++ b/etc/db_schema_whitelist.json
@@ -0,0 +1,16 @@
+{
+ "afterpay_tokens_log": {
+ "column": {
+ "log_id": true,
+ "order_id": true,
+ "token": true,
+ "expiration_date": true
+ },
+ "constraint": {
+ "PRIMARY": true,
+ "AFTERPAY_TOKENS_LOG_ORDER_ID_SALES_ORDER_ENTITY_ID": true,
+ "AFTERPAY_TOKENS_LOG_TOKEN": true,
+ "AFTERPAY_TOKENS_LOG_ORDER_ID": true
+ }
+ }
+}
\ No newline at end of file
diff --git a/etc/di.xml b/etc/di.xml
index 528ea6f..20d5999 100644
--- a/etc/di.xml
+++ b/etc/di.xml
@@ -8,6 +8,7 @@
+
@@ -390,4 +391,7 @@
Afterpay\Afterpay\Logger
+
+
+
diff --git a/etc/module.xml b/etc/module.xml
index 549f040..d279b42 100644
--- a/etc/module.xml
+++ b/etc/module.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/etc/schema.graphqls b/etc/schema.graphqls
index 0df4795..a169ba2 100644
--- a/etc/schema.graphqls
+++ b/etc/schema.graphqls
@@ -38,5 +38,6 @@ type afterpayConfigOutput {
is_enabled_cta_pdp: Boolean
is_enabled_cta_minicart: Boolean
is_enabled_cta_checkout: Boolean
+ api_mode: String
mpid: String
}
diff --git a/view/frontend/web/js/view/container/cta/cta.js b/view/frontend/web/js/view/container/cta/cta.js
index 459e3e4..ae05c9a 100644
--- a/view/frontend/web/js/view/container/cta/cta.js
+++ b/view/frontend/web/js/view/container/cta/cta.js
@@ -9,7 +9,8 @@
return Component.extend({
defaults: {
dataIsEligible: "true",
- dataCbtEnabledString: "false"
+ dataCbtEnabledString: "false",
+ pageType: "product"
},
initialize: function () {
const res = this._super();
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 0e95d87..0ed6127 100644
--- a/view/frontend/web/js/view/container/express-checkout/button.js
+++ b/view/frontend/web/js/view/container/express-checkout/button.js
@@ -36,13 +36,7 @@ define([
);
let errorMessage = $.localStorage.get('express-error-message');
if (errorMessage) {
- customerData.set('messages', {
- messages: [{
- type: 'error',
- text: $t(errorMessage)
- }]
- });
- $.localStorage.remove('express-error-message');
+ this._handleError(errorMessage);
}
return res;
},
@@ -103,7 +97,7 @@ define([
});
}
},
- _fail: function(actions, afterpayConst) {
+ _fail: function (actions, afterpayConst) {
actions.reject(afterpayConst);
AfterPay.close();
},
@@ -117,6 +111,45 @@ define([
return (this.countryCode && window.AfterPay !== undefined && this.isProductAllowed() &&
!(this.currentPrice() > floatMaxOrderTotal || this.currentPrice() < floatMinOrderTotal) &&
!this._getIsVirtual()) && this._super();
+ },
+ _handleError: function (errorMessage) {
+ $(document).ready(function () {
+ setTimeout(function () {
+ $('.message.error').fadeOut('slow');
+ }, 10000000);
+
+ var notNeedUpdateCount = 0;
+ var needUpdateCheck = setInterval(function () {
+ if (notNeedUpdateCount === 40) { // ~10s
+ clearInterval(needUpdateCheck);
+ }
+ let messages = customerData.get('messages');
+ let newMessages = [];
+ let needUpdate = true;
+ $.each(messages().messages, function (key, value) {
+ if (value.type === 'error' && value.text === errorMessage) {
+ needUpdate = false;
+ return false;
+ }
+ newMessages.push({
+ type: value.type,
+ text: value.text
+ });
+ });
+
+ if (needUpdate) {
+ newMessages.push({
+ type: 'error',
+ text: $t(errorMessage)
+ });
+ customerData.set('messages', {messages: newMessages});
+ } else {
+ notNeedUpdateCount++;
+ }
+ }, 250);
+
+ $.localStorage.remove('express-error-message');
+ });
}
});
});