From 37627f7572ab2f9c8255ba95526b1bb70e8278c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Klatt?= Date: Thu, 25 Jul 2024 10:03:48 +0200 Subject: [PATCH] ACP-2626 ABort Webhook handling when payment is in state cancelled and a refund webhook is called (#11) --- .../Transfer/app_payment.transfer.xml | 1 + .../Payment/Webhook/WebhookHandler.php | 5 +++++ .../Webhook/WebhookRequestExtender.php | 7 +++++++ .../RestApi/WebhooksRefundApiTest.php | 20 +++++++++++++++++++ 4 files changed, 33 insertions(+) diff --git a/src/Spryker/Shared/AppPayment/Transfer/app_payment.transfer.xml b/src/Spryker/Shared/AppPayment/Transfer/app_payment.transfer.xml index f78fb9f..ee8713b 100644 --- a/src/Spryker/Shared/AppPayment/Transfer/app_payment.transfer.xml +++ b/src/Spryker/Shared/AppPayment/Transfer/app_payment.transfer.xml @@ -168,6 +168,7 @@ + diff --git a/src/Spryker/Zed/AppPayment/Business/Payment/Webhook/WebhookHandler.php b/src/Spryker/Zed/AppPayment/Business/Payment/Webhook/WebhookHandler.php index 0f2bc35..eb3b6c5 100644 --- a/src/Spryker/Zed/AppPayment/Business/Payment/Webhook/WebhookHandler.php +++ b/src/Spryker/Zed/AppPayment/Business/Payment/Webhook/WebhookHandler.php @@ -36,6 +36,11 @@ public function handleWebhook(WebhookRequestTransfer $webhookRequestTransfer, We try { $webhookRequestTransfer = $this->extendWebhookRequestTransfer($webhookRequestTransfer); + if ($webhookRequestTransfer->getAbortHandling() === true) { + // This will result in a 200 OK Response send back to the caller of the webhook endpoint. + return $webhookResponseTransfer->setIsSuccessful(true); + } + $webhookResponseTransfer = $this->appPaymentPlatformPlugin->handleWebhook($webhookRequestTransfer, $webhookResponseTransfer); } catch (Throwable $throwable) { $this->getLogger()->error($throwable->getMessage(), [ diff --git a/src/Spryker/Zed/AppPayment/Business/Payment/Webhook/WebhookRequestExtender.php b/src/Spryker/Zed/AppPayment/Business/Payment/Webhook/WebhookRequestExtender.php index 380a512..9ddaa13 100644 --- a/src/Spryker/Zed/AppPayment/Business/Payment/Webhook/WebhookRequestExtender.php +++ b/src/Spryker/Zed/AppPayment/Business/Payment/Webhook/WebhookRequestExtender.php @@ -9,6 +9,7 @@ use Generated\Shared\Transfer\WebhookRequestTransfer; use Spryker\Zed\AppPayment\Business\Payment\AppConfig\AppConfigLoader; +use Spryker\Zed\AppPayment\Business\Payment\Status\PaymentStatus; use Spryker\Zed\AppPayment\Dependency\Plugin\AppPaymentPlatformPluginInterface; use Spryker\Zed\AppPayment\Persistence\AppPaymentRepositoryInterface; @@ -29,6 +30,12 @@ public function extendWebhookRequestTransfer(WebhookRequestTransfer $webhookRequ $webhookRequestTransfer->setPaymentOrFail($paymentTransfer); $webhookRequestTransfer->setAppConfigOrFail($appConfigTransfer); + // When the Payment Service Provider calls the refund webhook and the payment was cancelled before, there is no need for further process this webhook. + // This happens when the Payment is cancelled on the Tenant side before the refund process was initialized. + if ($webhookRequestTransfer->getTypeOrFail() === WebhookDataType::REFUND && $paymentTransfer->getStatus() === PaymentStatus::STATUS_CANCELED) { + return $webhookRequestTransfer->setAbortHandling(true); + } + if ($webhookRequestTransfer->getTypeOrFail() === WebhookDataType::REFUND) { $webhookRequestTransfer->setRefundOrFail( $this->appPaymentRepository->getRefundByRefundId($webhookRequestTransfer->getRefundOrFail()->getRefundIdOrFail()), diff --git a/tests/SprykerTest/Glue/AppPaymentBackendApi/RestApi/WebhooksRefundApiTest.php b/tests/SprykerTest/Glue/AppPaymentBackendApi/RestApi/WebhooksRefundApiTest.php index 87ebbfc..ba31baf 100644 --- a/tests/SprykerTest/Glue/AppPaymentBackendApi/RestApi/WebhooksRefundApiTest.php +++ b/tests/SprykerTest/Glue/AppPaymentBackendApi/RestApi/WebhooksRefundApiTest.php @@ -17,6 +17,7 @@ use Spryker\Zed\AppPayment\AppPaymentDependencyProvider; use Spryker\Zed\AppPayment\Business\Message\MessageBuilder; use Spryker\Zed\AppPayment\Business\Payment\Refund\PaymentRefundStatus; +use Spryker\Zed\AppPayment\Business\Payment\Status\PaymentStatus; use Spryker\Zed\AppPayment\Business\Payment\Webhook\WebhookDataType; use Spryker\Zed\AppPayment\Communication\Plugin\AppWebhook\PaymentWebhookHandlerPlugin; use Spryker\Zed\AppPayment\Dependency\Plugin\AppPaymentPlatformPluginInterface; @@ -146,6 +147,25 @@ public function testHandleRefundWebhookReturnsA400BadRequestWhenThereIsNoSuchPay $this->tester->seeResponseContainsErrorMessage(MessageBuilder::refundByRefundIdNotFound($refundId)); } + public function testHandleRefundWebhookReturnsA200OkWhenTheThePaymentIsInStateCancelled(): void + { + // Arrange + $transactionId = Uuid::uuid4()->toString(); + $tenantIdentifier = Uuid::uuid4()->toString(); + $refundId = Uuid::uuid4()->toString(); + + $this->tester->haveAppConfigForTenant($tenantIdentifier); + $this->tester->havePaymentForTransactionId($transactionId, $tenantIdentifier, PaymentStatus::STATUS_CANCELED); + + $this->tester->mockGlueRequestWebhookMapperPlugin(WebhookDataType::REFUND, $transactionId, $refundId); + + // Act + $this->tester->sendPost($this->tester->buildWebhookUrl(), ['web hook content received from third party payment provider']); + + // Assert + $this->tester->seeResponseCodeIs(Response::HTTP_OK); + } + public function testHandleRefundWebhookReturnsA400BadRequestWhenRefundIdHasBeenChangedByPlatformPlugin(): void { // Arrange