From fbc562309c406ff202258962e216504d1fbb0094 Mon Sep 17 00:00:00 2001 From: amirinterlutions Date: Fri, 1 Mar 2024 01:49:19 +0100 Subject: [PATCH 1/4] PAYOSWXP-121: Adds configuration for resending notification forwards --- src/Command/ResendNotifyCommand.php | 37 ++++++ .../ResendNotifyHandler.php | 43 ++++++ ...yonePaymentNotificationQueueCollection.php | 18 +++ ...yonePaymentNotificationQueueDefinition.php | 57 ++++++++ .../PayonePaymentNotificationQueueEntity.php | 75 +++++++++++ ...onePaymentNotificationTargetDefinition.php | 3 + .../PayonePaymentNotificationTargetEntity.php | 37 +++++- src/DependencyInjection/commands.xml | 13 ++ src/DependencyInjection/entities.xml | 4 + .../handler/global_handlers.xml | 5 + src/DependencyInjection/scheduled_tasks.xml | 10 ++ src/DependencyInjection/services.xml | 1 + src/DependencyInjection/webhooks.xml | 1 + ...1709142760AlterNotificationTargetTable.php | 31 +++++ .../Migration1709142764NotificationQueue.php | 36 ++++++ .../NotificationForwardHandler.php | 50 ++++++- .../index.js | 122 ++++++++++++++++-- ...ayone-notification-target-detail.html.twig | 38 +++++- .../payone-notification-target-list/index.js | 5 + .../payone-notification-target-list.html.twig | 9 ++ .../snippet/de_DE.json | 16 ++- .../snippet/en_GB.json | 16 ++- src/ScheduledTask/NotifySendRetry.php | 22 ++++ src/ScheduledTask/NotifySendRetryHandler.php | 30 +++++ 24 files changed, 663 insertions(+), 16 deletions(-) create mode 100644 src/Command/ResendNotifyCommand.php create mode 100644 src/Components/ResendNotifyHandler/ResendNotifyHandler.php create mode 100644 src/DataAbstractionLayer/Entity/NotificationQueue/PayonePaymentNotificationQueueCollection.php create mode 100644 src/DataAbstractionLayer/Entity/NotificationQueue/PayonePaymentNotificationQueueDefinition.php create mode 100644 src/DataAbstractionLayer/Entity/NotificationQueue/PayonePaymentNotificationQueueEntity.php create mode 100644 src/DependencyInjection/commands.xml create mode 100644 src/Migration/Migration1709142760AlterNotificationTargetTable.php create mode 100644 src/Migration/Migration1709142764NotificationQueue.php create mode 100644 src/ScheduledTask/NotifySendRetry.php create mode 100644 src/ScheduledTask/NotifySendRetryHandler.php diff --git a/src/Command/ResendNotifyCommand.php b/src/Command/ResendNotifyCommand.php new file mode 100644 index 000000000..93f09cd47 --- /dev/null +++ b/src/Command/ResendNotifyCommand.php @@ -0,0 +1,37 @@ +setDescription('Send notification forwarding queue'); + } + + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $output->writeln('Sending notification forwarding queue'); + + $this->resendNotifyHandler->send(); + + $output->writeln('Notification forwarding queue sent'); + + return 0; + } +} diff --git a/src/Components/ResendNotifyHandler/ResendNotifyHandler.php b/src/Components/ResendNotifyHandler/ResendNotifyHandler.php new file mode 100644 index 000000000..a0974c8d2 --- /dev/null +++ b/src/Components/ResendNotifyHandler/ResendNotifyHandler.php @@ -0,0 +1,43 @@ +addFilter(new RangeFilter('nextExecutionTime', [ + RangeFilter::LTE => $currentDate->format(Defaults::STORAGE_DATE_TIME_FORMAT) + ])); + + $notificationQueue = $this->notificationQueueRepository->search($criteria, Context::createDefaultContext())->getEntities(); + + /** @var PayonePaymentNotificationQueueEntity $notification */ + foreach ($notificationQueue as $notification) { + /** @var NotificationForwardCommand $message */ + $message = unserialize(base64_decode($notification->getMessage()),[]); + $this->notificationForwardHandler->handle($message, true); + $this->notificationQueueRepository->delete([['id' => $notification->getId()]], Context::createDefaultContext()); + } + echo "current time: " . $currentDate->format(Defaults::STORAGE_DATE_TIME_FORMAT) . "\n"; + } +} diff --git a/src/DataAbstractionLayer/Entity/NotificationQueue/PayonePaymentNotificationQueueCollection.php b/src/DataAbstractionLayer/Entity/NotificationQueue/PayonePaymentNotificationQueueCollection.php new file mode 100644 index 000000000..463ff6b35 --- /dev/null +++ b/src/DataAbstractionLayer/Entity/NotificationQueue/PayonePaymentNotificationQueueCollection.php @@ -0,0 +1,18 @@ + + */ +class PayonePaymentNotificationQueueCollection extends EntityCollection +{ + protected function getExpectedClass(): string + { + return PayonePaymentNotificationQueueEntity::class; + } +} diff --git a/src/DataAbstractionLayer/Entity/NotificationQueue/PayonePaymentNotificationQueueDefinition.php b/src/DataAbstractionLayer/Entity/NotificationQueue/PayonePaymentNotificationQueueDefinition.php new file mode 100644 index 000000000..dd1767731 --- /dev/null +++ b/src/DataAbstractionLayer/Entity/NotificationQueue/PayonePaymentNotificationQueueDefinition.php @@ -0,0 +1,57 @@ +setFlags(new PrimaryKey(), new Required()), + new FkField('notification_target_id', 'notificationTargetId', PayonePaymentNotificationTargetDefinition::class), + new OneToOneAssociationField('notificationTarget', 'notification_target_id', 'id', PayonePaymentNotificationTargetDefinition::class, true), + new IntField('response_http_code', 'responseHttpCode'), + (new LongTextField('message', 'message')), + new DateTimeField('last_execution_time', 'lastExecutionTime'), + (new DateTimeField('next_execution_time', 'nextExecutionTime'))->addFlags(new Required()), + ]); + } +} diff --git a/src/DataAbstractionLayer/Entity/NotificationQueue/PayonePaymentNotificationQueueEntity.php b/src/DataAbstractionLayer/Entity/NotificationQueue/PayonePaymentNotificationQueueEntity.php new file mode 100644 index 000000000..90ca2852e --- /dev/null +++ b/src/DataAbstractionLayer/Entity/NotificationQueue/PayonePaymentNotificationQueueEntity.php @@ -0,0 +1,75 @@ +notificationTargetId; + } + + public function setNotificationTargetId(string $notificationTargetId): void + { + $this->notificationTargetId = $notificationTargetId; + } + + public function getResponseHttpCode(): ?int + { + return $this->responseHttpCode; + } + + public function setResponseHttpCode(?int $responseHttpCode): void + { + $this->responseHttpCode = $responseHttpCode; + } + + public function getMessage(): ?string + { + return $this->message; + } + + public function setMessage(?string $message): void + { + $this->message = $message; + } + + public function getLastExecutionTime(): ?\DateTimeInterface + { + return $this->lastExecutionTime; + } + + public function setLastExecutionTime(?\DateTimeInterface $lastExecutionTime): void + { + $this->lastExecutionTime = $lastExecutionTime; + } + + public function setNextExecutionTime(\DateTimeInterface $nextExecutionTime): void + { + $this->nextExecutionTime = $nextExecutionTime; + } + + public function getNextExecutionTime(): \DateTimeInterface + { + return $this->nextExecutionTime; + } +} diff --git a/src/DataAbstractionLayer/Entity/NotificationTarget/PayonePaymentNotificationTargetDefinition.php b/src/DataAbstractionLayer/Entity/NotificationTarget/PayonePaymentNotificationTargetDefinition.php index 6e9240358..2905e5f95 100644 --- a/src/DataAbstractionLayer/Entity/NotificationTarget/PayonePaymentNotificationTargetDefinition.php +++ b/src/DataAbstractionLayer/Entity/NotificationTarget/PayonePaymentNotificationTargetDefinition.php @@ -42,6 +42,9 @@ protected function defineFields(): FieldCollection (new JsonField('txactions', 'txactions')), (new StringField('username', 'username')), (new StringField('password', 'password')), + (new BoolField('resend_notification', 'resendNotification')), + (new JsonField('resend_notification_time', 'resendNotificationTime')), + (new JsonField('resend_notification_status', 'resendNotificationStatus')), ]); } } diff --git a/src/DataAbstractionLayer/Entity/NotificationTarget/PayonePaymentNotificationTargetEntity.php b/src/DataAbstractionLayer/Entity/NotificationTarget/PayonePaymentNotificationTargetEntity.php index 0d84b2881..3d08572fa 100644 --- a/src/DataAbstractionLayer/Entity/NotificationTarget/PayonePaymentNotificationTargetEntity.php +++ b/src/DataAbstractionLayer/Entity/NotificationTarget/PayonePaymentNotificationTargetEntity.php @@ -17,8 +17,13 @@ class PayonePaymentNotificationTargetEntity extends Entity protected array $txactions; - protected ?string $username = null; + protected bool $resendNotification; + + protected array $resendNotificationTime; + + protected array $resendNotificationStatus; + protected ?string $username = null; protected ?string $password = null; public function getUrl(): string @@ -70,4 +75,34 @@ public function setPassword(?string $password): void { $this->password = $password; } + + public function getResendNotification(): bool + { + return $this->resendNotification; + } + + public function setResendNotification(bool $resendNotification): void + { + $this->resendNotification = $resendNotification; + } + + public function setResendNotificationTime(array $resendNotificationTime): void + { + $this->resendNotificationTime = $resendNotificationTime; + } + public function getResendNotificationTime(): array + { + return $this->resendNotificationTime; + } + + public function setResendNotificationStatus(array $resendNotificationStatus): void + { + $this->resendNotificationStatus = $resendNotificationStatus; + } + + public function getResendNotificationStatus(): array + { + return $this->resendNotificationStatus; + } + } diff --git a/src/DependencyInjection/commands.xml b/src/DependencyInjection/commands.xml new file mode 100644 index 000000000..7ea90564b --- /dev/null +++ b/src/DependencyInjection/commands.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/src/DependencyInjection/entities.xml b/src/DependencyInjection/entities.xml index 585c44243..82051616d 100644 --- a/src/DependencyInjection/entities.xml +++ b/src/DependencyInjection/entities.xml @@ -20,6 +20,10 @@ + + + + diff --git a/src/DependencyInjection/handler/global_handlers.xml b/src/DependencyInjection/handler/global_handlers.xml index ff87363ac..d2070ce90 100644 --- a/src/DependencyInjection/handler/global_handlers.xml +++ b/src/DependencyInjection/handler/global_handlers.xml @@ -55,5 +55,10 @@ + + + + + diff --git a/src/DependencyInjection/scheduled_tasks.xml b/src/DependencyInjection/scheduled_tasks.xml index 18a524c9c..3bfc7dff3 100644 --- a/src/DependencyInjection/scheduled_tasks.xml +++ b/src/DependencyInjection/scheduled_tasks.xml @@ -14,5 +14,15 @@ + + + + + + + + + + diff --git a/src/DependencyInjection/services.xml b/src/DependencyInjection/services.xml index 95e1ff5c3..9631a0ae6 100644 --- a/src/DependencyInjection/services.xml +++ b/src/DependencyInjection/services.xml @@ -17,6 +17,7 @@ + diff --git a/src/DependencyInjection/webhooks.xml b/src/DependencyInjection/webhooks.xml index 8cea1b92d..c6b305204 100644 --- a/src/DependencyInjection/webhooks.xml +++ b/src/DependencyInjection/webhooks.xml @@ -38,6 +38,7 @@ + diff --git a/src/Migration/Migration1709142760AlterNotificationTargetTable.php b/src/Migration/Migration1709142760AlterNotificationTargetTable.php new file mode 100644 index 000000000..206fe16aa --- /dev/null +++ b/src/Migration/Migration1709142760AlterNotificationTargetTable.php @@ -0,0 +1,31 @@ +executeStatement($sql); + } + + public function updateDestructive(Connection $connection): void + { + // implement update destructive + } +} diff --git a/src/Migration/Migration1709142764NotificationQueue.php b/src/Migration/Migration1709142764NotificationQueue.php new file mode 100644 index 000000000..075f05226 --- /dev/null +++ b/src/Migration/Migration1709142764NotificationQueue.php @@ -0,0 +1,36 @@ +executeStatement($sql); + } + + public function updateDestructive(Connection $connection): void + { + // implement update destructive + } +} diff --git a/src/Payone/Webhook/MessageBus/MessageHandler/NotificationForwardHandler.php b/src/Payone/Webhook/MessageBus/MessageHandler/NotificationForwardHandler.php index 3523f8cc1..5da801ebc 100644 --- a/src/Payone/Webhook/MessageBus/MessageHandler/NotificationForwardHandler.php +++ b/src/Payone/Webhook/MessageBus/MessageHandler/NotificationForwardHandler.php @@ -18,8 +18,10 @@ class NotificationForwardHandler implements MessageSubscriberInterface { + public function __construct( private readonly EntityRepository $notificationForwardRepository, + private readonly EntityRepository $notificationQueueRepository, private readonly Logger $logger ) { } @@ -29,7 +31,7 @@ public function __invoke(NotificationForwardCommand $message): void $this->handle($message); } - public function handle(NotificationForwardCommand $message): void + public function handle(NotificationForwardCommand $message, bool $isResendMessage = false): void { $notificationForwards = $this->getNotificationForwards($message->getNotificationTargetIds(), $message->getContext()); $multiHandle = curl_multi_init(); @@ -40,6 +42,7 @@ public function handle(NotificationForwardCommand $message): void if (empty($forwardRequests)) { curl_multi_close($multiHandle); + $this->logger->error('No notification targets found'); return; } @@ -58,6 +61,10 @@ public function handle(NotificationForwardCommand $message): void $responseInfo = curl_getinfo($handle); $responseContent = curl_multi_getcontent($handle); $this->statusLogger($responseInfo, $responseContent, $id); + if(!$isResendMessage) { + $this->createNotificationQueue($responseInfo, $message, $id, $notificationForwards->get($id)); + } + curl_multi_remove_handle($multiHandle, $handle); curl_close($handle); } @@ -115,6 +122,8 @@ private function getForwardRequests(\CurlMultiHandle $multiHandle, EntitySearchR $target = $forward->getNotificationTarget(); if ($target === null || empty($target->getUrl())) { + $this->logger->error('No target found for notification forward', ['id' => $id]); + continue; } @@ -125,6 +134,7 @@ private function getForwardRequests(\CurlMultiHandle $multiHandle, EntitySearchR $content = mb_convert_encoding($serialize, 'ISO-8859-1', 'UTF-8'); if (!\is_array($content)) { + $this->logger->error('Could not convert content to array', ['id' => $id]); continue; } @@ -160,6 +170,44 @@ private function buildHeaders( return $headers; } + private function createNotificationQueue($responseInfo, $message, $notificationForwardId, $forwardRequest): void + { + $this->logger->info('Creating notification queue', ['id' => $notificationForwardId]); + + $resendNotificationStatus = $forwardRequest->getNotificationTarget()?->getResendNotificationStatus(); + if (!in_array($responseInfo['http_code'], $resendNotificationStatus, true)) { + return; + } + + $resendNotificationTime = $forwardRequest->getNotificationTarget()?->getResendNotificationTime(); + + if ($resendNotificationTime === null) { + return; + } + + foreach ($resendNotificationTime as $time) { + $nextExecutionTime = new \DateTime(); + $nextExecutionTime->modify("+$time minutes"); + try { + $this->notificationQueueRepository->create([ + [ + 'notificationTargetId' => $notificationForwardId, + 'responseHttpCode' => $responseInfo['http_code'], + 'message' => base64_encode(serialize($message)), + 'lastExecutionTime' => new \DateTime(), + 'nextExecutionTime' => $nextExecutionTime, + ], + ], $message->getContext()); + }catch (\Exception $e) { + $this->logger->error('Could not create notification queue', [ + 'notificationForwardId' => $notificationForwardId, + 'nextExecutionTime' => $nextExecutionTime->format('Y-m-d H:i:s'), + 'error' => $e->getMessage() + ]); + } + } + } + private function statusLogger(array $responseInfo, ?string $responseContent, string $id): void { $statusCode = $responseInfo['http_code']; diff --git a/src/Resources/app/administration/src/module/payone-notification-target/page/payone-notification-target-detail/index.js b/src/Resources/app/administration/src/module/payone-notification-target/page/payone-notification-target-detail/index.js index 059c09689..2ec15a1a7 100644 --- a/src/Resources/app/administration/src/module/payone-notification-target/page/payone-notification-target-detail/index.js +++ b/src/Resources/app/administration/src/module/payone-notification-target/page/payone-notification-target-detail/index.js @@ -45,6 +45,73 @@ export default { notificationTargetRepository() { return this.repositoryFactory.create('payone_payment_notification_target'); + }, + + resendNotificationOptionValues() { + return [ + {value: 100, label: '(Http status = 100) Continue'}, + {value: 101, label: '(Http status = 101) Switching Protocols'}, + {value: 102, label: '(Http status = 102) Processing'}, // RFC2518 + {value: 103, label: '(Http status = 103) Early Hints'}, + {value: 200, label: '(Http status = 200) OK'}, + {value: 201, label: '(Http status = 201) Created'}, + {value: 202, label: '(Http status = 202) Accepted'}, + {value: 203, label: '(Http status = 203) Non-Authoritative Information'}, + {value: 204, label: '(Http status = 204) No Content'}, + {value: 205, label: '(Http status = 205) Reset Content'}, + {value: 206, label: '(Http status = 206) Partial Content'}, + {value: 207, label: '(Http status = 207) Multi-Status'}, // RFC4918 + {value: 208, label: '(Http status = 208) Already Reported'}, // RFC5842 + {value: 226, label: '(Http status = 226) IM Used'}, // RFC3229 + {value: 300, label: '(Http status = 300) Multiple Choices'}, + {value: 301, label: '(Http status = 301) Moved Permanently'}, + {value: 302, label: '(Http status = 302) Found'}, + {value: 303, label: '(Http status = 303) See Other'}, + {value: 304, label: '(Http status = 304) Not Modified'}, + {value: 305, label: '(Http status = 305) Use Proxy'}, + {value: 307, label: '(Http status = 307) Temporary Redirect'}, + {value: 308, label: '(Http status = 308) Permanent Redirect'}, // RFC7238 + {value: 400, label: '(Http status = 400) Bad Request'}, + {value: 401, label: '(Http status = 401) Unauthorized'}, + {value: 402, label: '(Http status = 402) Payment Required'}, + {value: 403, label: '(Http status = 403) Forbidden'}, + {value: 404, label: '(Http status = 404) Not Found'}, + {value: 405, label: '(Http status = 405) Method Not Allowed'}, + {value: 406, label: '(Http status = 406) Not Acceptable'}, + {value: 407, label: '(Http status = 407) Proxy Authentication Required'}, + {value: 408, label: '(Http status = 408) Request Timeout'}, + {value: 409, label: '(Http status = 409) Conflict'}, + {value: 410, label: '(Http status = 410) Gone'}, + {value: 411, label: '(Http status = 411) Length Required'}, + {value: 412, label: '(Http status = 412) Precondition Failed'}, + {value: 413, label: '(Http status = 413) Content Too Large'}, // RFC-ietf-httpbis-semantics + {value: 414, label: '(Http status = 414) URI Too Long'}, + {value: 415, label: '(Http status = 415) Unsupported Media Type'}, + {value: 416, label: '(Http status = 416) Range Not Satisfiable'}, + {value: 417, label: '(Http status = 417) Expectation Failed'}, + {value: 418, label: '(Http status = 418) I\'m a teapot'}, // RFC2324 + {value: 421, label: '(Http status = 421) Misdirected Request'}, // RFC7540 + {value: 422, label: '(Http status = 422) Unprocessable Content'}, // RFC-ietf-httpbis-semantics + {value: 423, label: '(Http status = 423) Locked'}, // RFC4918 + {value: 424, label: '(Http status = 424) Failed Dependency'}, // RFC4918 + {value: 425, label: '(Http status = 425) Too Early'}, // RFC-ietf-httpbis-replay-04 + {value: 426, label: '(Http status = 426) Upgrade Required'}, // RFC2817 + {value: 428, label: '(Http status = 428) Precondition Required'}, // RFC6585 + {value: 429, label: '(Http status = 429) Too Many Requests'}, // RFC6585 + {value: 431, label: '(Http status = 431) Request Header Fields Too Large'}, // RFC6585 + {value: 451, label: '(Http status = 451) Unavailable For Legal Reasons'}, // RFC7725 + {value: 500, label: '(Http status = 500) Internal Server Error'}, + {value: 501, label: '(Http status = 501) Not Implemented'}, + {value: 502, label: '(Http status = 502) Bad Gateway'}, + {value: 503, label: '(Http status = 503) Service Unavailable'}, + {value: 504, label: '(Http status = 504) Gateway Timeout'}, + {value: 505, label: '(Http status = 505) HTTP Version Not Supported'}, + {value: 506, label: '(Http status = 506) Variant Also Negotiates'}, // RFC2295 + {value: 507, label: '(Http status = 507) Insufficient Storage'}, // RFC4918 + {value: 508, label: '(Http status = 508) Loop Detected'}, // RFC5842 + {value: 510, label: '(Http status = 510) Not Extended'}, // RFC2774 + {value: 511, label: '(Http status = 511) Network Authentication Required'} + ] } }, @@ -63,8 +130,16 @@ export default { this.notificationTarget.txactions = value; }, + updateResendNotificationTime(value) { + this.notificationTarget.resendNotificationTime = value; + }, + + updateResendNotificationStatus(value) { + this.notificationTarget.resendNotificationStatus = value; + }, + createdComponent() { - if (this.notificationTargetId) { + if(this.notificationTargetId) { this.loadEntityData(); return; } @@ -92,21 +167,32 @@ export default { }, isInvalid() { - if(this.notificationTarget.isBasicAuth !== true ) { - return false; + let errorMessages = []; + if(this.notificationTarget.isBasicAuth === true) { + if(!this.notificationTarget.username || !this.notificationTarget.password) { + errorMessages.push(this.$tc('payonePayment.notificationTarget.detail.messages.notificationSaveErrorMessageRequiredUserPassInvalid')); + } } - if(this.notificationTarget.username && this.notificationTarget.password) { - return false; + if(this.notificationTarget.resendNotification === true) { + if(this.notificationTarget.resendNotificationTime.length === 0) { + errorMessages.push(this.$tc('payonePayment.notificationTarget.detail.messages.notificationSaveErrorMessageRequiredResendNotificationTimeInvalid')); + } + if(this.notificationTarget.resendNotificationStatus.length === 0) { + errorMessages.push(this.$tc('payonePayment.notificationTarget.detail.messages.notificationSaveErrorMessageRequiredResendNotificationStatusInvalid')); + } } - this.createNotificationError({ - message: this.$tc( - 'global.notification.notificationSaveErrorMessageRequiredFieldsInvalid' - ) - }); + if(errorMessages.length > 0) { + errorMessages.forEach((message) => { + this.createNotificationError({ + message: message + }); + }); - return true; + return true; + } + return false; }, onSave() { @@ -138,6 +224,20 @@ export default { onCancel() { this.$router.push({ name: 'payone.notification.target.list' }); + }, + + getTimeValueTranslate(value, type) { + if(value === null || value === undefined) { + return ''; + } + + if(type === 'minutes') { + return this.$tc('payonePayment.notificationTarget.detail.resendNotificationTimeValues.xMinutes', 0, {value: value}) + } + + if(type === 'hours') { + return this.$tc('payonePayment.notificationTarget.detail.resendNotificationTimeValues.xHours', 0, {value: value}) + } } } }; diff --git a/src/Resources/app/administration/src/module/payone-notification-target/page/payone-notification-target-detail/payone-notification-target-detail.html.twig b/src/Resources/app/administration/src/module/payone-notification-target/page/payone-notification-target-detail/payone-notification-target-detail.html.twig index c445ff699..3becbcd05 100644 --- a/src/Resources/app/administration/src/module/payone-notification-target/page/payone-notification-target-detail/payone-notification-target-detail.html.twig +++ b/src/Resources/app/administration/src/module/payone-notification-target/page/payone-notification-target-detail/payone-notification-target-detail.html.twig @@ -41,7 +41,6 @@ columns="repeat(auto-fit, minmax(250px, 1fr))" gap="0 30px">
- {% block payone_notification_target_detail_base_info_field_url %} {% endblock %} + {% block payone_notification_target_detail_base_info_field_is_resend_notification %} + + + {% endblock %} + + {% block payone_notification_target_detail_base_info_field_resend_notification_times %} + + + {% endblock %} + + {% block payone_notification_target_detail_base_info_field_resend_notification_status %} + + + {% endblock %} +
diff --git a/src/Resources/app/administration/src/module/payone-notification-target/page/payone-notification-target-list/index.js b/src/Resources/app/administration/src/module/payone-notification-target/page/payone-notification-target-list/index.js index d089d677f..dabc71836 100644 --- a/src/Resources/app/administration/src/module/payone-notification-target/page/payone-notification-target-list/index.js +++ b/src/Resources/app/administration/src/module/payone-notification-target/page/payone-notification-target-list/index.js @@ -45,6 +45,11 @@ export default { property: 'txactions', label: 'payonePayment.notificationTarget.columns.txactions' }, + { + dataIndex: 'resendNotification', + property: 'resendNotification', + label: 'payonePayment.notificationTarget.columns.resendNotification' + }, ]; }, repository() { diff --git a/src/Resources/app/administration/src/module/payone-notification-target/page/payone-notification-target-list/payone-notification-target-list.html.twig b/src/Resources/app/administration/src/module/payone-notification-target/page/payone-notification-target-list/payone-notification-target-list.html.twig index bf971a26d..bd8887855 100644 --- a/src/Resources/app/administration/src/module/payone-notification-target/page/payone-notification-target-list/payone-notification-target-list.html.twig +++ b/src/Resources/app/administration/src/module/payone-notification-target/page/payone-notification-target-list/payone-notification-target-list.html.twig @@ -73,6 +73,15 @@ {{ renderTxactions(item.txactions) }} {% endblock %} + + {% block payone_notification_target_list_grid_columns_is_resend_notification %} + + {% endblock %} + + {% endblock %} {% endblock %} diff --git a/src/Resources/app/administration/src/module/payone-notification-target/snippet/de_DE.json b/src/Resources/app/administration/src/module/payone-notification-target/snippet/de_DE.json index 09fe70b91..a0bea853d 100644 --- a/src/Resources/app/administration/src/module/payone-notification-target/snippet/de_DE.json +++ b/src/Resources/app/administration/src/module/payone-notification-target/snippet/de_DE.json @@ -21,16 +21,30 @@ "url": "Url", "isBasicAuth": "Basic Auth", "txactions": "txactions", + "resendNotification": "Benachrichtigung erneut senden", + "resendNotificationTime": "Zeit für erneutes Senden der Benachrichtigung", + "resendNotificationStatus": "Benachrichtigung erneut senden, wenn Status gleich:", "buttonSave": "Speichern", "buttonCancel": "Abbrechen", "username": "Benutzer", "password": "Passwort" + }, + "resendNotificationTimeValues": { + "xMinutes": "{Wert} Minuten", + "xHour": "{Wert} Stunde", + "xHours": "{Wert} Stunden" + }, + "messages": { + "notificationSaveErrorMessageRequiredUserPassInvalid": "Bitte geben Sie einen gültigen Benutzernamen und ein gültiges Passwort ein.", + "notificationSaveErrorMessageRequiredResendNotificationTimeInvalid": "Bitte geben Sie eine Zeit für erneutes Senden der Benachrichtigung ein.", + "notificationSaveErrorMessageRequiredResendNotificationStatusInvalid": "Bitte geben Sie einen Status für erneutes Senden der Benachrichtigung ein." } }, "columns": { "url": "Url", "isBasicAuth": "Basic Auth", - "txactions": "txactions" + "txactions": "txactions", + "resendNotification": "Benachrichtigung erneut senden", }, "actions": { "requeue": "Erneut senden" diff --git a/src/Resources/app/administration/src/module/payone-notification-target/snippet/en_GB.json b/src/Resources/app/administration/src/module/payone-notification-target/snippet/en_GB.json index 1d0e8102f..92b97785b 100644 --- a/src/Resources/app/administration/src/module/payone-notification-target/snippet/en_GB.json +++ b/src/Resources/app/administration/src/module/payone-notification-target/snippet/en_GB.json @@ -21,16 +21,30 @@ "url": "Url", "isBasicAuth": "Basic Auth", "txactions": "txactions", + "resendNotification": "Resend notification", + "resendNotificationTime": "Resend notification time", + "resendNotificationStatus": "Resend notification if status equal:", "buttonSave": "Save", "buttonCancel": "Cancel", "username": "Username", "password": "Password" + }, + "resendNotificationTimeValues": { + "xMinutes": "{value} minutes", + "xHour": "{value} hour", + "xHours": "{value} hours" + }, + "messages": { + "notificationSaveErrorMessageRequiredUserPassInvalid": "Please enter a valid username and password.", + "notificationSaveErrorMessageRequiredResendNotificationTimeInvalid": "Please enter a resend notification time.", + "notificationSaveErrorMessageRequiredResendNotificationStatusInvalid": "Please enter a resend notification status." } }, "columns": { "url": "Url", "isBasicAuth": "Basic Auth", - "txactions": "txactions" + "txactions": "txactions", + "resendNotification": "Resend notification" }, "actions": { "requeue": "Requeue" diff --git a/src/ScheduledTask/NotifySendRetry.php b/src/ScheduledTask/NotifySendRetry.php new file mode 100644 index 000000000..364b20953 --- /dev/null +++ b/src/ScheduledTask/NotifySendRetry.php @@ -0,0 +1,22 @@ +resendNotifyHandler->send(); + } +} From b81d4099ab270d926ee8af533f2bb5d7cf5717d5 Mon Sep 17 00:00:00 2001 From: amirinterlutions Date: Fri, 1 Mar 2024 14:21:51 +0100 Subject: [PATCH 2/4] PAYOSWXP-121: fixes phpstan and ecs --- src/Command/ResendNotifyCommand.php | 6 ++--- .../ResendNotifyHandler.php | 12 ++++++---- .../PayonePaymentNotificationQueueEntity.php | 4 ++-- .../PayonePaymentNotificationTargetEntity.php | 3 ++- .../NotificationForwardHandler.php | 23 +++++++++++-------- src/ScheduledTask/NotifySendRetryHandler.php | 1 - 6 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/Command/ResendNotifyCommand.php b/src/Command/ResendNotifyCommand.php index 93f09cd47..316c750b3 100644 --- a/src/Command/ResendNotifyCommand.php +++ b/src/Command/ResendNotifyCommand.php @@ -2,10 +2,10 @@ namespace PayonePayment\Command; +use PayonePayment\Components\ResendNotifyHandler\ResendNotifyHandler; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use PayonePayment\Components\ResendNotifyHandler\ResendNotifyHandler; class ResendNotifyCommand extends Command { @@ -13,8 +13,7 @@ class ResendNotifyCommand extends Command public function __construct( private readonly ResendNotifyHandler $resendNotifyHandler - ) - { + ) { parent::__construct(); } @@ -23,7 +22,6 @@ protected function configure(): void $this->setDescription('Send notification forwarding queue'); } - protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln('Sending notification forwarding queue'); diff --git a/src/Components/ResendNotifyHandler/ResendNotifyHandler.php b/src/Components/ResendNotifyHandler/ResendNotifyHandler.php index a0974c8d2..80ac33d3f 100644 --- a/src/Components/ResendNotifyHandler/ResendNotifyHandler.php +++ b/src/Components/ResendNotifyHandler/ResendNotifyHandler.php @@ -4,14 +4,14 @@ namespace PayonePayment\Components\ResendNotifyHandler; +use PayonePayment\DataAbstractionLayer\Entity\NotificationQueue\PayonePaymentNotificationQueueEntity; use PayonePayment\Payone\Webhook\MessageBus\Command\NotificationForwardCommand; +use PayonePayment\Payone\Webhook\MessageBus\MessageHandler\NotificationForwardHandler; use Shopware\Core\Defaults; use Shopware\Core\Framework\Context; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\RangeFilter; -use PayonePayment\DataAbstractionLayer\Entity\NotificationQueue\PayonePaymentNotificationQueueEntity; -use PayonePayment\Payone\Webhook\MessageBus\MessageHandler\NotificationForwardHandler; class ResendNotifyHandler { @@ -26,18 +26,20 @@ public function send(): void $criteria = new Criteria(); $currentDate = new \DateTime(); $criteria->addFilter(new RangeFilter('nextExecutionTime', [ - RangeFilter::LTE => $currentDate->format(Defaults::STORAGE_DATE_TIME_FORMAT) + RangeFilter::LTE => $currentDate->format(Defaults::STORAGE_DATE_TIME_FORMAT), ])); $notificationQueue = $this->notificationQueueRepository->search($criteria, Context::createDefaultContext())->getEntities(); /** @var PayonePaymentNotificationQueueEntity $notification */ foreach ($notificationQueue as $notification) { + $messageDecode = base64_decode($notification->getMessage() ?: '', true); /** @var NotificationForwardCommand $message */ - $message = unserialize(base64_decode($notification->getMessage()),[]); + $message = unserialize($messageDecode, []); + $this->notificationForwardHandler->handle($message, true); $this->notificationQueueRepository->delete([['id' => $notification->getId()]], Context::createDefaultContext()); } - echo "current time: " . $currentDate->format(Defaults::STORAGE_DATE_TIME_FORMAT) . "\n"; + echo 'current time: ' . $currentDate->format(Defaults::STORAGE_DATE_TIME_FORMAT) . "\n"; } } diff --git a/src/DataAbstractionLayer/Entity/NotificationQueue/PayonePaymentNotificationQueueEntity.php b/src/DataAbstractionLayer/Entity/NotificationQueue/PayonePaymentNotificationQueueEntity.php index 90ca2852e..44c6a9c99 100644 --- a/src/DataAbstractionLayer/Entity/NotificationQueue/PayonePaymentNotificationQueueEntity.php +++ b/src/DataAbstractionLayer/Entity/NotificationQueue/PayonePaymentNotificationQueueEntity.php @@ -53,12 +53,12 @@ public function setMessage(?string $message): void $this->message = $message; } - public function getLastExecutionTime(): ?\DateTimeInterface + public function getLastExecutionTime(): \DateTimeInterface { return $this->lastExecutionTime; } - public function setLastExecutionTime(?\DateTimeInterface $lastExecutionTime): void + public function setLastExecutionTime(\DateTimeInterface $lastExecutionTime): void { $this->lastExecutionTime = $lastExecutionTime; } diff --git a/src/DataAbstractionLayer/Entity/NotificationTarget/PayonePaymentNotificationTargetEntity.php b/src/DataAbstractionLayer/Entity/NotificationTarget/PayonePaymentNotificationTargetEntity.php index 3d08572fa..2ac010133 100644 --- a/src/DataAbstractionLayer/Entity/NotificationTarget/PayonePaymentNotificationTargetEntity.php +++ b/src/DataAbstractionLayer/Entity/NotificationTarget/PayonePaymentNotificationTargetEntity.php @@ -24,6 +24,7 @@ class PayonePaymentNotificationTargetEntity extends Entity protected array $resendNotificationStatus; protected ?string $username = null; + protected ?string $password = null; public function getUrl(): string @@ -90,6 +91,7 @@ public function setResendNotificationTime(array $resendNotificationTime): void { $this->resendNotificationTime = $resendNotificationTime; } + public function getResendNotificationTime(): array { return $this->resendNotificationTime; @@ -104,5 +106,4 @@ public function getResendNotificationStatus(): array { return $this->resendNotificationStatus; } - } diff --git a/src/Payone/Webhook/MessageBus/MessageHandler/NotificationForwardHandler.php b/src/Payone/Webhook/MessageBus/MessageHandler/NotificationForwardHandler.php index 5da801ebc..0c6e32f2a 100644 --- a/src/Payone/Webhook/MessageBus/MessageHandler/NotificationForwardHandler.php +++ b/src/Payone/Webhook/MessageBus/MessageHandler/NotificationForwardHandler.php @@ -18,7 +18,6 @@ class NotificationForwardHandler implements MessageSubscriberInterface { - public function __construct( private readonly EntityRepository $notificationForwardRepository, private readonly EntityRepository $notificationQueueRepository, @@ -61,8 +60,11 @@ public function handle(NotificationForwardCommand $message, bool $isResendMessag $responseInfo = curl_getinfo($handle); $responseContent = curl_multi_getcontent($handle); $this->statusLogger($responseInfo, $responseContent, $id); - if(!$isResendMessage) { - $this->createNotificationQueue($responseInfo, $message, $id, $notificationForwards->get($id)); + if (!$isResendMessage) { + $notificationForward = $notificationForwards->get($id); + if ($notificationForward instanceof PayonePaymentNotificationForwardEntity) { + $this->createNotificationQueue($responseInfo, $message, $id, $notificationForward); + } } curl_multi_remove_handle($multiHandle, $handle); @@ -135,6 +137,7 @@ private function getForwardRequests(\CurlMultiHandle $multiHandle, EntitySearchR if (!\is_array($content)) { $this->logger->error('Could not convert content to array', ['id' => $id]); + continue; } @@ -170,16 +173,17 @@ private function buildHeaders( return $headers; } - private function createNotificationQueue($responseInfo, $message, $notificationForwardId, $forwardRequest): void + private function createNotificationQueue(array $responseInfo, NotificationForwardCommand $message, string $notificationForwardId, PayonePaymentNotificationForwardEntity $notificationForward): void { $this->logger->info('Creating notification queue', ['id' => $notificationForwardId]); - $resendNotificationStatus = $forwardRequest->getNotificationTarget()?->getResendNotificationStatus(); - if (!in_array($responseInfo['http_code'], $resendNotificationStatus, true)) { + + $resendNotificationStatus = $notificationForward->getNotificationTarget()?->getResendNotificationStatus() ?? []; + if (!\in_array($responseInfo['http_code'], $resendNotificationStatus, true)) { return; } - $resendNotificationTime = $forwardRequest->getNotificationTarget()?->getResendNotificationTime(); + $resendNotificationTime = $notificationForward->getNotificationTarget()?->getResendNotificationTime(); if ($resendNotificationTime === null) { return; @@ -188,6 +192,7 @@ private function createNotificationQueue($responseInfo, $message, $notificationF foreach ($resendNotificationTime as $time) { $nextExecutionTime = new \DateTime(); $nextExecutionTime->modify("+$time minutes"); + try { $this->notificationQueueRepository->create([ [ @@ -198,11 +203,11 @@ private function createNotificationQueue($responseInfo, $message, $notificationF 'nextExecutionTime' => $nextExecutionTime, ], ], $message->getContext()); - }catch (\Exception $e) { + } catch (\Exception $e) { $this->logger->error('Could not create notification queue', [ 'notificationForwardId' => $notificationForwardId, 'nextExecutionTime' => $nextExecutionTime->format('Y-m-d H:i:s'), - 'error' => $e->getMessage() + 'error' => $e->getMessage(), ]); } } diff --git a/src/ScheduledTask/NotifySendRetryHandler.php b/src/ScheduledTask/NotifySendRetryHandler.php index b5d8d0322..b0a41a681 100644 --- a/src/ScheduledTask/NotifySendRetryHandler.php +++ b/src/ScheduledTask/NotifySendRetryHandler.php @@ -10,7 +10,6 @@ class NotifySendRetryHandler extends ScheduledTaskHandler { - public function __construct( EntityRepository $scheduledTaskRepository, private readonly ResendNotifyHandler $resendNotifyHandler From 3df76a3e2a77a36c3174e5f5a572ebf4c219f35c Mon Sep 17 00:00:00 2001 From: amirinterlutions Date: Fri, 1 Mar 2024 14:46:06 +0100 Subject: [PATCH 3/4] PAYOSWXP-121: fixes phpstan --- .../ResendNotifyHandler.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Components/ResendNotifyHandler/ResendNotifyHandler.php b/src/Components/ResendNotifyHandler/ResendNotifyHandler.php index 80ac33d3f..919f9d951 100644 --- a/src/Components/ResendNotifyHandler/ResendNotifyHandler.php +++ b/src/Components/ResendNotifyHandler/ResendNotifyHandler.php @@ -33,13 +33,19 @@ public function send(): void /** @var PayonePaymentNotificationQueueEntity $notification */ foreach ($notificationQueue as $notification) { - $messageDecode = base64_decode($notification->getMessage() ?: '', true); - /** @var NotificationForwardCommand $message */ - $message = unserialize($messageDecode, []); - - $this->notificationForwardHandler->handle($message, true); - $this->notificationQueueRepository->delete([['id' => $notification->getId()]], Context::createDefaultContext()); + $messageDecode = null; + if ($notification->getMessage() !== null) { + $messageDecode = base64_decode($notification->getMessage(), true); + } + if ($messageDecode !== null) { + /** @var NotificationForwardCommand $message */ + $message = unserialize($messageDecode, []); + + $this->notificationForwardHandler->handle($message, true); + $this->notificationQueueRepository->delete([['id' => $notification->getId()]], Context::createDefaultContext()); + } } + echo 'current time: ' . $currentDate->format(Defaults::STORAGE_DATE_TIME_FORMAT) . "\n"; } } From 329c7c884d80cfb3901fca82aed37ed739d6efdd Mon Sep 17 00:00:00 2001 From: amirinterlutions Date: Fri, 1 Mar 2024 14:50:29 +0100 Subject: [PATCH 4/4] PAYOSWXP-121: fixes phpstan --- src/Components/ResendNotifyHandler/ResendNotifyHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/ResendNotifyHandler/ResendNotifyHandler.php b/src/Components/ResendNotifyHandler/ResendNotifyHandler.php index 919f9d951..53c6da6da 100644 --- a/src/Components/ResendNotifyHandler/ResendNotifyHandler.php +++ b/src/Components/ResendNotifyHandler/ResendNotifyHandler.php @@ -35,7 +35,7 @@ public function send(): void foreach ($notificationQueue as $notification) { $messageDecode = null; if ($notification->getMessage() !== null) { - $messageDecode = base64_decode($notification->getMessage(), true); + $messageDecode = base64_decode($notification->getMessage()); } if ($messageDecode !== null) { /** @var NotificationForwardCommand $message */