diff --git a/composer.json b/composer.json index a364ab9..2b457b3 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,8 @@ "license": "proprietary", "require": { "php": ">=8.2", - "spryker/app-kernel": "^2.0.0", + "spryker/application": "^3.35.0", + "spryker/app-kernel": "^2.1.0", "spryker/app-webhook": "^1.1.0", "spryker/event-dispatcher": "^1.6.0", "spryker/glue-application-extension": "^1.0.0", @@ -14,6 +15,7 @@ "spryker/kernel": "^3.30.0", "spryker/log": "^3.0.0", "spryker/message-broker": "^1.11.0", + "spryker/message-broker-aws-extension": "^1.0.0", "spryker/router": "^1.19.0", "spryker/symfony": "^3.0.0", "spryker/transfer": "^3.33.0", @@ -65,7 +67,7 @@ "minimum-stability": "dev", "prefer-stable": true, "scripts": { - "setup": "tests/bin/console app-payment:setup && tests/bin/console transfer:generate && tests/bin/console transfer:databuilder:generate && tests/bin/console dev:ide-auto-completion:zed:generate && tests/bin/console dev:ide-auto-completion:glue:generate && tests/bin/console dev:ide-auto-completion:glue-backend:generate && tests/bin/console propel:install && vendor/bin/codecept build", + "setup": "tests/bin/console app-payment:setup && tests/bin/console transfer:generate && tests/bin/console transfer:databuilder:generate && tests/bin/console translator:generate && tests/bin/console dev:ide-auto-completion:zed:generate && tests/bin/console dev:ide-auto-completion:glue:generate && tests/bin/console dev:ide-auto-completion:glue-backend:generate && tests/bin/console propel:install && vendor/bin/codecept build", "cs-check": "phpcs -p src/ tests/", "cs-fix": "phpcbf -p src/ tests/", "stan": "phpstan analyze src/Spryker/ --memory-limit=1G", diff --git a/data/translation/Zed/de_DE.csv b/data/translation/Zed/de_DE.csv new file mode 100644 index 0000000..499553d --- /dev/null +++ b/data/translation/Zed/de_DE.csv @@ -0,0 +1,2 @@ +"The payment App cannot be disconnected when there are open orders. Open orders won’t be proceed automatically if you delete the App. Close the open orders to continue.","Die Zahlungs-App kann nicht getrennt werden, wenn offene Bestellungen vorliegen. Offene Bestellungen werden nicht automatisch abgeschlossen, wenn Sie die App löschen. Schließen Sie die offenen Bestellungen, um fortzufahren." +"Please close any open orders and try again.","Bitte schließen Sie alle offenen Bestellungen und versuchen Sie es erneut." diff --git a/data/translation/Zed/en_US.csv b/data/translation/Zed/en_US.csv new file mode 100644 index 0000000..1c305a3 --- /dev/null +++ b/data/translation/Zed/en_US.csv @@ -0,0 +1,2 @@ +"The payment App cannot be disconnected when there are open orders. Open orders won’t be proceed automatically if you delete the App. Close the open orders to continue.","The payment App cannot be disconnected when there are open orders. Open orders won’t be proceed automatically if you delete the App. Close the open orders to continue." +"Please close any open orders and try again.","Please close any open orders and try again." diff --git a/rector.php b/rector.php index 1411e63..142bc5e 100644 --- a/rector.php +++ b/rector.php @@ -45,6 +45,7 @@ ], ClassPropertyAssignToConstructorPromotionRector::class => [ __DIR__ . '/src/Spryker/Glue/AppPaymentBackendApi/Dependency/Facade/AppPaymentBackendApiToAppPaymentFacadeBridge.php', + __DIR__ . '/src/Spryker/Glue/AppPaymentBackendApi/Dependency/Facade/AppPaymentBackendApiToTranslatorFacadeBridge.php', __DIR__ . '/src/Spryker/Zed/AppPayment/Dependency/Facade/AppPaymentToAppKernelFacadeBridge.php', __DIR__ . '/src/Spryker/Zed/AppPayment/Dependency/Facade/AppPaymentToMessageBrokerFacadeBridge.php', __DIR__ . '/src/Spryker/Zed/AppPayment/Dependency/Facade/AppPaymentToAppWebhookFacadeBridge.php', @@ -52,6 +53,7 @@ ], AddParamTypeFromPropertyTypeRector::class => [ __DIR__ . '/src/Spryker/Glue/AppPaymentBackendApi/Dependency/Facade/AppPaymentBackendApiToAppPaymentFacadeBridge.php', + __DIR__ . '/src/Spryker/Glue/AppPaymentBackendApi/Dependency/Facade/AppPaymentBackendApiToTranslatorFacadeBridge.php', __DIR__ . '/src/Spryker/Zed/AppPayment/Dependency/Facade/AppPaymentToAppKernelFacadeBridge.php', __DIR__ . '/src/Spryker/Zed/AppPayment/Dependency/Facade/AppPaymentToMessageBrokerFacadeBridge.php', __DIR__ . '/src/Spryker/Zed/AppPayment/Dependency/Facade/AppPaymentToAppWebhookFacadeBridge.php', diff --git a/src/Spryker/Glue/AppPaymentBackendApi/AppPaymentBackendApiConfig.php b/src/Spryker/Glue/AppPaymentBackendApi/AppPaymentBackendApiConfig.php index c06cebe..8473d38 100644 --- a/src/Spryker/Glue/AppPaymentBackendApi/AppPaymentBackendApiConfig.php +++ b/src/Spryker/Glue/AppPaymentBackendApi/AppPaymentBackendApiConfig.php @@ -11,4 +11,13 @@ class AppPaymentBackendApiConfig extends AbstractBundleConfig { + /** + * @var string + */ + public const ERROR_CODE_PAYMENT_DISCONNECTION_CANNOT_BE_PROCEEDED = '21000'; + + /** + * @var string + */ + public const ERROR_CODE_PAYMENT_DISCONNECTION_FORBIDDEN = '21001'; } diff --git a/src/Spryker/Glue/AppPaymentBackendApi/AppPaymentBackendApiDependencyProvider.php b/src/Spryker/Glue/AppPaymentBackendApi/AppPaymentBackendApiDependencyProvider.php index 71c101d..745b89e 100644 --- a/src/Spryker/Glue/AppPaymentBackendApi/AppPaymentBackendApiDependencyProvider.php +++ b/src/Spryker/Glue/AppPaymentBackendApi/AppPaymentBackendApiDependencyProvider.php @@ -9,6 +9,8 @@ use Spryker\Glue\AppPaymentBackendApi\Dependency\Facade\AppPaymentBackendApiToAppPaymentFacadeBridge; use Spryker\Glue\AppPaymentBackendApi\Dependency\Facade\AppPaymentBackendApiToAppPaymentFacadeInterface; +use Spryker\Glue\AppPaymentBackendApi\Dependency\Facade\AppPaymentBackendApiToTranslatorFacadeBridge; +use Spryker\Glue\AppPaymentBackendApi\Dependency\Facade\AppPaymentBackendApiToTranslatorFacadeInterface; use Spryker\Glue\Kernel\Backend\AbstractBundleDependencyProvider; use Spryker\Glue\Kernel\Backend\Container; @@ -22,10 +24,16 @@ class AppPaymentBackendApiDependencyProvider extends AbstractBundleDependencyPro */ public const FACADE_APP_PAYMENT = 'PAYMENT_BACKEND_API:FACADE_PAYMENT'; + /** + * @var string + */ + public const FACADE_TRANSLATOR = 'FACADE_TRANSLATOR'; + public function provideBackendDependencies(Container $container): Container { $container = parent::provideBackendDependencies($container); $container = $this->addAppPaymentFacade($container); + $container = $this->addTranslatorFacade($container); return $container; } @@ -41,4 +49,13 @@ protected function addAppPaymentFacade(Container $container): Container return $container; } + + protected function addTranslatorFacade(Container $container): Container + { + $container->set(static::FACADE_TRANSLATOR, static function (Container $container): AppPaymentBackendApiToTranslatorFacadeInterface { + return new AppPaymentBackendApiToTranslatorFacadeBridge($container->getLocator()->translator()->facade()); + }); + + return $container; + } } diff --git a/src/Spryker/Glue/AppPaymentBackendApi/AppPaymentBackendApiFactory.php b/src/Spryker/Glue/AppPaymentBackendApi/AppPaymentBackendApiFactory.php index f90f036..aad6bdf 100644 --- a/src/Spryker/Glue/AppPaymentBackendApi/AppPaymentBackendApiFactory.php +++ b/src/Spryker/Glue/AppPaymentBackendApi/AppPaymentBackendApiFactory.php @@ -8,6 +8,7 @@ namespace Spryker\Glue\AppPaymentBackendApi; use Spryker\Glue\AppPaymentBackendApi\Dependency\Facade\AppPaymentBackendApiToAppPaymentFacadeInterface; +use Spryker\Glue\AppPaymentBackendApi\Dependency\Facade\AppPaymentBackendApiToTranslatorFacadeInterface; use Spryker\Glue\AppPaymentBackendApi\Mapper\Payment\GlueRequestPaymentMapper; use Spryker\Glue\AppPaymentBackendApi\Mapper\Payment\GlueRequestPaymentMapperInterface; use Spryker\Glue\AppPaymentBackendApi\Mapper\Payment\GlueResponsePaymentMapper; @@ -34,4 +35,10 @@ public function getAppPaymentFacade(): AppPaymentBackendApiToAppPaymentFacadeInt /** @phpstan-var \Spryker\Glue\AppPaymentBackendApi\Dependency\Facade\AppPaymentBackendApiToAppPaymentFacadeInterface */ return $this->getProvidedDependency(AppPaymentBackendApiDependencyProvider::FACADE_APP_PAYMENT); } + + public function getTranslatorFacade(): AppPaymentBackendApiToTranslatorFacadeInterface + { + /** @phpstan-var \Spryker\Glue\AppPaymentBackendApi\Dependency\Facade\AppPaymentBackendApiToTranslatorFacadeInterface */ + return $this->getProvidedDependency(AppPaymentBackendApiDependencyProvider::FACADE_TRANSLATOR); + } } diff --git a/src/Spryker/Glue/AppPaymentBackendApi/Dependency/Facade/AppPaymentBackendApiToAppPaymentFacadeBridge.php b/src/Spryker/Glue/AppPaymentBackendApi/Dependency/Facade/AppPaymentBackendApiToAppPaymentFacadeBridge.php index 3c9da61..7f2316e 100644 --- a/src/Spryker/Glue/AppPaymentBackendApi/Dependency/Facade/AppPaymentBackendApiToAppPaymentFacadeBridge.php +++ b/src/Spryker/Glue/AppPaymentBackendApi/Dependency/Facade/AppPaymentBackendApiToAppPaymentFacadeBridge.php @@ -13,6 +13,8 @@ use Generated\Shared\Transfer\ConfirmPreOrderPaymentResponseTransfer; use Generated\Shared\Transfer\InitializePaymentRequestTransfer; use Generated\Shared\Transfer\InitializePaymentResponseTransfer; +use Generated\Shared\Transfer\PaymentCollectionTransfer; +use Generated\Shared\Transfer\PaymentCriteriaTransfer; use Generated\Shared\Transfer\PaymentTransmissionsRequestTransfer; use Generated\Shared\Transfer\PaymentTransmissionsResponseTransfer; @@ -52,4 +54,9 @@ public function cancelPreOrderPayment( ): CancelPreOrderPaymentResponseTransfer { return $this->appPaymentFacade->cancelPreOrderPayment($cancelPreOrderPaymentRequestTransfer); } + + public function getPaymentCollection(PaymentCriteriaTransfer $paymentCriteriaTransfer): PaymentCollectionTransfer + { + return $this->appPaymentFacade->getPaymentCollection($paymentCriteriaTransfer); + } } diff --git a/src/Spryker/Glue/AppPaymentBackendApi/Dependency/Facade/AppPaymentBackendApiToAppPaymentFacadeInterface.php b/src/Spryker/Glue/AppPaymentBackendApi/Dependency/Facade/AppPaymentBackendApiToAppPaymentFacadeInterface.php index 90e9356..d0b7f0d 100644 --- a/src/Spryker/Glue/AppPaymentBackendApi/Dependency/Facade/AppPaymentBackendApiToAppPaymentFacadeInterface.php +++ b/src/Spryker/Glue/AppPaymentBackendApi/Dependency/Facade/AppPaymentBackendApiToAppPaymentFacadeInterface.php @@ -13,6 +13,8 @@ use Generated\Shared\Transfer\ConfirmPreOrderPaymentResponseTransfer; use Generated\Shared\Transfer\InitializePaymentRequestTransfer; use Generated\Shared\Transfer\InitializePaymentResponseTransfer; +use Generated\Shared\Transfer\PaymentCollectionTransfer; +use Generated\Shared\Transfer\PaymentCriteriaTransfer; use Generated\Shared\Transfer\PaymentTransmissionsRequestTransfer; use Generated\Shared\Transfer\PaymentTransmissionsResponseTransfer; @@ -29,4 +31,6 @@ public function confirmPreOrderPayment( public function cancelPreOrderPayment( CancelPreOrderPaymentRequestTransfer $cancelPreOrderPaymentRequestTransfer ): CancelPreOrderPaymentResponseTransfer; + + public function getPaymentCollection(PaymentCriteriaTransfer $paymentCriteriaTransfer): PaymentCollectionTransfer; } diff --git a/src/Spryker/Glue/AppPaymentBackendApi/Dependency/Facade/AppPaymentBackendApiToTranslatorFacadeBridge.php b/src/Spryker/Glue/AppPaymentBackendApi/Dependency/Facade/AppPaymentBackendApiToTranslatorFacadeBridge.php new file mode 100644 index 0000000..e90b01a --- /dev/null +++ b/src/Spryker/Glue/AppPaymentBackendApi/Dependency/Facade/AppPaymentBackendApiToTranslatorFacadeBridge.php @@ -0,0 +1,34 @@ +translatorFacade = $translatorFacade; + } + + /** + * @param array $parameters + * @param string|null $domain + * @param string|null $locale + */ + public function trans(string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string + { + return $this->translatorFacade->trans($id, $parameters, $domain, $locale); + } +} diff --git a/src/Spryker/Glue/AppPaymentBackendApi/Dependency/Facade/AppPaymentBackendApiToTranslatorFacadeInterface.php b/src/Spryker/Glue/AppPaymentBackendApi/Dependency/Facade/AppPaymentBackendApiToTranslatorFacadeInterface.php new file mode 100644 index 0000000..a78eeab --- /dev/null +++ b/src/Spryker/Glue/AppPaymentBackendApi/Dependency/Facade/AppPaymentBackendApiToTranslatorFacadeInterface.php @@ -0,0 +1,18 @@ + $parameters + * @param string|null $domain + * @param string|null $locale + */ + public function trans(string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string; +} diff --git a/src/Spryker/Glue/AppPaymentBackendApi/Plugin/GlueApplication/PaymentConfirmDisconnectionRequestValidatorPlugin.php b/src/Spryker/Glue/AppPaymentBackendApi/Plugin/GlueApplication/PaymentConfirmDisconnectionRequestValidatorPlugin.php new file mode 100644 index 0000000..2915178 --- /dev/null +++ b/src/Spryker/Glue/AppPaymentBackendApi/Plugin/GlueApplication/PaymentConfirmDisconnectionRequestValidatorPlugin.php @@ -0,0 +1,56 @@ +getFactory()->getAppPaymentFacade()->getPaymentCollection( + (new PaymentCriteriaTransfer())->setPaymentConditions( + (new PaymentConditionsTransfer()) + ->setTenantIdentifier($tenantIdentifier) + ->setExcludingStatuses([ + PaymentStatus::STATUS_CANCELED, + PaymentStatus::STATUS_CAPTURED, + ]), + ), + ); + + if ($paymentCollectionTransfer->getPayments()->count() === 0) { + return (new GlueRequestValidationTransfer()) + ->setIsValid(true); + } + + return $this->getFailedGlueRequestValidationTransfer( + AppPaymentBackendApiConfig::ERROR_CODE_PAYMENT_DISCONNECTION_CANNOT_BE_PROCEEDED, + $this->getFactory()->getTranslatorFacade()->trans('The payment App cannot be disconnected when there are open orders. Open orders won’t be proceed automatically if you delete the App. Close the open orders to continue.'), + ); + } + + protected function getCancellationErrorCode(): string + { + return AppPaymentBackendApiConfig::ERROR_CODE_PAYMENT_DISCONNECTION_FORBIDDEN; + } + + protected function getCancellationErrorMessage(): string + { + return $this->getFactory()->getTranslatorFacade()->trans('Please close any open orders and try again.'); + } +} diff --git a/src/Spryker/Shared/AppPayment/Transfer/app_payment.transfer.xml b/src/Spryker/Shared/AppPayment/Transfer/app_payment.transfer.xml index ff8c161..1fe1f2a 100644 --- a/src/Spryker/Shared/AppPayment/Transfer/app_payment.transfer.xml +++ b/src/Spryker/Shared/AppPayment/Transfer/app_payment.transfer.xml @@ -410,6 +410,19 @@ + + + + + + + + + + + + + diff --git a/src/Spryker/Zed/AppPayment/Business/AppPaymentFacade.php b/src/Spryker/Zed/AppPayment/Business/AppPaymentFacade.php index 335b99d..4b5f8ab 100644 --- a/src/Spryker/Zed/AppPayment/Business/AppPaymentFacade.php +++ b/src/Spryker/Zed/AppPayment/Business/AppPaymentFacade.php @@ -17,6 +17,8 @@ use Generated\Shared\Transfer\InitializePaymentRequestTransfer; use Generated\Shared\Transfer\InitializePaymentResponseTransfer; use Generated\Shared\Transfer\PaymentCollectionDeleteCriteriaTransfer; +use Generated\Shared\Transfer\PaymentCollectionTransfer; +use Generated\Shared\Transfer\PaymentCriteriaTransfer; use Generated\Shared\Transfer\PaymentPageRequestTransfer; use Generated\Shared\Transfer\PaymentPageResponseTransfer; use Generated\Shared\Transfer\PaymentTransmissionsRequestTransfer; @@ -145,6 +147,16 @@ public function getRedirectUrl(RedirectRequestTransfer $redirectRequestTransfer) return $this->getFactory()->createRedirect()->getRedirectUrl($redirectRequestTransfer); } + /** + * {@inheritDoc} + * + * @api + */ + public function getPaymentCollection(PaymentCriteriaTransfer $paymentCriteriaTransfer): PaymentCollectionTransfer + { + return $this->getRepository()->getPaymentCollection($paymentCriteriaTransfer); + } + /** * {@inheritDoc} * diff --git a/src/Spryker/Zed/AppPayment/Business/AppPaymentFacadeInterface.php b/src/Spryker/Zed/AppPayment/Business/AppPaymentFacadeInterface.php index f8d4ffc..d49af9d 100644 --- a/src/Spryker/Zed/AppPayment/Business/AppPaymentFacadeInterface.php +++ b/src/Spryker/Zed/AppPayment/Business/AppPaymentFacadeInterface.php @@ -17,6 +17,8 @@ use Generated\Shared\Transfer\InitializePaymentRequestTransfer; use Generated\Shared\Transfer\InitializePaymentResponseTransfer; use Generated\Shared\Transfer\PaymentCollectionDeleteCriteriaTransfer; +use Generated\Shared\Transfer\PaymentCollectionTransfer; +use Generated\Shared\Transfer\PaymentCriteriaTransfer; use Generated\Shared\Transfer\PaymentPageRequestTransfer; use Generated\Shared\Transfer\PaymentPageResponseTransfer; use Generated\Shared\Transfer\PaymentTransmissionsRequestTransfer; @@ -171,6 +173,14 @@ public function handleRefundPayment(RefundPaymentTransfer $refundPaymentTransfer */ public function getRedirectUrl(RedirectRequestTransfer $redirectRequestTransfer): RedirectResponseTransfer; + /** + * Specification: + * - Returns a collection of payments based on the provided criteria. + * + * @api + */ + public function getPaymentCollection(PaymentCriteriaTransfer $paymentCriteriaTransfer): PaymentCollectionTransfer; + /** * Specification: * - Requires `PaymentCollectionDeleteCriteria.tenantIdentifier` to be set. diff --git a/src/Spryker/Zed/AppPayment/Business/MessageBroker/CapturePaymentMessageHandler.php b/src/Spryker/Zed/AppPayment/Business/MessageBroker/CapturePaymentMessageHandler.php index 6127052..ccd52d4 100644 --- a/src/Spryker/Zed/AppPayment/Business/MessageBroker/CapturePaymentMessageHandler.php +++ b/src/Spryker/Zed/AppPayment/Business/MessageBroker/CapturePaymentMessageHandler.php @@ -57,7 +57,7 @@ protected function determineAndSendMessage( $messageContextTransfer = $this->buildMessageContextTransfer($capturePaymentTransfer); match ($paymentStatus) { - PaymentStatus::STATUS_CAPTURED, PaymentStatus::STATUS_SUCCEEDED => $this->messageSender->sendPaymentCapturedMessage($paymentTransfer, $messageContextTransfer), + PaymentStatus::STATUS_CAPTURED => $this->messageSender->sendPaymentCapturedMessage($paymentTransfer, $messageContextTransfer), PaymentStatus::STATUS_CAPTURE_FAILED => $this->messageSender->sendPaymentCaptureFailedMessage($paymentTransfer, $messageContextTransfer), default => 'do nothing and wait for webhooks', }; diff --git a/src/Spryker/Zed/AppPayment/Business/Payment/Refund/PaymentRefundValidator.php b/src/Spryker/Zed/AppPayment/Business/Payment/Refund/PaymentRefundValidator.php index 0db4f07..05840d5 100644 --- a/src/Spryker/Zed/AppPayment/Business/Payment/Refund/PaymentRefundValidator.php +++ b/src/Spryker/Zed/AppPayment/Business/Payment/Refund/PaymentRefundValidator.php @@ -28,13 +28,13 @@ public function validatePaymentRefundRequest( $paymentStatus = $refundPaymentRequestTransfer->getPaymentOrFail()->getStatusOrFail(); // only captured and succeeded payments can be refunded, everything else is cancellation process - if ($paymentStatus !== PaymentStatus::STATUS_CAPTURED && $paymentStatus !== PaymentStatus::STATUS_SUCCEEDED) { + if ($paymentStatus !== PaymentStatus::STATUS_CAPTURED) { return (new RefundPaymentResponseTransfer()) ->setIsSuccessful(false) ->setMessage(sprintf( 'Payment is in status "%s" and cannot be refunded. Only payments that are in status "%s" can be refunded', $paymentStatus, - implode(', ', [PaymentStatus::STATUS_CAPTURED, PaymentStatus::STATUS_SUCCEEDED]), + PaymentStatus::STATUS_CAPTURED, )) ->setStatus(PaymentRefundStatus::FAILED); } diff --git a/src/Spryker/Zed/AppPayment/Business/Payment/Status/PaymentStatus.php b/src/Spryker/Zed/AppPayment/Business/Payment/Status/PaymentStatus.php index 2d3b22b..07798fa 100644 --- a/src/Spryker/Zed/AppPayment/Business/Payment/Status/PaymentStatus.php +++ b/src/Spryker/Zed/AppPayment/Business/Payment/Status/PaymentStatus.php @@ -49,11 +49,6 @@ enum PaymentStatus */ public const STATUS_AUTHORIZATION_FAILED = 'authorization_failed'; - /** - * @var string - */ - public const STATUS_SUCCEEDED = 'succeeded'; - /** * @var array> */ @@ -79,9 +74,6 @@ enum PaymentStatus PaymentStatus::STATUS_CANCELED, PaymentStatus::STATUS_CANCELLATION_FAILED, ], - PaymentStatus::STATUS_CAPTURED => [ - PaymentStatus::STATUS_SUCCEEDED, - ], PaymentStatus::STATUS_CAPTURE_FAILED => [ PaymentStatus::STATUS_CAPTURED, PaymentStatus::STATUS_CAPTURE_REQUESTED, diff --git a/src/Spryker/Zed/AppPayment/Communication/Controller/IndexController.php b/src/Spryker/Zed/AppPayment/Communication/Controller/IndexController.php index 02ec44b..6896ecd 100644 --- a/src/Spryker/Zed/AppPayment/Communication/Controller/IndexController.php +++ b/src/Spryker/Zed/AppPayment/Communication/Controller/IndexController.php @@ -1,4 +1,4 @@ -applyPaymentCriteria( + $this->getFactory()->createPaymentQuery(), + $paymentCriteriaTransfer, + )->find(); + + return $this->getFactory()->createPaymentMapper() + ->mapPaymentEntitiesToPaymentCollectionTransfer($paymentCollection, new PaymentCollectionTransfer()); + } + /** * @throws \Spryker\Zed\AppPayment\Persistence\Exception\PaymentByTransactionIdNotFoundException */ @@ -105,6 +119,23 @@ public function getRefundsByTransactionIdAndOrderItemIdAndStatuses( ->mapPaymentRefundEntityCollectionToPaymentRefundTransfers($paymentRefundEntityCollection); } + protected function applyPaymentCriteria( + SpyPaymentQuery $spyPaymentQuery, + PaymentCriteriaTransfer $paymentCriteriaTransfer + ): SpyPaymentQuery { + $paymentConditionsTransfer = $paymentCriteriaTransfer->getPaymentConditionsOrFail(); + + if ($paymentConditionsTransfer->getTenantIdentifier() !== null) { + $spyPaymentQuery->filterByTenantIdentifier($paymentConditionsTransfer->getTenantIdentifier()); + } + + if ($paymentConditionsTransfer->getExcludingStatuses() !== []) { + $spyPaymentQuery->filterByStatus($paymentConditionsTransfer->getExcludingStatuses(), Criteria::NOT_IN); + } + + return $spyPaymentQuery; + } + protected function mapPaymentEntityToPaymentTransfer(SpyPayment $spyPayment): PaymentTransfer { return $this->getFactory()->createPaymentMapper()->mapPaymentEntityToPaymentTransfer($spyPayment, new PaymentTransfer()); diff --git a/src/Spryker/Zed/AppPayment/Persistence/AppPaymentRepositoryInterface.php b/src/Spryker/Zed/AppPayment/Persistence/AppPaymentRepositoryInterface.php index a907c73..0c77e88 100644 --- a/src/Spryker/Zed/AppPayment/Persistence/AppPaymentRepositoryInterface.php +++ b/src/Spryker/Zed/AppPayment/Persistence/AppPaymentRepositoryInterface.php @@ -7,12 +7,16 @@ namespace Spryker\Zed\AppPayment\Persistence; +use Generated\Shared\Transfer\PaymentCollectionTransfer; +use Generated\Shared\Transfer\PaymentCriteriaTransfer; use Generated\Shared\Transfer\PaymentMethodTransfer; use Generated\Shared\Transfer\PaymentRefundTransfer; use Generated\Shared\Transfer\PaymentTransfer; interface AppPaymentRepositoryInterface { + public function getPaymentCollection(PaymentCriteriaTransfer $paymentCriteriaTransfer): PaymentCollectionTransfer; + /** * @throws \Spryker\Zed\AppPayment\Persistence\Exception\PaymentByTransactionIdNotFoundException */ diff --git a/src/Spryker/Zed/AppPayment/Persistence/Propel/Payment/Mapper/PaymentMapper.php b/src/Spryker/Zed/AppPayment/Persistence/Propel/Payment/Mapper/PaymentMapper.php index 7b1c800..c37b945 100644 --- a/src/Spryker/Zed/AppPayment/Persistence/Propel/Payment/Mapper/PaymentMapper.php +++ b/src/Spryker/Zed/AppPayment/Persistence/Propel/Payment/Mapper/PaymentMapper.php @@ -7,6 +7,7 @@ namespace Spryker\Zed\AppPayment\Persistence\Propel\Payment\Mapper; +use Generated\Shared\Transfer\PaymentCollectionTransfer; use Generated\Shared\Transfer\PaymentMethodTransfer; use Generated\Shared\Transfer\PaymentRefundTransfer; use Generated\Shared\Transfer\PaymentTransfer; @@ -19,6 +20,17 @@ class PaymentMapper { + public function mapPaymentEntitiesToPaymentCollectionTransfer( + Collection $paymentEntityCollection, + PaymentCollectionTransfer $paymentCollectionTransfer + ): PaymentCollectionTransfer { + foreach ($paymentEntityCollection as $paymentEntity) { + $paymentCollectionTransfer->addPayment($this->mapPaymentEntityToPaymentTransfer($paymentEntity, new PaymentTransfer())); + } + + return $paymentCollectionTransfer; + } + public function mapPaymentTransferToPaymentEntity(PaymentTransfer $paymentTransfer, SpyPayment $spyPayment): SpyPayment { $quoteTransfer = $paymentTransfer->getQuoteOrFail(); diff --git a/tests/SprykerTest/AsyncApi/AppPayment/AppPaymentTests/PaymentCommands/RefundPaymentTest.php b/tests/SprykerTest/AsyncApi/AppPayment/AppPaymentTests/PaymentCommands/RefundPaymentTest.php index 7931422..f914da2 100644 --- a/tests/SprykerTest/AsyncApi/AppPayment/AppPaymentTests/PaymentCommands/RefundPaymentTest.php +++ b/tests/SprykerTest/AsyncApi/AppPayment/AppPaymentTests/PaymentCommands/RefundPaymentTest.php @@ -80,43 +80,6 @@ public function testRefundPaymentMessageSendsPaymentRefundedMessageAndCreatesPay $this->tester->assertMessageWasSent(PaymentRefundedTransfer::class); } - public function testRefundPaymentMessageSendsPaymentRefundedMessageAndCreatesPaymentRefundWhenPaymentIsInSucceededState(): void - { - // Arrange - $tenantIdentifier = Uuid::uuid4()->toString(); - $transactionId = Uuid::uuid4()->toString(); - $refundId = Uuid::uuid4()->toString(); - $this->tester->haveAppConfigForTenant($tenantIdentifier); - $paymentTransfer = $this->tester->havePaymentForTransactionId($transactionId, $tenantIdentifier, PaymentStatus::STATUS_SUCCEEDED); - - $refundPaymentTransfer = $this->tester->haveRefundPaymentTransfer([ - MessageAttributesTransfer::TENANT_IDENTIFIER => $tenantIdentifier, - RefundPaymentTransfer::ORDER_REFERENCE => $paymentTransfer->getOrderReference(), - ]); - $refundPaymentResponseTransfer = (new RefundPaymentResponseTransfer()) - ->setIsSuccessful(true) - ->setRefundId($refundId) - ->setStatus(PaymentRefundStatus::SUCCEEDED); - - $platformPluginMock = Stub::makeEmpty(AppPaymentPlatformPluginInterface::class, [ - 'refundPayment' => function (RefundPaymentRequestTransfer $refundPaymentRequestTransfer) use ($refundPaymentResponseTransfer) { - $this->assertInstanceOf(AppConfigTransfer::class, $refundPaymentRequestTransfer->getAppConfig()); - $this->assertInstanceOf(PaymentTransfer::class, $refundPaymentRequestTransfer->getPayment()); - - return $refundPaymentResponseTransfer; - }, - ]); - - $this->getDependencyHelper()->setDependency(AppPaymentDependencyProvider::PLUGIN_PLATFORM, $platformPluginMock); - - // Act: This will trigger the MessageHandlerPlugin for this message. - $this->tester->runMessageReceiveTest($refundPaymentTransfer, 'payment-commands'); - - // Assert - $this->tester->assertPaymentRefundIsInStatus($refundId, PaymentRefundStatus::SUCCEEDED); - $this->tester->assertMessageWasSent(PaymentRefundedTransfer::class); - } - /** * @dataProvider refundSuccessfulPathStatusDataProvider * diff --git a/tests/SprykerTest/Glue/AppPaymentBackendApi/Plugin/PaymentConfirmDisconnectionRequestValidatorPluginTest.php b/tests/SprykerTest/Glue/AppPaymentBackendApi/Plugin/PaymentConfirmDisconnectionRequestValidatorPluginTest.php new file mode 100644 index 0000000..f652d2b --- /dev/null +++ b/tests/SprykerTest/Glue/AppPaymentBackendApi/Plugin/PaymentConfirmDisconnectionRequestValidatorPluginTest.php @@ -0,0 +1,193 @@ +tester->setDependency(AppPaymentBackendApiDependencyProvider::FACADE_TRANSLATOR, $this->getTranslatorFacadeMock()); + + $paymentConfirmDisconnectionRequestValidatorPlugin = new PaymentConfirmDisconnectionRequestValidatorPlugin(); + + // Act + $glueRequestValidationTransfer = $paymentConfirmDisconnectionRequestValidatorPlugin + ->validate(new GlueRequestTransfer()); + + // Assert + $this->assertFalse($glueRequestValidationTransfer->getIsValid()); + $this->assertCount(1, $glueRequestValidationTransfer->getErrors()); + $this->assertSame( + AppKernelConfig::ERROR_CODE_PAYMENT_DISCONNECTION_TENANT_IDENTIFIER_MISSING, + $glueRequestValidationTransfer->getErrors()[0]->getCode(), + ); + } + + public function testPaymentConfirmDisconnectionRequestValidatorPluginReturnsSuccessIfThereAreNoPaymentsForExistingTenant(): void + { + // Arrange + $this->tester->setDependency(AppPaymentBackendApiDependencyProvider::FACADE_TRANSLATOR, $this->getTranslatorFacadeMock()); + + $paymentConfirmDisconnectionRequestValidatorPlugin = new PaymentConfirmDisconnectionRequestValidatorPlugin(); + $glueRequestTransfer = new GlueRequestTransfer(); + $glueRequestTransfer->setMeta([ + GlueRequestPaymentMapper::HEADER_TENANT_IDENTIFIER => [static::TENANT_IDENTIFIER], + ]); + + // Act + $glueRequestValidationTransfer = $paymentConfirmDisconnectionRequestValidatorPlugin + ->validate($glueRequestTransfer); + + // Assert + $this->assertTrue($glueRequestValidationTransfer->getIsValid()); + } + + public function testPaymentConfirmDisconnectionRequestValidatorPluginReturnsErrorIfThereArePaymentsForExistingTenant(): void + { + // Arrange + $this->tester->setDependency(AppPaymentBackendApiDependencyProvider::FACADE_TRANSLATOR, $this->getTranslatorFacadeMock()); + + $paymentConfirmDisconnectionRequestValidatorPlugin = new PaymentConfirmDisconnectionRequestValidatorPlugin(); + $glueRequestTransfer = new GlueRequestTransfer(); + $glueRequestTransfer->setMeta([ + GlueRequestPaymentMapper::HEADER_TENANT_IDENTIFIER => [static::TENANT_IDENTIFIER], + ]); + + $this->tester->havePayment([ + PaymentTransfer::TENANT_IDENTIFIER => static::TENANT_IDENTIFIER, + ]); + + // Act + $glueRequestValidationTransfer = $paymentConfirmDisconnectionRequestValidatorPlugin + ->validate($glueRequestTransfer); + + // Assert + $this->assertFalse($glueRequestValidationTransfer->getIsValid()); + $this->assertCount(1, $glueRequestValidationTransfer->getErrors()); + $this->assertSame( + AppPaymentBackendApiConfig::ERROR_CODE_PAYMENT_DISCONNECTION_CANNOT_BE_PROCEEDED, + $glueRequestValidationTransfer->getErrors()[0]->getCode(), + ); + $this->assertInstanceOf(GlueErrorConfirmTransfer::class, $glueRequestValidationTransfer->getErrors()[0]->getConfirm()); + } + + /** + * @dataProvider confirmationStatusDataProvider + * + * @param string $confirmationStatus + * + * @return void + */ + public function testPaymentConfirmDisconnectionRequestValidatorPluginReturnsErrorIfThereArePaymentsForExistingTenantAndTheRequestContainsConfirmationCanceledResponse( + string $confirmationStatus + ): void { + // Arrange + $this->tester->setDependency(AppPaymentBackendApiDependencyProvider::FACADE_TRANSLATOR, $this->getTranslatorFacadeMock()); + + $paymentConfirmDisconnectionRequestValidatorPlugin = new PaymentConfirmDisconnectionRequestValidatorPlugin(); + $glueRequestTransfer = new GlueRequestTransfer(); + $glueRequestTransfer->setMeta([ + GlueRequestPaymentMapper::HEADER_TENANT_IDENTIFIER => [static::TENANT_IDENTIFIER], + static::HEADER_CONFIRMATION_STATUS => [$confirmationStatus], + ]); + + $this->tester->havePayment([ + PaymentTransfer::TENANT_IDENTIFIER => static::TENANT_IDENTIFIER, + ]); + + // Act + $glueRequestValidationTransfer = $paymentConfirmDisconnectionRequestValidatorPlugin + ->validate($glueRequestTransfer); + + // Assert + $this->assertFalse($glueRequestValidationTransfer->getIsValid()); + $this->assertCount(1, $glueRequestValidationTransfer->getErrors()); + $this->assertSame( + AppPaymentBackendApiConfig::ERROR_CODE_PAYMENT_DISCONNECTION_FORBIDDEN, + $glueRequestValidationTransfer->getErrors()[0]->getCode(), + ); + } + + public function testPaymentConfirmDisconnectionRequestValidatorPluginReturnsSuccessIfThereArePaymentsForExistingTenantAndTheRequestContainsConfirmationSuccessfulResponse(): void + { + // Arrange + $this->tester->setDependency(AppPaymentBackendApiDependencyProvider::FACADE_TRANSLATOR, $this->getTranslatorFacadeMock()); + + $paymentConfirmDisconnectionRequestValidatorPlugin = new PaymentConfirmDisconnectionRequestValidatorPlugin(); + $glueRequestTransfer = new GlueRequestTransfer(); + $glueRequestTransfer->setMeta([ + GlueRequestPaymentMapper::HEADER_TENANT_IDENTIFIER => [static::TENANT_IDENTIFIER], + static::HEADER_CONFIRMATION_STATUS => ['true'], + ]); + + $this->tester->havePayment([ + PaymentTransfer::TENANT_IDENTIFIER => static::TENANT_IDENTIFIER, + ]); + + // Act + $glueRequestValidationTransfer = $paymentConfirmDisconnectionRequestValidatorPlugin + ->validate($glueRequestTransfer); + + // Assert + $this->assertTrue($glueRequestValidationTransfer->getIsValid()); + } + + protected function getTranslatorFacadeMock(): AppPaymentBackendApiToTranslatorFacadeInterface + { + return $this->getMockBuilder(AppPaymentBackendApiToTranslatorFacadeInterface::class)->getMock(); + } + + /** + * @return array> + */ + public function confirmationStatusDataProvider(): array + { + return [ + 'confirmation status false' => ['false'], + 'confirmation status double false' => ['false,true'], + 'confirmation status any value' => ['value'], + ]; + } +} diff --git a/tests/SprykerTest/Glue/AppPaymentBackendApi/RestApi/WebhooksPaymentApiTest.php b/tests/SprykerTest/Glue/AppPaymentBackendApi/RestApi/WebhooksPaymentApiTest.php index cec6f18..99fde8a 100644 --- a/tests/SprykerTest/Glue/AppPaymentBackendApi/RestApi/WebhooksPaymentApiTest.php +++ b/tests/SprykerTest/Glue/AppPaymentBackendApi/RestApi/WebhooksPaymentApiTest.php @@ -192,26 +192,6 @@ public function testGivenPaymentInStateCaptureRequestedWhenThePlatformPluginRetu $this->tester->assertPaymentIsInState($transactionId, PaymentStatus::STATUS_CAPTURE_FAILED); } - public function testGivenPaymentInStateCapturedWhenThePlatformPluginReturnsASuccessfulWebhookResponseTransferAndSucceededStatusThenPaymentIsMovedToSucceeded(): void - { - // Arrange - $transactionId = Uuid::uuid4()->toString(); - $tenantIdentifier = Uuid::uuid4()->toString(); - - $this->tester->haveAppConfigForTenant($tenantIdentifier); - $this->tester->havePaymentForTransactionId($transactionId, $tenantIdentifier, PaymentStatus::STATUS_CAPTURED); - - $this->tester->mockGlueRequestWebhookMapperPlugin(WebhookDataType::PAYMENT, $transactionId); - $this->mockPaymentPlatformPlugin(true, PaymentStatus::STATUS_SUCCEEDED); - - // Act - $this->tester->sendPost($this->tester->buildWebhookUrl(), ['data' => ['object' => ['id' => 123456789]]]); - - // Assert - $this->tester->seeResponseCodeIs(Response::HTTP_OK); - $this->tester->assertPaymentIsInState($transactionId, PaymentStatus::STATUS_SUCCEEDED); - } - public function testGivenNoContentInPostRequestWhenISendTheRequestThenTheApplicationReturnsAHttpStatus400(): void { // Act diff --git a/tests/SprykerTest/Glue/AppPaymentBackendApi/_support/AppPaymentBackendApiPluginTester.php b/tests/SprykerTest/Glue/AppPaymentBackendApi/_support/AppPaymentBackendApiPluginTester.php new file mode 100644 index 0000000..f2ef344 --- /dev/null +++ b/tests/SprykerTest/Glue/AppPaymentBackendApi/_support/AppPaymentBackendApiPluginTester.php @@ -0,0 +1,33 @@ +addCommands([ new GenerateGlueIdeAutoCompletionConsole(), new GenerateGlueBackendIdeAutoCompletionConsole(), new ControllerCacheCollectorConsole(), + new GenerateTranslationCacheConsole(), new class extends Command { /**