From fff0d38dbe6525085c633294db4c4214e3806660 Mon Sep 17 00:00:00 2001 From: Eugenia Poidenko Date: Tue, 17 Mar 2026 18:04:26 +0200 Subject: [PATCH 1/3] SOL-478 Refund support. --- .../Zed/oms/PaymentTemplateCreditCard01.xml | 15 ++++ config/Zed/oms/PaymentTemplateInvoice01.xml | 15 ++++ data/import/glossary.csv | 2 + .../Api/Refund/RefundApiRequest.php | 69 +++++++++++++++ .../Api/Refund/RefundMapper.php | 48 ++++++++++ .../PaymentTemplate/PaymentTemplateClient.php | 11 ++- .../PaymentTemplateClientInterface.php | 21 ++++- .../PaymentTemplate/PaymentTemplateConfig.php | 5 ++ .../PaymentTemplateFactory.php | 17 ++++ .../PaymentTemplate/PaymentTemplateConfig.php | 5 ++ .../PaymentTemplateConstants.php | 2 + .../Transfer/payment_template.transfer.xml | 12 +++ .../Oms/Command/OmsCommandHandler.php | 87 +++++++++++++++++++ .../Oms/Condition/OmsConditionChecker.php | 16 ++++ .../OmsConditionCheckerInterface.php | 2 + .../Business/PaymentTemplateFacade.php | 33 +++++++ .../PaymentTemplateFacadeInterface.php | 28 ++++++ .../Plugin/Oms/Command/RefundPlugin.php | 43 +++++++++ .../Plugin/Oms/Condition/IsRefundedPlugin.php | 37 ++++++++ .../PaymentTemplate/PaymentTemplateConfig.php | 16 +--- 20 files changed, 465 insertions(+), 19 deletions(-) create mode 100644 src/Spryker/Client/PaymentTemplate/Api/Refund/RefundApiRequest.php create mode 100644 src/Spryker/Client/PaymentTemplate/Api/Refund/RefundMapper.php create mode 100644 src/Spryker/Zed/PaymentTemplate/Communication/Plugin/Oms/Command/RefundPlugin.php create mode 100644 src/Spryker/Zed/PaymentTemplate/Communication/Plugin/Oms/Condition/IsRefundedPlugin.php diff --git a/config/Zed/oms/PaymentTemplateCreditCard01.xml b/config/Zed/oms/PaymentTemplateCreditCard01.xml index e825a58..a9f3826 100644 --- a/config/Zed/oms/PaymentTemplateCreditCard01.xml +++ b/config/Zed/oms/PaymentTemplateCreditCard01.xml @@ -18,6 +18,8 @@ + + @@ -63,6 +65,18 @@ captured + + + captured + refund pending + refund + + + + refund pending + refunded + + authorized @@ -79,6 +93,7 @@ + diff --git a/config/Zed/oms/PaymentTemplateInvoice01.xml b/config/Zed/oms/PaymentTemplateInvoice01.xml index 8d18816..d59fd4f 100644 --- a/config/Zed/oms/PaymentTemplateInvoice01.xml +++ b/config/Zed/oms/PaymentTemplateInvoice01.xml @@ -18,6 +18,8 @@ + + @@ -63,6 +65,18 @@ captured + + + captured + refund pending + refund + + + + refund pending + refunded + + authorized @@ -79,6 +93,7 @@ + diff --git a/data/import/glossary.csv b/data/import/glossary.csv index 3ae0ec4..956aee1 100644 --- a/data/import/glossary.csv +++ b/data/import/glossary.csv @@ -9,3 +9,5 @@ oms.state.authorization-failed,Authorization Failed,en_US oms.state.authorized,Authorized,en_US oms.state.capture-pending,Capture Pending,en_US oms.state.captured,Captured,en_US +oms.state.refund-pending,Refund Pending,en_US +oms.state.refunded,Refunded,en_US diff --git a/src/Spryker/Client/PaymentTemplate/Api/Refund/RefundApiRequest.php b/src/Spryker/Client/PaymentTemplate/Api/Refund/RefundApiRequest.php new file mode 100644 index 0000000..32c29a3 --- /dev/null +++ b/src/Spryker/Client/PaymentTemplate/Api/Refund/RefundApiRequest.php @@ -0,0 +1,69 @@ +apiLogger->logRequest($this->getUrl(), $paymentTemplateRefundRequestTransfer); + + $requestBody = $this->refundMapper->mapRequest($paymentTemplateRefundRequestTransfer); + $httpResponse = $this->sendRequest($requestBody); + + $paymentTemplateApiResponseTransfer = $this->refundMapper->mapResponse($httpResponse); + $this->apiLogger->logResponse($httpResponse, $paymentTemplateApiResponseTransfer); + + return $paymentTemplateApiResponseTransfer; + } catch (RequestException $exception) { + $this->apiLogger->logErrorRequest($exception, $exception->hasResponse() ? $exception->getResponse() : null); + + return $this->refundMapper->mapErrorResponse($exception); + } + } + + protected function sendRequest(string $requestBody): ResponseInterface + { + $requestOptions = [ + RequestOptions::HEADERS => $this->paymentTemplateConfig->getDefaultHeaders(), + RequestOptions::TIMEOUT => $this->paymentTemplateConfig->getApiTimeout(), + RequestOptions::BODY => $requestBody, + ]; + + return $this->guzzleClient->request( + 'POST', + $this->getUrl(), + $requestOptions, + ); + } + + protected function getUrl(): string + { + return $this->paymentTemplateConfig->getRefundUrl(); + } +} diff --git a/src/Spryker/Client/PaymentTemplate/Api/Refund/RefundMapper.php b/src/Spryker/Client/PaymentTemplate/Api/Refund/RefundMapper.php new file mode 100644 index 0000000..224d3f7 --- /dev/null +++ b/src/Spryker/Client/PaymentTemplate/Api/Refund/RefundMapper.php @@ -0,0 +1,48 @@ +toArray()); + } + + public function mapResponse(ResponseInterface $httpResponse): PaymentTemplateRefundResponseTransfer + { + $responseBody = json_decode($httpResponse->getBody()->getContents(), true); + + return (new PaymentTemplateRefundResponseTransfer())->fromArray($responseBody); + } + + public function mapErrorResponse(RequestException $exception): PaymentTemplateRefundResponseTransfer + { + $errorResponseTransfer = (new PaymentTemplateApiErrorResponseTransfer()) + ->setMessage($exception->getMessage()) + ->setStatusCode($exception->getCode()); + + if ($exception->hasResponse()) { + $errorResponseTransfer + ->setStatusCode($exception->getResponse()->getStatusCode()) + ->setBody($exception->getResponse()->getBody()->getContents()); + } + + return (new PaymentTemplateRefundResponseTransfer()) + ->setIsSuccess(false) + ->setErrorResponse($errorResponseTransfer); + } +} diff --git a/src/Spryker/Client/PaymentTemplate/PaymentTemplateClient.php b/src/Spryker/Client/PaymentTemplate/PaymentTemplateClient.php index 863ee7a..bf3cac9 100644 --- a/src/Spryker/Client/PaymentTemplate/PaymentTemplateClient.php +++ b/src/Spryker/Client/PaymentTemplate/PaymentTemplateClient.php @@ -15,6 +15,8 @@ use Generated\Shared\Transfer\PaymentTemplateCaptureResponseTransfer; use Generated\Shared\Transfer\PaymentTemplatePaymentMethodsRequestTransfer; use Generated\Shared\Transfer\PaymentTemplatePaymentMethodsResponseTransfer; +use Generated\Shared\Transfer\PaymentTemplateRefundRequestTransfer; +use Generated\Shared\Transfer\PaymentTemplateRefundResponseTransfer; use Generated\Shared\Transfer\PaymentTemplateWebhookPayloadTransfer; use Generated\Shared\Transfer\PaymentTemplateWebhookProcessResponseTransfer; use Spryker\Client\Kernel\AbstractClient; @@ -33,9 +35,14 @@ public function authorize(PaymentTemplateAuthorizeRequestTransfer $paymentTempla return $this->getFactory()->createAuthorizeRequest()->request($paymentTemplateAuthorizeRequestTransfer); } - public function capture(PaymentTemplateCaptureRequestTransfer $paymentTemplateAuthorizeResponseTransfer): PaymentTemplateCaptureResponseTransfer + public function capture(PaymentTemplateCaptureRequestTransfer $paymentTemplateCaptureRequestTransfer): PaymentTemplateCaptureResponseTransfer { - return $this->getFactory()->createCaptureRequest()->request($paymentTemplateAuthorizeResponseTransfer); + return $this->getFactory()->createCaptureRequest()->request($paymentTemplateCaptureRequestTransfer); + } + + public function refund(PaymentTemplateRefundRequestTransfer $paymentTemplateRefundRequestTransfer): PaymentTemplateRefundResponseTransfer + { + return $this->getFactory()->createRefundRequest()->request($paymentTemplateRefundRequestTransfer); } public function cancel(PaymentTemplateCancelRequestTransfer $paymentTemplateCancelRequestTransfer): PaymentTemplateCancelResponseTransfer diff --git a/src/Spryker/Client/PaymentTemplate/PaymentTemplateClientInterface.php b/src/Spryker/Client/PaymentTemplate/PaymentTemplateClientInterface.php index 9daddfa..d08463f 100644 --- a/src/Spryker/Client/PaymentTemplate/PaymentTemplateClientInterface.php +++ b/src/Spryker/Client/PaymentTemplate/PaymentTemplateClientInterface.php @@ -15,6 +15,8 @@ use Generated\Shared\Transfer\PaymentTemplateCaptureResponseTransfer; use Generated\Shared\Transfer\PaymentTemplatePaymentMethodsRequestTransfer; use Generated\Shared\Transfer\PaymentTemplatePaymentMethodsResponseTransfer; +use Generated\Shared\Transfer\PaymentTemplateRefundRequestTransfer; +use Generated\Shared\Transfer\PaymentTemplateRefundResponseTransfer; use Generated\Shared\Transfer\PaymentTemplateWebhookPayloadTransfer; use Generated\Shared\Transfer\PaymentTemplateWebhookProcessResponseTransfer; @@ -44,11 +46,26 @@ public function authorize(PaymentTemplateAuthorizeRequestTransfer $paymentTempla * * @api * - * @param \Generated\Shared\Transfer\PaymentTemplateCaptureRequestTransfer $paymentTemplateAuthorizeResponseTransfer + * @param \Generated\Shared\Transfer\PaymentTemplateCaptureRequestTransfer $paymentTemplateCaptureRequestTransfer * * @return \Generated\Shared\Transfer\PaymentTemplateCaptureResponseTransfer */ - public function capture(PaymentTemplateCaptureRequestTransfer $paymentTemplateAuthorizeResponseTransfer): PaymentTemplateCaptureResponseTransfer; + public function capture(PaymentTemplateCaptureRequestTransfer $paymentTemplateCaptureRequestTransfer): PaymentTemplateCaptureResponseTransfer; + + /** + * Specification: + * - Sends capture request to payment provider to capture authorized amount. + * - Maps request data to provider-specific format. + * - Returns capture response. + * - Logs the request, response and error. + * + * @api + * + * @param \Generated\Shared\Transfer\PaymentTemplateRefundRequestTransfer $paymentTemplateRefundRequestTransfer + * + * @return \Generated\Shared\Transfer\PaymentTemplateRefundResponseTransfer + */ + public function refund(PaymentTemplateRefundRequestTransfer $paymentTemplateRefundRequestTransfer): PaymentTemplateRefundResponseTransfer; /** * Specification: diff --git a/src/Spryker/Client/PaymentTemplate/PaymentTemplateConfig.php b/src/Spryker/Client/PaymentTemplate/PaymentTemplateConfig.php index f253ad6..8ea5078 100644 --- a/src/Spryker/Client/PaymentTemplate/PaymentTemplateConfig.php +++ b/src/Spryker/Client/PaymentTemplate/PaymentTemplateConfig.php @@ -74,6 +74,11 @@ public function getCaptureUrl(): string return $this->getApiBaseUrl() . $this->getSharedConfig()->getCapturePath(); } + public function getRefundUrl(): string + { + return $this->getApiBaseUrl() . $this->getSharedConfig()->getRefundPath(); + } + /** * @api * diff --git a/src/Spryker/Client/PaymentTemplate/PaymentTemplateFactory.php b/src/Spryker/Client/PaymentTemplate/PaymentTemplateFactory.php index 1985999..b80274b 100644 --- a/src/Spryker/Client/PaymentTemplate/PaymentTemplateFactory.php +++ b/src/Spryker/Client/PaymentTemplate/PaymentTemplateFactory.php @@ -9,6 +9,8 @@ use GuzzleHttp\Client; use Spryker\Client\Kernel\AbstractFactory; +use Spryker\Client\PaymentTemplate\Api\Refund\RefundApiRequest; +use Spryker\Client\PaymentTemplate\Api\Refund\RefundMapper; use Spryker\Client\ZedRequest\ZedRequestClientInterface; use Spryker\Client\PaymentTemplate\Api\ApiLogger; use Spryker\Client\PaymentTemplate\Api\Authorization\AuthorizationApiRequest; @@ -72,6 +74,21 @@ public function createCaptureMapper(): CaptureMapper return new CaptureMapper(); } + public function createRefundRequest(): RefundApiRequest + { + return new RefundApiRequest( + $this->getConfig(), + $this->createApiLogger(), + $this->createGuzzleClient(), + $this->createRefundMapper(), + ); + } + + public function createRefundMapper(): RefundMapper + { + return new RefundMapper(); + } + public function createCancelRequest(): CancelApiRequest { return new CancelApiRequest( diff --git a/src/Spryker/Shared/PaymentTemplate/PaymentTemplateConfig.php b/src/Spryker/Shared/PaymentTemplate/PaymentTemplateConfig.php index ebf459f..3545834 100644 --- a/src/Spryker/Shared/PaymentTemplate/PaymentTemplateConfig.php +++ b/src/Spryker/Shared/PaymentTemplate/PaymentTemplateConfig.php @@ -77,6 +77,11 @@ public function getCapturePath(): string return $this->get(PaymentTemplateConstants::API_CAPTURE_PATH, '/api/v1/capture'); } + public function getRefundPath():string + { + return $this->get(PaymentTemplateConstants::API_REFUND_PATH, '/api/v1/refund'); + } + public function getCancelPath(): string { return $this->get(PaymentTemplateConstants::API_CANCEL_PATH, '/api/v1/cancel'); diff --git a/src/Spryker/Shared/PaymentTemplate/PaymentTemplateConstants.php b/src/Spryker/Shared/PaymentTemplate/PaymentTemplateConstants.php index f46e2b7..de3181f 100644 --- a/src/Spryker/Shared/PaymentTemplate/PaymentTemplateConstants.php +++ b/src/Spryker/Shared/PaymentTemplate/PaymentTemplateConstants.php @@ -24,6 +24,8 @@ interface PaymentTemplateConstants public const string API_CAPTURE_PATH = 'PAYMENT_TEMPLATE:API_CAPTURE_PATH'; + public const string API_REFUND_PATH = 'PAYMENT_TEMPLATE:API_REFUND_PATH'; + public const string API_CANCEL_PATH = 'PAYMENT_TEMPLATE:API_CANCEL_PATH'; public const string API_PAYMENT_METHODS_PATH = 'PAYMENT_TEMPLATE:API_PAYMENT_METHODS_PATH'; diff --git a/src/Spryker/Shared/PaymentTemplate/Transfer/payment_template.transfer.xml b/src/Spryker/Shared/PaymentTemplate/Transfer/payment_template.transfer.xml index a9882d9..347c923 100644 --- a/src/Spryker/Shared/PaymentTemplate/Transfer/payment_template.transfer.xml +++ b/src/Spryker/Shared/PaymentTemplate/Transfer/payment_template.transfer.xml @@ -83,6 +83,18 @@ + + + + + + + + + + diff --git a/src/Spryker/Zed/PaymentTemplate/Business/Oms/Command/OmsCommandHandler.php b/src/Spryker/Zed/PaymentTemplate/Business/Oms/Command/OmsCommandHandler.php index ae38052..5ff2bbc 100644 --- a/src/Spryker/Zed/PaymentTemplate/Business/Oms/Command/OmsCommandHandler.php +++ b/src/Spryker/Zed/PaymentTemplate/Business/Oms/Command/OmsCommandHandler.php @@ -15,6 +15,8 @@ use Generated\Shared\Transfer\PaymentTemplateCancelResponseTransfer; use Generated\Shared\Transfer\PaymentTemplateCaptureRequestTransfer; use Generated\Shared\Transfer\PaymentTemplateCaptureResponseTransfer; +use Generated\Shared\Transfer\PaymentTemplateRefundRequestTransfer; +use Generated\Shared\Transfer\PaymentTemplateRefundResponseTransfer; use Generated\Shared\Transfer\PaymentTemplateTransfer; use Orm\Zed\Sales\Persistence\SpySalesOrder; use Spryker\Client\PaymentTemplate\PaymentTemplateClientInterface; @@ -97,6 +99,39 @@ public function executeCaptureCommand(SpySalesOrder $orderEntity, array $orderIt } } + /** + * @param \Orm\Zed\Sales\Persistence\SpySalesOrder $orderEntity + * @param array<\Orm\Zed\Sales\Persistence\SpySalesOrderItem> $orderItems + * + * @return void + */ + public function executeRefundCommand(SpySalesOrder $orderEntity, array $orderItems): void + { + $paymentTemplateTransfer = $this->paymentReader->findPaymentByIdSalesOrder( + $orderEntity->getIdSalesOrder(), + ); + + if ($paymentTemplateTransfer === null) { + return; + } + + $paymentTemplateRefundRequestTransfer = $this->buildRefundRequest($paymentTemplateTransfer); + + try { + $paymentTemplateRefundResponseTransfer = $this->client->refund($paymentTemplateRefundRequestTransfer); + + if (!$paymentTemplateRefundResponseTransfer->getIsSuccess()) { + $this->handleRefundError($paymentTemplateTransfer, $paymentTemplateRefundRequestTransfer, $paymentTemplateRefundResponseTransfer->getErrorResponse()); + + return; + } + + $this->updatePaymentAfterRefund($paymentTemplateTransfer, $paymentTemplateRefundRequestTransfer, $paymentTemplateRefundResponseTransfer); + } catch (Exception $exception) { + $this->handleRefundError($paymentTemplateTransfer, $paymentTemplateRefundRequestTransfer, null); + } + } + /** * @param \Orm\Zed\Sales\Persistence\SpySalesOrder $orderEntity * @param array<\Orm\Zed\Sales\Persistence\SpySalesOrderItem> $orderItems @@ -235,6 +270,58 @@ protected function updatePaymentAfterCapture( ); } + protected function buildRefundRequest(PaymentTemplateTransfer $paymentTemplateTransfer): PaymentTemplateRefundRequestTransfer + { + // TODO: Compose the request transfer from data in the PaymentTemplateTransfer. + // If your payment service provider requires additional data not available in PaymentTemplateTransfer, + // add additional parameters to this method signature and pass them from executeRefundCommand. + // e.g. + // return (new PaymentTemplateRefundRequestTransfer()) + // ->setProviderReference($paymentTemplateTransfer->getProviderReference()) + // ->setAmount($paymentTemplateTransfer->getAmount()) + // ->setCurrency($paymentTemplateTransfer->getCurrency()); + return (new PaymentTemplateRefundRequestTransfer()); + } + + protected function handleRefundError( + PaymentTemplateTransfer $paymentTemplateTransfer, + PaymentTemplateRefundRequestTransfer $paymentTemplateRefundRequestTransfer, + ?PaymentTemplateApiErrorResponseTransfer $paymentTemplateApiErrorResponseTransfer + ): void { + // TODO: Define status constants in PaymentTemplateConfig based on payment service provider specific statuses. + // Extract error details from $paymentTemplateApiErrorResponseTransfer if needed and pass appropriate status to updatePaymentStatus. + // You may need to handle different error types with different status constants. + // e.g. + // $this->entityManager->updatePaymentStatus( + // $paymentTemplateTransfer->getIdPaymentTemplateOrFail(), + // PaymentTemplateConfig::PAYMENT_STATUS_REFUND_FAILED, + // $paymentTemplateApiErrorResponseTransfer?->getProviderReference(), + // ); + $this->entityManager->updatePaymentStatus( + $paymentTemplateTransfer->getIdPaymentTemplateOrFail(), + PaymentTemplateConfig::PAYMENT_STATUS_REFUND_FAILED, + ); + } + + protected function updatePaymentAfterRefund( + PaymentTemplateTransfer $paymentTemplateTransfer, + PaymentTemplateRefundRequestTransfer $paymentTemplateRefundRequestTransfer, + PaymentTemplateRefundResponseTransfer $paymentTemplateRefundResponseTransfer, + ): void { + // TODO: Define status constants in PaymentTemplateConfig based on payment service provider specific statuses. + // Extract the appropriate status from $paymentTemplateRefundResponseTransfer and pass it to updatePaymentStatus. + // e.g. + // $this->entityManager->updatePaymentStatus( + // $paymentTemplateTransfer->getIdPaymentTemplateOrFail(), + // PaymentTemplateConfig::PAYMENT_STATUS_REFUNDED, + // $paymentTemplateRefundResponseTransfer->getProviderReference(), + // ); + $this->entityManager->updatePaymentStatus( + $paymentTemplateTransfer->getIdPaymentTemplateOrFail(), + '', + ); + } + protected function buildCancelRequest(PaymentTemplateTransfer $paymentTemplateTransfer): PaymentTemplateCancelRequestTransfer { // TODO: Compose the request transfer from data in the PaymentTemplateTransfer. diff --git a/src/Spryker/Zed/PaymentTemplate/Business/Oms/Condition/OmsConditionChecker.php b/src/Spryker/Zed/PaymentTemplate/Business/Oms/Condition/OmsConditionChecker.php index 56dfa6a..0b59921 100644 --- a/src/Spryker/Zed/PaymentTemplate/Business/Oms/Condition/OmsConditionChecker.php +++ b/src/Spryker/Zed/PaymentTemplate/Business/Oms/Condition/OmsConditionChecker.php @@ -66,4 +66,20 @@ public function isPaymentCaptured(SpySalesOrderItem $orderItemEntity): bool //return $paymentTemplateTransfer->getStatus() === $this->config::PAYMENT_STATUS_CAPTURED; return true; // Placeholder - replace with actual status check } + + public function isPaymentRefunded(SpySalesOrderItem $orderItemEntity): bool + { + $paymentTemplateTransfer = $this->paymentReader->findPaymentByOrderItem($orderItemEntity); + + if ($paymentTemplateTransfer === null) { + return false; + } + + // TODO: Replace placeholder return value with actual status check. + // Check if the payment status matches the refunded state. + // The status constant must match what you set in OmsCommandHandler::updatePaymentAfterRefund(). + // e.g. + //return $paymentTemplateTransfer->getStatus() === $this->config::PAYMENT_STATUS_REFUNDED; + return true; // Placeholder - replace with actual status check + } } diff --git a/src/Spryker/Zed/PaymentTemplate/Business/Oms/Condition/OmsConditionCheckerInterface.php b/src/Spryker/Zed/PaymentTemplate/Business/Oms/Condition/OmsConditionCheckerInterface.php index 8411d42..61baeaa 100644 --- a/src/Spryker/Zed/PaymentTemplate/Business/Oms/Condition/OmsConditionCheckerInterface.php +++ b/src/Spryker/Zed/PaymentTemplate/Business/Oms/Condition/OmsConditionCheckerInterface.php @@ -16,4 +16,6 @@ public function isPaymentAuthorized(SpySalesOrderItem $orderItemEntity): bool; public function isPaymentAuthorizationFailed(SpySalesOrderItem $orderItemEntity): bool; public function isPaymentCaptured(SpySalesOrderItem $orderItemEntity): bool; + + public function isPaymentRefunded(SpySalesOrderItem $orderItemEntity): bool; } diff --git a/src/Spryker/Zed/PaymentTemplate/Business/PaymentTemplateFacade.php b/src/Spryker/Zed/PaymentTemplate/Business/PaymentTemplateFacade.php index cea5aca..d87c45d 100644 --- a/src/Spryker/Zed/PaymentTemplate/Business/PaymentTemplateFacade.php +++ b/src/Spryker/Zed/PaymentTemplate/Business/PaymentTemplateFacade.php @@ -111,6 +111,23 @@ public function executeCaptureCommand(SpySalesOrder $orderEntity, array $orderIt ->executeCaptureCommand($orderEntity, $orderItems); } + /** + * {@inheritDoc} + * + * @api + * + * @param \Orm\Zed\Sales\Persistence\SpySalesOrder $orderEntity + * @param array<\Orm\Zed\Sales\Persistence\SpySalesOrderItem> $orderItems + * + * @return void + */ + public function executeRefundCommand(SpySalesOrder $orderEntity, array $orderItems): void + { + $this->getFactory() + ->createOmsCommandHandler() + ->executeRefundCommand($orderEntity, $orderItems); + } + /** * {@inheritDoc} * @@ -176,6 +193,22 @@ public function isPaymentCaptured(SpySalesOrderItem $orderItemEntity): bool ->isPaymentCaptured($orderItemEntity); } + /** + * {@inheritDoc} + * + * @api + * + * @param \Orm\Zed\Sales\Persistence\SpySalesOrderItem $orderItemEntity + * + * @return bool + */ + public function isPaymentRefunded(SpySalesOrderItem $orderItemEntity): bool + { + return $this->getFactory() + ->createOmsConditionChecker() + ->isPaymentRefunded($orderItemEntity); + } + /** * {@inheritDoc} * diff --git a/src/Spryker/Zed/PaymentTemplate/Business/PaymentTemplateFacadeInterface.php b/src/Spryker/Zed/PaymentTemplate/Business/PaymentTemplateFacadeInterface.php index 2793e32..bdecc66 100644 --- a/src/Spryker/Zed/PaymentTemplate/Business/PaymentTemplateFacadeInterface.php +++ b/src/Spryker/Zed/PaymentTemplate/Business/PaymentTemplateFacadeInterface.php @@ -93,6 +93,21 @@ public function executeAuthorizeCommand(SpySalesOrder $orderEntity, array $order */ public function executeCaptureCommand(SpySalesOrder $orderEntity, array $orderItems): void; + /** + * Specification: + * - Executes payment refund command. + * - Called by OMS RefundPlugin refund the captured funds. + * - Communicates with payment provider via RefundAdapter. + * + * @api + * + * @param \Orm\Zed\Sales\Persistence\SpySalesOrder $orderEntity + * @param array<\Orm\Zed\Sales\Persistence\SpySalesOrderItem> $orderItems + * + * @return void + */ + public function executeRefundCommand(SpySalesOrder $orderEntity, array $orderItems): void; + /** * Specification: * - Executes payment cancellation command. @@ -147,6 +162,19 @@ public function isPaymentAuthorizationFailed(SpySalesOrderItem $orderItemEntity) */ public function isPaymentCaptured(SpySalesOrderItem $orderItemEntity): bool; + /** + * Specification: + * - Checks if payment is refunded. + * - Called by OMS IsRefundedPlugin condition. + * + * @api + * + * @param \Orm\Zed\Sales\Persistence\SpySalesOrderItem $orderItemEntity + * + * @return bool + */ + public function isPaymentRefunded(SpySalesOrderItem $orderItemEntity): bool; + /** * Specification: * - Processes incoming webhook notification from payment service provider. diff --git a/src/Spryker/Zed/PaymentTemplate/Communication/Plugin/Oms/Command/RefundPlugin.php b/src/Spryker/Zed/PaymentTemplate/Communication/Plugin/Oms/Command/RefundPlugin.php new file mode 100644 index 0000000..54526d0 --- /dev/null +++ b/src/Spryker/Zed/PaymentTemplate/Communication/Plugin/Oms/Command/RefundPlugin.php @@ -0,0 +1,43 @@ + $orderItems + * @param \Orm\Zed\Sales\Persistence\SpySalesOrder $orderEntity + * @param \Spryker\Zed\Oms\Business\Util\ReadOnlyArrayObject $data + * + * @return array<\Orm\Zed\Sales\Persistence\SpySalesOrderItem> + */ + public function run(array $orderItems, SpySalesOrder $orderEntity, ReadOnlyArrayObject $data): array + { + $this->getFacade()->executeRefundCommand($orderEntity, $orderItems); + + return $orderItems; + } +} diff --git a/src/Spryker/Zed/PaymentTemplate/Communication/Plugin/Oms/Condition/IsRefundedPlugin.php b/src/Spryker/Zed/PaymentTemplate/Communication/Plugin/Oms/Condition/IsRefundedPlugin.php new file mode 100644 index 0000000..9cbec9c --- /dev/null +++ b/src/Spryker/Zed/PaymentTemplate/Communication/Plugin/Oms/Condition/IsRefundedPlugin.php @@ -0,0 +1,37 @@ +getFacade()->isPaymentRefunded($orderItemEntity); + } +} diff --git a/src/Spryker/Zed/PaymentTemplate/PaymentTemplateConfig.php b/src/Spryker/Zed/PaymentTemplate/PaymentTemplateConfig.php index 6182ba5..63a259c 100644 --- a/src/Spryker/Zed/PaymentTemplate/PaymentTemplateConfig.php +++ b/src/Spryker/Zed/PaymentTemplate/PaymentTemplateConfig.php @@ -13,27 +13,13 @@ class PaymentTemplateConfig extends AbstractBundleConfig { public const string PAYMENT_STATUS_NEW = 'new'; - public const string PAYMENT_STATUS_AUTHORIZATION_PENDING = 'authorization_pending'; - public const string PAYMENT_STATUS_AUTHORIZED = 'authorized'; public const string PAYMENT_STATUS_AUTHORIZATION_FAILED = 'authorization_failed'; - public const string PAYMENT_STATUS_CAPTURE_PENDING = 'capture_pending'; - - public const string PAYMENT_STATUS_CAPTURED = 'captured'; - public const string PAYMENT_STATUS_CAPTURE_FAILED = 'capture_failed'; - public const string PAYMENT_STATUS_REFUND_PENDING = 'refund_pending'; - - public const string PAYMENT_STATUS_CANCELED = 'canceled'; + public const string PAYMENT_STATUS_REFUND_FAILED = 'refund_failed'; public const string PAYMENT_STATUS_CANCEL_FAILED = 'cancel_failed'; - - public const string TRANSACTION_TYPE_AUTHORIZE = 'authorize'; - - public const string TRANSACTION_TYPE_CAPTURE = 'capture'; - - public const string TRANSACTION_TYPE_CANCEL = 'cancel'; } From ed459043e9a55b7bd3e1cc6eae962fc54b0a170d Mon Sep 17 00:00:00 2001 From: Eugenia Poidenko Date: Tue, 17 Mar 2026 18:08:44 +0200 Subject: [PATCH 2/3] SOL-478 Cleanups. --- .../Client/PaymentTemplate/Api/Refund/RefundApiRequest.php | 2 -- src/Spryker/Client/PaymentTemplate/Api/Refund/RefundMapper.php | 2 -- src/Spryker/Shared/PaymentTemplate/PaymentTemplateConfig.php | 2 +- .../Communication/Plugin/Oms/Condition/IsRefundedPlugin.php | 2 +- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Spryker/Client/PaymentTemplate/Api/Refund/RefundApiRequest.php b/src/Spryker/Client/PaymentTemplate/Api/Refund/RefundApiRequest.php index 32c29a3..24c60e8 100644 --- a/src/Spryker/Client/PaymentTemplate/Api/Refund/RefundApiRequest.php +++ b/src/Spryker/Client/PaymentTemplate/Api/Refund/RefundApiRequest.php @@ -7,8 +7,6 @@ namespace Spryker\Client\PaymentTemplate\Api\Refund; -use Generated\Shared\Transfer\PaymentTemplateCaptureRequestTransfer; -use Generated\Shared\Transfer\PaymentTemplateCaptureResponseTransfer; use Generated\Shared\Transfer\PaymentTemplateRefundRequestTransfer; use Generated\Shared\Transfer\PaymentTemplateRefundResponseTransfer; use GuzzleHttp\Client; diff --git a/src/Spryker/Client/PaymentTemplate/Api/Refund/RefundMapper.php b/src/Spryker/Client/PaymentTemplate/Api/Refund/RefundMapper.php index 224d3f7..5ec14eb 100644 --- a/src/Spryker/Client/PaymentTemplate/Api/Refund/RefundMapper.php +++ b/src/Spryker/Client/PaymentTemplate/Api/Refund/RefundMapper.php @@ -8,8 +8,6 @@ namespace Spryker\Client\PaymentTemplate\Api\Refund; use Generated\Shared\Transfer\PaymentTemplateApiErrorResponseTransfer; -use Generated\Shared\Transfer\PaymentTemplateCaptureRequestTransfer; -use Generated\Shared\Transfer\PaymentTemplateCaptureResponseTransfer; use Generated\Shared\Transfer\PaymentTemplateRefundRequestTransfer; use Generated\Shared\Transfer\PaymentTemplateRefundResponseTransfer; use GuzzleHttp\Exception\RequestException; diff --git a/src/Spryker/Shared/PaymentTemplate/PaymentTemplateConfig.php b/src/Spryker/Shared/PaymentTemplate/PaymentTemplateConfig.php index 3545834..38c4aa2 100644 --- a/src/Spryker/Shared/PaymentTemplate/PaymentTemplateConfig.php +++ b/src/Spryker/Shared/PaymentTemplate/PaymentTemplateConfig.php @@ -77,7 +77,7 @@ public function getCapturePath(): string return $this->get(PaymentTemplateConstants::API_CAPTURE_PATH, '/api/v1/capture'); } - public function getRefundPath():string + public function getRefundPath(): string { return $this->get(PaymentTemplateConstants::API_REFUND_PATH, '/api/v1/refund'); } diff --git a/src/Spryker/Zed/PaymentTemplate/Communication/Plugin/Oms/Condition/IsRefundedPlugin.php b/src/Spryker/Zed/PaymentTemplate/Communication/Plugin/Oms/Condition/IsRefundedPlugin.php index 9cbec9c..9a2fcd8 100644 --- a/src/Spryker/Zed/PaymentTemplate/Communication/Plugin/Oms/Condition/IsRefundedPlugin.php +++ b/src/Spryker/Zed/PaymentTemplate/Communication/Plugin/Oms/Condition/IsRefundedPlugin.php @@ -22,7 +22,7 @@ class IsRefundedPlugin extends AbstractPlugin implements ConditionInterface * {@inheritDoc} * - Checks if payment capture is confirmed. * - Reads status from spy_payment_template table. - * - Called by OMS to determine if transition to "captured" state is actual. + * - Called by OMS to determine if transition to "refunded" state is actual. * * @api * From e29c5456841c4c752888f03484c86ce27233b3dd Mon Sep 17 00:00:00 2001 From: Eugenia Poidenko Date: Tue, 17 Mar 2026 18:43:01 +0200 Subject: [PATCH 3/3] SOL-478 add refundable period. --- config/Zed/oms/PaymentTemplateCreditCard01.xml | 8 ++++++++ config/Zed/oms/PaymentTemplateInvoice01.xml | 8 ++++++++ data/import/glossary.csv | 1 + 3 files changed, 17 insertions(+) diff --git a/config/Zed/oms/PaymentTemplateCreditCard01.xml b/config/Zed/oms/PaymentTemplateCreditCard01.xml index a9f3826..073b9fe 100644 --- a/config/Zed/oms/PaymentTemplateCreditCard01.xml +++ b/config/Zed/oms/PaymentTemplateCreditCard01.xml @@ -20,6 +20,7 @@ + @@ -77,6 +78,12 @@ refunded + + captured + closed for refund + close for refund + + authorized @@ -94,6 +101,7 @@ + diff --git a/config/Zed/oms/PaymentTemplateInvoice01.xml b/config/Zed/oms/PaymentTemplateInvoice01.xml index d59fd4f..af16e2b 100644 --- a/config/Zed/oms/PaymentTemplateInvoice01.xml +++ b/config/Zed/oms/PaymentTemplateInvoice01.xml @@ -20,6 +20,7 @@ + @@ -77,6 +78,12 @@ refunded + + captured + closed for refund + close for refund + + authorized @@ -94,6 +101,7 @@ + diff --git a/data/import/glossary.csv b/data/import/glossary.csv index 956aee1..c070a36 100644 --- a/data/import/glossary.csv +++ b/data/import/glossary.csv @@ -11,3 +11,4 @@ oms.state.capture-pending,Capture Pending,en_US oms.state.captured,Captured,en_US oms.state.refund-pending,Refund Pending,en_US oms.state.refunded,Refunded,en_US +oms.state.closed-for-refund,Refunded,en_US