diff --git a/Bootstrap.php b/Bootstrap.php
index f3df2ff..edd1d06 100644
--- a/Bootstrap.php
+++ b/Bootstrap.php
@@ -10,16 +10,22 @@
use JTL\Events\Dispatcher;
use JTL\Exceptions\CircularReferenceException;
use JTL\Exceptions\ServiceNotFoundException;
+use JTL\Router\Router;
+use Plugin\ws5_mollie\lib\CleanupCronJob;
use Plugin\ws5_mollie\lib\Hook\ApplePay;
use Plugin\ws5_mollie\lib\Hook\Checkbox;
use Plugin\ws5_mollie\lib\Hook\IncompletePaymentHandler;
use Plugin\ws5_mollie\lib\Hook\Queue;
+use Plugin\ws5_mollie\lib\Hook\FrontendHook;
use Plugin\ws5_mollie\lib\PluginHelper;
+use JTL\Events\Event;
require_once __DIR__ . '/vendor/autoload.php';
class Bootstrap extends \WS\JTL5\V1_0_16\Bootstrap
{
+ private const CRON_TYPE = 'cronjob_mollie_cleanup';
+
/**
* @param Dispatcher $dispatcher
* @throws CircularReferenceException
@@ -29,7 +35,16 @@ public function boot(Dispatcher $dispatcher): void
{
parent::boot($dispatcher);
+ $dispatcher->listen(Event::GET_AVAILABLE_CRONJOBS, [$this, 'availableCronjobType']);
+ $dispatcher->listen(Event::MAP_CRONJOB_TYPE, static function (array &$args) {
+ if ($args['type'] === self::CRON_TYPE) {
+ $args['mapping'] = CleanupCronJob::class;
+ }
+ });
+
+
$this->listen(HOOK_SMARTY_OUTPUTFILTER, [ApplePay::class, 'execute']);
+ $this->listen(HOOK_SMARTY_OUTPUTFILTER, [FrontendHook::class, 'execute']);
$this->listen(HOOK_BESTELLVORGANG_PAGE, [IncompletePaymentHandler::class, 'checkForIncompletePayment']);
@@ -44,6 +59,69 @@ public function boot(Dispatcher $dispatcher): void
if (PluginHelper::getSetting('useCustomerAPI') === 'C') {
$this->listen(HOOK_CHECKBOX_CLASS_GETCHECKBOXFRONTEND, [Checkbox::class, 'execute']);
}
+
+ //routes
+ if (PluginHelper::getSetting('queue') === 'async') {
+ $this->listen(HOOK_ROUTER_PRE_DISPATCH, function ($args) {
+ /** @var Router $router */
+ $router = $args['router'];
+ $router->addRoute('/' . self::getPlugin()->getPluginID() . '/queue', [\Plugin\ws5_mollie\lib\Queue::class, 'runAsynchronous'], null, ['POST']);
+ });
+ }
+ }
+
+ /**
+ * @return void
+ */
+ private function addCleanupCron(): void
+ {
+ $isInstalled = $this->getDB()->executeQueryPrepared('SELECT * FROM tcron WHERE name = :name AND jobType = :jobType',
+ [
+ ':name' => 'Mollie Queue Cleanup',
+ ':jobType' => self::CRON_TYPE
+ ],
+ 3) > 0;
+
+ if (!$isInstalled) {
+ $job = new \stdClass();
+ $job->name = 'Mollie Queue Cleanup';
+ $job->jobType = self::CRON_TYPE;
+ $job->frequency = 1;
+ $job->startDate = 'NOW()';
+ $job->startTime = '06:00:00';
+ $this->getDB()->insert('tcron', $job);
+ }
+ }
+
+ /**
+ * @param array $args
+ * @return void
+ */
+ public function availableCronjobType(array &$args): void
+ {
+ if (!\in_array(self::CRON_TYPE, $args['jobs'], true)) {
+ $args['jobs'][] = self::CRON_TYPE;
+ }
+ }
+
+
+ /**
+ * @return void
+ */
+ public function installed(): void
+ {
+ parent::installed();
+ $this->addCleanupCron();
+ }
+
+ /**
+ * @param bool $deleteData
+ * @return void
+ */
+ public function uninstalled(bool $deleteData = true): void
+ {
+ parent::uninstalled($deleteData);
+ $this->getDB()->delete('tcron', ['name', 'jobType'], ['Mollie Queue Cleanup', self::CRON_TYPE]);
}
/**
@@ -55,6 +133,10 @@ public function updated($oldVersion, $newVersion): void
{
parent::updated($oldVersion, $newVersion);
+ if ($newVersion >= "1.9.0") {
+ $this->addCleanupCron();
+ }
+
if (PluginHelper::isShopVersionEqualOrGreaterThan('5.3.0')) {
\JTL\Update\DBMigrationHelper::migrateToInnoDButf8('xplugin_ws5_mollie_kunde'); // TODO: remove this code when min. shop version is 5.3
\JTL\Update\DBMigrationHelper::migrateToInnoDButf8('xplugin_ws5_mollie_orders'); // TODO: remove this code when min. shop version is 5.3
diff --git a/Migrations/Migration20240919161400.php b/Migrations/Migration20240919161400.php
new file mode 100644
index 0000000..24176cf
--- /dev/null
+++ b/Migrations/Migration20240919161400.php
@@ -0,0 +1,23 @@
+execute('CREATE INDEX idx_composite_with_order ON xplugin_ws5_mollie_queue (`dDone`, `bLock`, `cType`, `dCreated` DESC);');
+ }
+
+ public function down()
+ {
+ // No need to change since 'xplugin_ws5_mollie_orders' is removed in Migration where it is created, and we don't support downgrading of Plugins
+ }
+}
\ No newline at end of file
diff --git a/frontend/hooks/131.php b/frontend/hooks/131.php
index f395068..3ee8b33 100644
--- a/frontend/hooks/131.php
+++ b/frontend/hooks/131.php
@@ -24,9 +24,11 @@
require_once __DIR__ . '/../../vendor/autoload.php';
- ifndef('MOLLIE_QUEUE_MAX', 3);
- /** @noinspection PhpUndefinedConstantInspection */
- Queue::run(MOLLIE_QUEUE_MAX);
+ if (PluginHelper::getSetting('queue') === 'sync') {
+ ifndef('MOLLIE_QUEUE_MAX', 3);
+ /** @noinspection PhpUndefinedConstantInspection */
+ Queue::runSynchronous(MOLLIE_QUEUE_MAX);
+ }
//TODO: remove this check in next version
// forces v5.1 Shop to abschlussseite
@@ -83,32 +85,10 @@
}
}
-
-
-
- ifndef('MOLLIE_REMINDER_PROP', 10);
- if (random_int(1, MOLLIE_REMINDER_PROP) % MOLLIE_REMINDER_PROP === 0) {
- /** @noinspection PhpUndefinedConstantInspection */
- $lock = new ExclusiveLock('mollie_reminder', PFAD_ROOT . PFAD_COMPILEDIR);
- if ($lock->lock()) {
- AbstractCheckout::sendReminders();
- Queue::storno(PluginHelper::getSetting('autoStorno'));
- }
- }
-
- // TODO: DOKU
- ifndef('MOLLIE_DISABLE_USER_CLEANUP', false);
-
- if (!MOLLIE_DISABLE_USER_CLEANUP) {
- ifndef('MOLLIE_CLEANUP_PROP', 15);
- /** @noinspection PhpUndefinedConstantInspection */
- if (MOLLIE_CLEANUP_PROP && random_int(1, MOLLIE_CLEANUP_PROP) % MOLLIE_CLEANUP_PROP === 0) {
- QueueModel::cleanUp();
- }
- }
if (array_key_exists('mollie_cleanup_cron', $_REQUEST)) {
exit((string) QueueModel::cleanUp());
}
+
} catch (Exception $e) {
Shop::Container()->getLogService()->error($e->getMessage() . " (Trace: {$e->getTraceAsString()})");
}
diff --git a/frontend/template/queue.tpl b/frontend/template/queue.tpl
new file mode 100644
index 0000000..15aab94
--- /dev/null
+++ b/frontend/template/queue.tpl
@@ -0,0 +1,22 @@
+
\ No newline at end of file
diff --git a/info.xml b/info.xml
index 2694604..36bc9ef 100644
--- a/info.xml
+++ b/info.xml
@@ -8,7 +8,8 @@
5.2.0
ws5_mollie
2023-02-13
- 1.9.0
+ 1.9.1
+ 689388c6-9f04-4648-b516-e67d96b0dc1d
hooks/131.php
diff --git a/lib/CleanupCronJob.php b/lib/CleanupCronJob.php
new file mode 100644
index 0000000..f046e4d
--- /dev/null
+++ b/lib/CleanupCronJob.php
@@ -0,0 +1,32 @@
+logger->debug('Mollie Queue Cleanup');
+ ifndef('MOLLIE_DISABLE_USER_CLEANUP', false);
+ if (!MOLLIE_DISABLE_USER_CLEANUP) {
+ QueueModel::cleanUp();
+ }
+
+ PluginHelper::cleanupPaymentLogs();
+
+ } catch (\Exception $e) {
+ $this->logger->debug('Mollie Queue Exception: ' . $e->getMessage());
+ }
+
+ $this->setFinished(true);
+ return $this;
+ }
+}
diff --git a/lib/Hook/ApplePay.php b/lib/Hook/ApplePay.php
index a8d9a60..2e8afb3 100644
--- a/lib/Hook/ApplePay.php
+++ b/lib/Hook/ApplePay.php
@@ -24,15 +24,8 @@ public static function execute($args_arr = []): void
return;
}
- // Reset CreditCard-Token after Order!
- if (
- ($key = sprintf('kPlugin_%d_creditcard', PluginHelper::getPlugin()->getID()))
- && array_key_exists($key, $_SESSION) && !array_key_exists('Zahlungsart', $_SESSION)
- ) {
- unset($_SESSION[$key]);
- }
-
- if (!array_key_exists('ws_mollie_applepay_available', $_SESSION)) {
+ // append applepay script
+ if (!array_key_exists('ws_mollie_applepay_available', $_SESSION) && self::isActive()) {
pq('head')->append("");
}
} catch (Exception $e) {
@@ -58,4 +51,23 @@ public static function setAvailable(bool $status): void
{
$_SESSION['ws_mollie_applepay_available'] = $status;
}
+
+ /**
+ * @return bool
+ */
+ public static function isActive(): bool
+ {
+ $kZahlunsgart = PluginHelper::getDB()->executeQueryPrepared('SELECT kZahlungsart FROM tzahlungsart WHERE cModulId = :cModulId',
+ [
+ ':cModulId' => 'kPlugin_' . PluginHelper::getPlugin()->getID() . '_applepay'
+ ], 1)->kZahlungsart ?? null;
+ if ($kZahlunsgart > 0) {
+ return PluginHelper::getDB()->executeQueryPrepared('SELECT * FROM tversandartzahlungsart WHERE kZahlungsart = :kZahlungsart',
+ [
+ ':kZahlungsart' => $kZahlunsgart
+ ], 3) > 0;
+ } else {
+ return false;
+ }
+ }
}
diff --git a/lib/Hook/FrontendHook.php b/lib/Hook/FrontendHook.php
new file mode 100644
index 0000000..e8ead30
--- /dev/null
+++ b/lib/Hook/FrontendHook.php
@@ -0,0 +1,45 @@
+getID()))
+ && array_key_exists($key, $_SESSION) && !array_key_exists('Zahlungsart', $_SESSION)
+ ) {
+ unset($_SESSION[$key]);
+ }
+
+ // append queue script
+ if (PluginHelper::getSetting('queue') === 'async') {
+ Shop::Smarty()->assign('wsQueueURL', Shop::getURL() . '/ws5_mollie/queue');
+ pq('body')->append(Shop::Smarty()->fetch(PluginHelper::getPlugin()->getPaths()->getFrontendPath() . 'template/queue.tpl', false));
+ }
+ } catch (Exception $e) {
+ }
+ }
+}
diff --git a/lib/PluginHelper.php b/lib/PluginHelper.php
index 3984059..3b54664 100644
--- a/lib/PluginHelper.php
+++ b/lib/PluginHelper.php
@@ -11,4 +11,13 @@
class PluginHelper extends AbstractPluginHelper
{
protected static $pluginId = 'ws5_mollie';
+
+ public static function cleanupPaymentLogs(): void
+ {
+ self::getDB()->executeQueryPrepared('DELETE FROM tzahlungslog WHERE cModulId LIKE :cModulId AND dDatum < DATE_SUB(NOW(), INTERVAL 3 MONTH) LIMIT 100000;',
+ [
+ 'cModulId' => 'kPlugin_' . self::getPlugin()->getID() . '_%'
+ ],
+ 10);
+ }
}
diff --git a/lib/Queue.php b/lib/Queue.php
index b67237b..b4cdb0e 100644
--- a/lib/Queue.php
+++ b/lib/Queue.php
@@ -12,6 +12,7 @@
use JTL\Exceptions\CircularReferenceException;
use JTL\Exceptions\ServiceNotFoundException;
use JTL\Shop;
+use JTL\Helpers\Request;
use Mollie\Api\Types\OrderStatus;
use Plugin\ws5_mollie\lib\Checkout\AbstractCheckout;
use Plugin\ws5_mollie\lib\Checkout\OrderCheckout;
@@ -24,13 +25,75 @@ class Queue
{
use Plugins;
+
/**
* @param int $limit
* @throws CircularReferenceException
* @throws ServiceNotFoundException
*/
- public static function run(int $limit = 10): void
+ public static function runSynchronous(int $limit = 10): void
+ {
+ /** @var QueueModel $todo */
+ foreach (self::getOpen($limit) as $todo) {
+ if (!self::lock($todo)) {
+ continue;
+ }
+
+ if (([$type, $id] = explode(':', $todo->cType))) {
+ try {
+ switch ($type) {
+ case 'webhook':
+ self::handleWebhook($id, $todo);
+
+ break;
+ case 'hook':
+ self::handleHook((int)$id, $todo);
+
+ break;
+ }
+ } catch (Exception $e) {
+ Shop::Container()->getLogService()->notice('Mollie Queue Fehler: ' . $e->getMessage() . " ($type, $id)");
+ $todo->cError = "{$e->getMessage()}\n{$e->getFile()}:{$e->getLine()}\n{$e->getTraceAsString()}";
+ $todo->done();
+ }
+ }
+
+ self::unlock($todo);
+ }
+
+ ifndef('MOLLIE_REMINDER_PROP', 10);
+ if (random_int(1, MOLLIE_REMINDER_PROP) % MOLLIE_REMINDER_PROP === 0) {
+ /** @noinspection PhpUndefinedConstantInspection */
+ $lock = new ExclusiveLock('mollie_reminder', PFAD_ROOT . PFAD_COMPILEDIR);
+ if ($lock->lock()) {
+ AbstractCheckout::sendReminders();
+ Queue::storno(PluginHelper::getSetting('autoStorno'));
+ }
+ }
+ }
+
+
+ /**
+ * @throws CircularReferenceException
+ * @throws ServiceNotFoundException
+ */
+ public static function runAsynchronous(): void
{
+ ifndef('MOLLIE_QUEUE_MAX', 3);
+
+ if ($_SERVER['REQUEST_METHOD'] === 'POST' && Request::isAjaxRequest()) {
+ $limit = MOLLIE_QUEUE_MAX;
+ } else {
+ $response = [
+ "status" => "error",
+ "message" => "Invalid request"
+ ];
+
+ header('Content-Type: application/json');
+ echo json_encode($response);
+ exit;
+ }
+
/** @var QueueModel $todo */
foreach (self::getOpen($limit) as $todo) {
if (!self::lock($todo)) {
@@ -58,8 +121,30 @@ public static function run(int $limit = 10): void
self::unlock($todo);
}
+
+ ifndef('MOLLIE_REMINDER_PROP', 10);
+ if (random_int(1, MOLLIE_REMINDER_PROP) % MOLLIE_REMINDER_PROP === 0) {
+ /** @noinspection PhpUndefinedConstantInspection */
+ $lock = new ExclusiveLock('mollie_reminder', PFAD_ROOT . PFAD_COMPILEDIR);
+ if ($lock->lock()) {
+ AbstractCheckout::sendReminders();
+ Queue::storno(PluginHelper::getSetting('autoStorno'));
+ }
+ }
+
+ $response = [
+ "status" => "success",
+ "data" => [
+ "message" => "Request was successful"
+ ]
+ ];
+
+ header('Content-Type: application/json');
+ echo json_encode($response);
+ exit;
}
+
/**
* @param int $limit
* @return Generator
@@ -79,6 +164,7 @@ private static function getOpen(int $limit): Generator
}
}
+
/**
* @param QueueModel $todo
* @return bool
diff --git a/pluginSettings.json b/pluginSettings.json
index 1f02bd6..6964c1b 100644
--- a/pluginSettings.json
+++ b/pluginSettings.json
@@ -286,5 +286,17 @@
{"value": "A" , "label": "Nur Authorized"},
{"value": "N" , "label": "Keine"}
]
+ },
+ {
+ "id": "queue",
+ "label": "Abarbeitung der Queue",
+ "description": "Soll die Queue schrittweise bei jedem Aufruf abgearbeitet werden (wie bisher), oder über asynchrone Aufrufe via Javascript auf der Shopseite?",
+ "settingType": "select",
+ "type": "string",
+ "defaultValue": "sync",
+ "selectItems": [
+ {"value": "sync" , "label": "Synchron"},
+ {"value": "async" , "label": "Asynchron"}
+ ]
}
]
\ No newline at end of file