diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml
index 5f0063151..f0638fa31 100644
--- a/src/Resources/config/services.xml
+++ b/src/Resources/config/services.xml
@@ -50,6 +50,13 @@
+
+
+
+
+
+
+
diff --git a/src/Resources/config/services/scheduled_tasks.xml b/src/Resources/config/services/scheduled_tasks.xml
index 1c072edfc..5ab26627a 100755
--- a/src/Resources/config/services/scheduled_tasks.xml
+++ b/src/Resources/config/services/scheduled_tasks.xml
@@ -22,5 +22,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Resources/config/services/subscriber.xml b/src/Resources/config/services/subscriber.xml
index 3efd493f7..fad289545 100644
--- a/src/Resources/config/services/subscriber.xml
+++ b/src/Resources/config/services/subscriber.xml
@@ -67,9 +67,7 @@
-
-
-
+
diff --git a/src/ScheduledTask/OrderStatus/ExpireOrderTask.php b/src/ScheduledTask/OrderStatus/ExpireOrderTask.php
new file mode 100644
index 000000000..4b062807a
--- /dev/null
+++ b/src/ScheduledTask/OrderStatus/ExpireOrderTask.php
@@ -0,0 +1,19 @@
+getRepository(), $logger);
+ $this->logger = $logger;
+ $this->orderRepository = $orderRepository;
+ $this->orderExpireService = $orderExpireService;
+ }
+
+ public function run(): void
+ {
+ $this->logger->info('Start resetting in_progress orders');
+
+ $context = new Context(new SystemSource());
+
+ $criteria = new Criteria();
+ $criteria->addAssociation('transactions.stateMachineState');
+ $criteria->getAssociation('transactions.stateMachineState')->addFilter(new EqualsFilter('technicalName', OrderStates::STATE_IN_PROGRESS));
+
+ $this->logger->debug('Search for orders which are in progress state');
+
+ $searchResult = $this->orderRepository->search($criteria, $context);
+ if ($searchResult->count() === 0) {
+ $this->logger->debug('No in progress orders found');
+ return;
+ }
+
+ $this->logger->info('Found orders which are in progress', ['foundOrders' => $searchResult->count()]);
+
+ /**
+ * @var OrderCollection $orders
+ */
+ $orders = $searchResult->getEntities();
+ $resetted = $this->orderExpireService->cancelExpiredOrders($orders, $context);
+
+ $this->logger->info('Rested expired orders', ['restedOrders' => $resetted]);
+ }
+
+ /**
+ * @return iterable
+ */
+ public static function getHandledMessages(): iterable
+ {
+ return [
+ ExpireOrderTask::class
+ ];
+ }
+}
diff --git a/src/ScheduledTask/Subscription/RenewalReminder/RenewalReminderTaskHandler.php b/src/ScheduledTask/Subscription/RenewalReminder/RenewalReminderTaskHandler.php
index 5f04a93a0..3387c939e 100755
--- a/src/ScheduledTask/Subscription/RenewalReminder/RenewalReminderTaskHandler.php
+++ b/src/ScheduledTask/Subscription/RenewalReminder/RenewalReminderTaskHandler.php
@@ -8,6 +8,8 @@
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\MessageQueue\ScheduledTask\ScheduledTaskHandler;
+#[\Symfony\Component\Messenger\Attribute\AsMessageHandler(handles: RenewalReminderTask::class)]
+#[\Symfony\Component\Messenger\Attribute\AsMessageHandler(handles: RenewalReminderTaskDev::class)]
class RenewalReminderTaskHandler extends ScheduledTaskHandler
{
/**
@@ -28,7 +30,8 @@ class RenewalReminderTaskHandler extends ScheduledTaskHandler
*/
public function __construct(ScheduledTaskRepositoryInterface $scheduledTaskRepository, SubscriptionManager $subscriptionManager, LoggerInterface $logger)
{
- parent::__construct($scheduledTaskRepository->getRepository());
+ /** @phpstan-ignore-next-line */
+ parent::__construct($scheduledTaskRepository->getRepository(), $logger);
$this->subscriptionManager = $subscriptionManager;
$this->logger = $logger;
diff --git a/src/Service/Order/OrderExpireService.php b/src/Service/Order/OrderExpireService.php
new file mode 100644
index 000000000..22a742872
--- /dev/null
+++ b/src/Service/Order/OrderExpireService.php
@@ -0,0 +1,144 @@
+orderStatusUpdater = $orderStatusUpdater;
+ $this->orderTimeService = $orderTimeService;
+ $this->settingsService = $settingsService;
+ $this->logger = $logger;
+ }
+
+ /**
+ * If an order is "in progress" but the payment link is already expired, the order is changed to cannceled
+ * @param OrderCollection $orders
+ * @param Context $context
+ * @return int
+ */
+ public function cancelExpiredOrders(OrderCollection $orders, Context $context): int
+ {
+ $resetted = 0;
+ /** @var OrderEntity $order */
+ foreach ($orders as $order) {
+ if (! $order instanceof OrderEntity) {
+ continue;
+ }
+
+ $orderAttributes = new OrderAttributes($order);
+
+ if (strlen($orderAttributes->getMollieOrderId()) === 0) {
+ continue;
+ }
+
+ $transactions = $order->getTransactions();
+
+ if ($transactions === null || $transactions->count() === 0) {
+ continue;
+ }
+
+ $transactions->sort(function (OrderTransactionEntity $a, OrderTransactionEntity $b) {
+ return $a->getCreatedAt() <=> $b->getCreatedAt();
+ });
+
+ /** @var OrderTransactionEntity $lastTransaction */
+ $lastTransaction = $transactions->last();
+
+ $stateMachineState = $lastTransaction->getStateMachineState();
+ if ($stateMachineState === null) {
+ continue;
+ }
+
+ $lastStatus = $stateMachineState->getTechnicalName();
+
+ // disregard any orders that are not in progress
+ if ($lastStatus !== OrderStates::STATE_IN_PROGRESS) {
+ continue;
+ }
+
+ $settings = $this->settingsService->getSettings();
+ $finalizeTransactionTimeInMinutes = $settings->getPaymentFinalizeTransactionTime();
+
+ if ($this->orderUsesSepaPayment($lastTransaction)) {
+ $finalizeTransactionTimeInMinutes = (int)ceil($settings->getPaymentMethodBankTransferDueDateDays() / 24 / 60);
+ }
+
+ if ($this->orderTimeService->isOrderAgeGreaterThan($order, $finalizeTransactionTimeInMinutes) === false) {
+ continue;
+ }
+
+ // orderStatusUpdater needs the order to be set on the transaction
+ $lastTransaction->setOrder($order);
+
+ // this forces the order to be open again
+ $context->addState(OrderStatusUpdater::ORDER_STATE_FORCE_OPEN);
+
+ try {
+ $this->orderStatusUpdater->updatePaymentStatus($lastTransaction, MolliePaymentStatus::MOLLIE_PAYMENT_CANCELED, $context);
+ $resetted++;
+ } catch (\Exception $exception) {
+ $this->logger->error('Failed to update payment status for transaction', [
+ 'transaction' => $lastTransaction->getId(),
+ 'order' => $order->getOrderNumber()
+ ]);
+ }
+ }
+
+ return $resetted;
+ }
+
+ /**
+ * @param OrderTransactionEntity $transaction
+ * @return bool
+ * @todo refactor once php8.0 is minimum version. Use Null-safe operator
+ */
+ private function orderUsesSepaPayment(OrderTransactionEntity $transaction): bool
+ {
+ $paymentMethod = $transaction->getPaymentMethod();
+
+ if ($paymentMethod === null) {
+ return false;
+ }
+
+ return $paymentMethod->getHandlerIdentifier() === BankTransferPayment::class;
+ }
+}
diff --git a/src/Subscriber/OrderEditSubscriber.php b/src/Subscriber/OrderEditSubscriber.php
index 29e22039a..927cf859f 100644
--- a/src/Subscriber/OrderEditSubscriber.php
+++ b/src/Subscriber/OrderEditSubscriber.php
@@ -3,43 +3,22 @@
namespace Kiener\MolliePayments\Subscriber;
-use Closure;
-use Kiener\MolliePayments\Handler\Method\BankTransferPayment;
-use Kiener\MolliePayments\Service\Mollie\MolliePaymentStatus;
-use Kiener\MolliePayments\Service\Order\OrderStatusUpdater;
-use Kiener\MolliePayments\Service\Order\OrderTimeService;
-use Kiener\MolliePayments\Service\SettingsService;
-use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity;
-use Shopware\Core\Checkout\Order\OrderEntity;
-use Shopware\Core\Checkout\Order\OrderStates;
+use Kiener\MolliePayments\Service\Order\OrderExpireService;
+use Shopware\Core\Checkout\Order\OrderCollection;
use Shopware\Storefront\Page\Account\Order\AccountOrderPageLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class OrderEditSubscriber implements EventSubscriberInterface
{
/**
- * @var OrderStatusUpdater
+ * @var OrderExpireService
*/
- private $orderStatusUpdater;
-
- /**
- * @var OrderTimeService
- */
- private $orderTimeService;
-
- /**
- * @var SettingsService
- */
- private $settingsService;
+ private $orderExpireService;
public function __construct(
- OrderStatusUpdater $orderStatusUpdater,
- OrderTimeService $orderTimeService,
- SettingsService $settingsService
+ OrderExpireService $orderExpireService
) {
- $this->orderStatusUpdater = $orderStatusUpdater;
- $this->orderTimeService = $orderTimeService;
- $this->settingsService = $settingsService;
+ $this->orderExpireService = $orderExpireService;
}
public static function getSubscribedEvents(): array
@@ -51,95 +30,11 @@ public static function getSubscribedEvents(): array
public function accountOrderDetailPageLoaded(AccountOrderPageLoadedEvent $event): void
{
- $orders = $event->getPage()->getOrders();
-
- foreach ($orders as $order) {
- if (!$order instanceof OrderEntity || $this->isMolliePayment($order) === false) {
- continue;
- }
-
- $transactions = $order->getTransactions();
-
- if ($transactions === null || $transactions->count() === 0) {
- continue;
- }
+ /** @var OrderCollection $orders */
+ $orders = $event->getPage()->getOrders()->getEntities();
- $transactions->sort(Closure::fromCallable([$this, 'sortTransactionsByDate']));
+ $context = $event->getContext();
- $lastTransaction = $transactions->last();
-
- $lastStatus = $lastTransaction->getStateMachineState()->getTechnicalName();
-
- // disregard any orders that are not in progress
- if ($lastStatus !== OrderStates::STATE_IN_PROGRESS) {
- continue;
- }
-
- $settings = $this->settingsService->getSettings();
- $finalizeTransactionTimeInMinutes = $settings->getPaymentFinalizeTransactionTime();
-
- if ($this->orderUsesSepaPayment($order)) {
- $finalizeTransactionTimeInMinutes = (int) ceil($settings->getPaymentMethodBankTransferDueDateDays() / 24 / 60);
- }
-
- if ($this->orderTimeService->isOrderAgeGreaterThan($order, $finalizeTransactionTimeInMinutes) === false) {
- continue;
- }
-
- // orderStatusUpdater needs the order to be set on the transaction
- $lastTransaction->setOrder($order);
- $context = $event->getContext();
- // this forces the order to be open again
- $context->addState(OrderStatusUpdater::ORDER_STATE_FORCE_OPEN);
- try {
- $this->orderStatusUpdater->updatePaymentStatus($lastTransaction, MolliePaymentStatus::MOLLIE_PAYMENT_CANCELED, $context);
- } catch (\Exception $exception) {
- }
- }
- }
-
- /**
- * @param OrderEntity $order
- * @return bool
- * @todo refactor once php8.0 is minimum version. Use Null-safe operator
- */
- private function orderUsesSepaPayment(OrderEntity $order): bool
- {
- $transactions = $order->getTransactions();
-
- if ($transactions === null || count($transactions) === 0) {
- return false;
- }
-
- $lastTransaction = $transactions->last();
-
- if ($lastTransaction instanceof OrderTransactionEntity === false) {
- return false;
- }
-
- $paymentMethod = $lastTransaction->getPaymentMethod();
-
- if ($paymentMethod === null) {
- return false;
- }
-
- return $paymentMethod->getHandlerIdentifier() === BankTransferPayment::class;
- }
-
- private function isMolliePayment(OrderEntity $order): bool
- {
- $customFields = $order->getCustomFields();
-
- return is_array($customFields) && count($customFields) && isset($customFields['mollie_payments']);
- }
-
- /**
- * @param OrderTransactionEntity $a
- * @param OrderTransactionEntity $b
- * @return int
- */
- private function sortTransactionsByDate(OrderTransactionEntity $a, OrderTransactionEntity $b): int
- {
- return $a->getCreatedAt() <=> $b->getCreatedAt();
+ $rested = $this->orderExpireService->cancelExpiredOrders($orders, $context);
}
}