Skip to content

Commit

Permalink
ACP-2930: Added validation plugin for disconnection (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
pushokwhite authored Oct 3, 2024
1 parent fd7b7d9 commit e5b61a0
Show file tree
Hide file tree
Showing 29 changed files with 491 additions and 73 deletions.
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions data/translation/Zed/de_DE.csv
Original file line number Diff line number Diff line change
@@ -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."
2 changes: 2 additions & 0 deletions data/translation/Zed/en_US.csv
Original file line number Diff line number Diff line change
@@ -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."
2 changes: 2 additions & 0 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@
],
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',
__DIR__ . '/src/Spryker/Zed/AppPayment/Dependency/Service/AppPaymentToUtilEncodingServiceBridge.php',
],
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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
}
Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -52,4 +54,9 @@ public function cancelPreOrderPayment(
): CancelPreOrderPaymentResponseTransfer {
return $this->appPaymentFacade->cancelPreOrderPayment($cancelPreOrderPaymentRequestTransfer);
}

public function getPaymentCollection(PaymentCriteriaTransfer $paymentCriteriaTransfer): PaymentCollectionTransfer
{
return $this->appPaymentFacade->getPaymentCollection($paymentCriteriaTransfer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -29,4 +31,6 @@ public function confirmPreOrderPayment(
public function cancelPreOrderPayment(
CancelPreOrderPaymentRequestTransfer $cancelPreOrderPaymentRequestTransfer
): CancelPreOrderPaymentResponseTransfer;

public function getPaymentCollection(PaymentCriteriaTransfer $paymentCriteriaTransfer): PaymentCollectionTransfer;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

/**
* This file is part of the Spryker Suite.
* For full license information, please view the LICENSE file that was distributed with this source code.
*/

namespace Spryker\Glue\AppPaymentBackendApi\Dependency\Facade;

class AppPaymentBackendApiToTranslatorFacadeBridge implements AppPaymentBackendApiToTranslatorFacadeInterface
{
/**
* @var \Spryker\Zed\Translator\Business\TranslatorFacadeInterface
*/
protected $translatorFacade;

/**
* @param \Spryker\Zed\Translator\Business\TranslatorFacadeInterface $translatorFacade
*/
public function __construct($translatorFacade)
{
$this->translatorFacade = $translatorFacade;
}

/**
* @param array<mixed> $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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

/**
* This file is part of the Spryker Suite.
* For full license information, please view the LICENSE file that was distributed with this source code.
*/

namespace Spryker\Glue\AppPaymentBackendApi\Dependency\Facade;

interface AppPaymentBackendApiToTranslatorFacadeInterface
{
/**
* @param array<mixed> $parameters
* @param string|null $domain
* @param string|null $locale
*/
public function trans(string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

/**
* This file is part of the Spryker Suite.
* For full license information, please view the LICENSE file that was distributed with this source code.
*/

namespace Spryker\Glue\AppPaymentBackendApi\Plugin\GlueApplication;

use Generated\Shared\Transfer\GlueRequestTransfer;
use Generated\Shared\Transfer\GlueRequestValidationTransfer;
use Generated\Shared\Transfer\PaymentConditionsTransfer;
use Generated\Shared\Transfer\PaymentCriteriaTransfer;
use Spryker\Glue\AppKernel\Plugin\GlueApplication\AbstractConfirmDisconnectionRequestValidatorPlugin;
use Spryker\Glue\AppPaymentBackendApi\AppPaymentBackendApiConfig;
use Spryker\Zed\AppPayment\Business\Payment\Status\PaymentStatus;

/**
* @method \Spryker\Glue\AppPaymentBackendApi\AppPaymentBackendApiFactory getFactory()
*/
class PaymentConfirmDisconnectionRequestValidatorPlugin extends AbstractConfirmDisconnectionRequestValidatorPlugin
{
protected function validateDisconnectionRequest(GlueRequestTransfer $glueRequestTransfer, string $tenantIdentifier): GlueRequestValidationTransfer
{
$paymentCollectionTransfer = $this->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.');
}
}
13 changes: 13 additions & 0 deletions src/Spryker/Shared/AppPayment/Transfer/app_payment.transfer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,19 @@
<property name="amount" type="string"/>
</transfer>

<transfer name="PaymentCriteriaTransfer" strict="true">
<property name="paymentConditions" type="PaymentConditions"/>
</transfer>

<transfer name="PaymentConditions" strict="true">
<property name="tenantIdentifier" type="string"/>
<property name="excludingStatuses" type="string[]" singular="excludingStatus"/>
</transfer>

<transfer name="PaymentCollectionTransfer" strict="true">
<property name="payments" type="Payment[]" singular="payment"/>
</transfer>

<transfer name="PaymentCollectionDeleteCriteria" strict="true">
<property name="tenantIdentifier" type="string"/>
<property name="transactionId" type="string"/>
Expand Down
12 changes: 12 additions & 0 deletions src/Spryker/Zed/AppPayment/Business/AppPaymentFacade.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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}
*
Expand Down
10 changes: 10 additions & 0 deletions src/Spryker/Zed/AppPayment/Business/AppPaymentFacadeInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,6 @@ enum PaymentStatus
*/
public const STATUS_AUTHORIZATION_FAILED = 'authorization_failed';

/**
* @var string
*/
public const STATUS_SUCCEEDED = 'succeeded';

/**
* @var array<string, array<string>>
*/
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php
<?php /** @noinspection PhpUndefinedClassInspection */

/**
* This file is part of the Spryker Suite.
Expand All @@ -14,7 +14,6 @@

/**
* @method \Spryker\Zed\AppPayment\Business\AppPaymentFacadeInterface getFacade()
* @method \Spryker\Zed\AppPayment\Communication\AppPaymentCommunicationFactory getFactory()
* @method \Spryker\Zed\AppPayment\Persistence\AppPaymentRepositoryInterface getRepository()
*/
class IndexController extends AbstractController
Expand Down
Loading

0 comments on commit e5b61a0

Please sign in to comment.