Add payment request pay route to be able to finalize a capture or an authorize request

This commit is contained in:
Francis Hilaire 2024-10-31 12:50:36 +01:00
parent a6c03ab9f9
commit fbf0a38891
No known key found for this signature in database
GPG key ID: 3392F830BF33D421
20 changed files with 239 additions and 42 deletions

View file

@ -0,0 +1,58 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\CoreBundle\OrderPay\Action;
use Sylius\Bundle\CoreBundle\OrderPay\Provider\UrlProviderInterface;
use Sylius\Bundle\PaymentBundle\Processor\HttpResponseProcessorInterface;
use Sylius\Bundle\ResourceBundle\Controller\RequestConfigurationFactoryInterface;
use Sylius\Component\Payment\Model\PaymentRequestInterface;
use Sylius\Component\Payment\Repository\PaymentRequestRepositoryInterface;
use Sylius\Resource\Metadata\MetadataInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
final class PaymentRequestPayAction
{
/**
* @param PaymentRequestRepositoryInterface<PaymentRequestInterface> $paymentRequestRepository
*/
public function __construct(
private MetadataInterface $paymentRequestMetadata,
private RequestConfigurationFactoryInterface $requestConfigurationFactory,
private PaymentRequestRepositoryInterface $paymentRequestRepository,
private HttpResponseProcessorInterface $httpResponseProcessor,
private UrlProviderInterface $afterPayUrlProvider,
) {
}
public function __invoke(Request $request, string $hash): Response
{
$requestConfiguration = $this->requestConfigurationFactory->create($this->paymentRequestMetadata, $request);
$paymentRequest = $this->paymentRequestRepository->find($hash);
if (null === $paymentRequest) {
throw new NotFoundHttpException(sprintf('No payment request found with hash "%s".', $hash));
}
$response = $this->httpResponseProcessor->process($requestConfiguration, $paymentRequest);
if (null !== $response) {
return $response;
}
return new RedirectResponse($this->afterPayUrlProvider->getUrl($paymentRequest));
}
}

View file

@ -14,9 +14,7 @@ declare(strict_types=1);
namespace Sylius\Bundle\CoreBundle\OrderPay\Provider;
use Sylius\Bundle\CoreBundle\OrderPay\Handler\PaymentStateFlashHandlerInterface;
use Sylius\Bundle\PaymentBundle\Announcer\PaymentRequestAnnouncerInterface;
use Sylius\Bundle\PaymentBundle\CommandProvider\PaymentRequestCommandProviderInterface;
use Sylius\Bundle\PaymentBundle\Provider\ServiceProviderAwareProviderInterface;
use Sylius\Bundle\PaymentBundle\Processor\HttpResponseProcessorInterface;
use Sylius\Bundle\ResourceBundle\Controller\RequestConfiguration;
use Sylius\Component\Core\Model\PaymentInterface;
use Sylius\Component\Payment\Factory\PaymentRequestFactoryInterface;
@ -36,10 +34,8 @@ final class PaymentRequestAfterPayResponseProvider implements AfterPayResponsePr
*/
public function __construct(
private PaymentRequestFactoryInterface $paymentRequestFactory,
private PaymentRequestAnnouncerInterface $paymentRequestAnnouncer,
private ServiceProviderAwareProviderInterface $httpResponseProvider,
private HttpResponseProcessorInterface $httpResponseProcessor,
private PaymentRequestRepositoryInterface $paymentRequestRepository,
private PaymentRequestCommandProviderInterface $paymentRequestCommandProvider,
private PaymentStateFlashHandlerInterface $paymentStateFlashHandler,
private FinalUrlProviderInterface $orderPayFinalUrlProvider,
) {
@ -59,20 +55,15 @@ final class PaymentRequestAfterPayResponseProvider implements AfterPayResponsePr
$paymentRequest = $this->paymentRequestFactory->createFromPaymentRequest($previousPaymentRequest);
$paymentRequest->setAction(PaymentRequestInterface::ACTION_STATUS);
if ($this->paymentRequestCommandProvider->supports($paymentRequest)) {
$this->paymentRequestRepository->add($paymentRequest);
$this->paymentRequestAnnouncer->dispatchPaymentRequestCommand($paymentRequest);
}
$this->paymentRequestRepository->add($paymentRequest);
if ($this->httpResponseProvider->supports($requestConfiguration, $paymentRequest)) {
return $this->httpResponseProvider->getResponse($requestConfiguration, $paymentRequest);
}
$response = $this->httpResponseProcessor->process($requestConfiguration, $paymentRequest);
/** @var PaymentInterface $payment */
$payment = $paymentRequest->getPayment();
$this->paymentStateFlashHandler->handle($requestConfiguration, $payment->getState());
return new RedirectResponse($this->orderPayFinalUrlProvider->getUrl($payment));
return $response ?? new RedirectResponse($this->orderPayFinalUrlProvider->getUrl($payment));
}
public function supports(RequestConfiguration $requestConfiguration): bool

View file

@ -14,10 +14,8 @@ declare(strict_types=1);
namespace Sylius\Bundle\CoreBundle\OrderPay\Provider;
use Sylius\Bundle\CoreBundle\OrderPay\Resolver\PaymentToPayResolverInterface;
use Sylius\Bundle\PaymentBundle\Announcer\PaymentRequestAnnouncerInterface;
use Sylius\Bundle\PaymentBundle\Provider\DefaultActionProviderInterface;
use Sylius\Bundle\PaymentBundle\Provider\DefaultPayloadProviderInterface;
use Sylius\Bundle\PaymentBundle\Provider\ServiceProviderAwareProviderInterface;
use Sylius\Bundle\ResourceBundle\Controller\RequestConfiguration;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Payment\Factory\PaymentRequestFactoryInterface;
@ -37,12 +35,10 @@ final class PaymentRequestPayResponseProvider implements PayResponseProviderInte
public function __construct(
private PaymentRequestFactoryInterface $paymentRequestFactory,
private PaymentRequestRepositoryInterface $paymentRequestRepository,
private PaymentRequestAnnouncerInterface $paymentRequestAnnouncer,
private ServiceProviderAwareProviderInterface $httpResponseProvider,
private DefaultActionProviderInterface $defaultActionProvider,
private DefaultPayloadProviderInterface $defaultPayloadProvider,
private PaymentToPayResolverInterface $paymentToPayResolver,
private AfterPayUrlProviderInterface $afterPayUrlProvider,
private UrlProviderInterface $paymentRequestPayUrlProvider,
) {
}
@ -55,19 +51,23 @@ final class PaymentRequestPayResponseProvider implements PayResponseProviderInte
Assert::notNull($paymentMethod, sprintf('Payment (id %s) must have payment method.', $payment->getId()));
$paymentRequest = $this->paymentRequestFactory->create($payment, $paymentMethod);
$action = $this->defaultActionProvider->getAction($paymentRequest);
$paymentRequest->setAction($action);
$paymentRequest->setAction($this->defaultActionProvider->getAction($paymentRequest));
$paymentRequest->setPayload($this->defaultPayloadProvider->getPayload($paymentRequest));
$existingPaymentRequest = $this->paymentRequestRepository->findOneByActionPaymentAndMethod(
$action,
$payment,
$paymentMethod
);
$this->paymentRequestRepository->add($paymentRequest);
$this->paymentRequestAnnouncer->dispatchPaymentRequestCommand($paymentRequest);
if ($this->httpResponseProvider->supports($requestConfiguration, $paymentRequest)) {
return $this->httpResponseProvider->getResponse($requestConfiguration, $paymentRequest);
if (null === $existingPaymentRequest) {
$paymentRequest->setPayload($this->defaultPayloadProvider->getPayload($paymentRequest));
$this->paymentRequestRepository->add($paymentRequest);
} else {
$paymentRequest = $existingPaymentRequest;
}
return new RedirectResponse($this->afterPayUrlProvider->getUrl($paymentRequest));
return new RedirectResponse($this->paymentRequestPayUrlProvider->getUrl($paymentRequest));
}
public function supports(RequestConfiguration $requestConfiguration, OrderInterface $order): bool

View file

@ -18,7 +18,7 @@ use Sylius\Component\Payment\Model\PaymentRequestInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/** @experimental */
final class AfterPayUrlProvider implements AfterPayUrlProviderInterface
final class UrlProvider implements UrlProviderInterface
{
/**
* @param array<string, string> $afterPayRouteParameters

View file

@ -17,7 +17,7 @@ use Sylius\Component\Payment\Model\PaymentRequestInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/** @experimental */
interface AfterPayUrlProviderInterface
interface UrlProviderInterface
{
public function getUrl(
PaymentRequestInterface $paymentRequest,

View file

@ -29,5 +29,17 @@
<argument type="service" id="sylius.resource_controller.request_configuration_factory" />
</service>
<service abstract="true" id="sylius.controller.payment_request_pay" class="Sylius\Bundle\CoreBundle\OrderPay\Action\PaymentRequestPayAction">
<argument type="service">
<service class="Sylius\Component\Resource\Metadata\MetadataInterface">
<factory service="sylius.resource_registry" method="get" />
<argument type="string">sylius.payment_request</argument>
</service>
</argument>
<argument type="service" id="sylius.resource_controller.request_configuration_factory" />
<argument type="service" id="sylius.repository.payment_request" />
<argument type="service" id="sylius.processor.payment_request.http_response" />
</service>
</services>
</container>

View file

@ -17,7 +17,11 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"
>
<services>
<service abstract="true" id="sylius.provider.order_pay.after_pay_url" class="Sylius\Bundle\CoreBundle\OrderPay\Provider\AfterPayUrlProvider">
<service abstract="true" id="sylius.provider.order_pay.payment_request_pay_url" class="Sylius\Bundle\CoreBundle\OrderPay\Provider\UrlProvider">
<argument type="service" id="sylius.processor.order_pay.route_parameters" />
</service>
<service abstract="true" id="sylius.provider.order_pay.after_pay_url" class="Sylius\Bundle\CoreBundle\OrderPay\Provider\UrlProvider">
<argument type="service" id="sylius.processor.order_pay.route_parameters" />
</service>
@ -30,8 +34,6 @@
<service abstract="true" id="sylius.provider.order_pay.pay_response.payment_request" class="Sylius\Bundle\CoreBundle\OrderPay\Provider\PaymentRequestPayResponseProvider">
<argument type="service" id="sylius.factory.payment_request" />
<argument type="service" id="sylius.repository.payment_request" />
<argument type="service" id="sylius.announcer.payment_request" />
<argument type="service" id="sylius.provider.http_response.default" />
<argument type="service" id="sylius.provider.default_action" />
<argument type="service" id="sylius.provider.default_payload" />
</service>
@ -42,10 +44,8 @@
<service abstract="true" id="sylius.provider.order_pay.after_pay_response.payment_request" class="Sylius\Bundle\CoreBundle\OrderPay\Provider\PaymentRequestAfterPayResponseProvider">
<argument type="service" id="sylius.factory.payment_request" />
<argument type="service" id="sylius.announcer.payment_request" />
<argument type="service" id="sylius.provider.http_response.default" />
<argument type="service" id="sylius.processor.payment_request.http_response" />
<argument type="service" id="sylius.repository.payment_request" />
<argument type="service" id="sylius.command_provider.payment_request.default" />
</service>
</services>
</container>

View file

@ -42,9 +42,7 @@ final class PaymentRequestNotifyAction
public function __invoke(Request $request, string $hash): Response
{
$paymentRequest = $this->paymentRequestRepository->findOneBy([
'hash' => $hash,
]);
$paymentRequest = $this->paymentRequestRepository->find($hash);
if (null === $paymentRequest) {
throw new NotFoundHttpException(sprintf('No payment request found with hash "%s".', $hash));

View file

@ -15,6 +15,8 @@ namespace Sylius\Bundle\PaymentBundle\Doctrine\ORM;
use Doctrine\ORM\QueryBuilder;
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
use Sylius\Component\Payment\Model\PaymentInterface;
use Sylius\Component\Payment\Model\PaymentMethodInterface;
use Sylius\Component\Payment\Model\PaymentRequestInterface;
use Sylius\Component\Payment\Repository\PaymentRequestRepositoryInterface;
use Symfony\Bridge\Doctrine\Types\UuidType;
@ -65,8 +67,8 @@ class PaymentRequestRepository extends EntityRepository implements PaymentReques
->andWhere('o.method = :method')
->setParameter('paymentRequest', $paymentRequest->getHash(), UuidType::NAME)
->setParameter('action', $paymentRequest->getAction())
->setParameter('method', $paymentRequest->getMethod())
->setParameter('payment', $paymentRequest->getPayment())
->setParameter('method', $paymentRequest->getMethod())
->getQuery()
->getResult()
;
@ -90,4 +92,25 @@ class PaymentRequestRepository extends EntityRepository implements PaymentReques
->getResult()
;
}
public function findOneByActionPaymentAndMethod(
string $action,
PaymentInterface $payment,
PaymentMethodInterface $paymentMethod,
): ?PaymentRequestInterface {
$queryBuilder = $this->createQueryBuilder('o');
return $queryBuilder
->innerJoin('o.payment', 'payment')
->innerJoin('o.method', 'method')
->andWhere('o.action = :action')
->andWhere('o.payment = :payment')
->andWhere('o.method = :method')
->setParameter('action', $action)
->setParameter('payment', $payment)
->setParameter('method', $paymentMethod)
->getQuery()
->getOneOrNullResult()
;
}
}

View file

@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\PaymentBundle\Processor;
use Sylius\Bundle\PaymentBundle\Announcer\PaymentRequestAnnouncerInterface;
use Sylius\Bundle\PaymentBundle\Provider\ServiceProviderAwareProviderInterface;
use Sylius\Bundle\ResourceBundle\Controller\RequestConfiguration;
use Sylius\Component\Payment\Model\PaymentRequestInterface;
use Symfony\Component\HttpFoundation\Response;
final class HttpResponseProcessor implements HttpResponseProcessorInterface
{
public function __construct(
private PaymentRequestAnnouncerInterface $paymentRequestAnnouncer,
private ServiceProviderAwareProviderInterface $httpResponseProvider,
) {
}
public function process(
RequestConfiguration $requestConfiguration,
PaymentRequestInterface $paymentRequest
): ?Response {
$this->paymentRequestAnnouncer->dispatchPaymentRequestCommand($paymentRequest);
if ($this->httpResponseProvider->supports($requestConfiguration, $paymentRequest)) {
return $this->httpResponseProvider->getResponse($requestConfiguration, $paymentRequest);
}
return null;
}
}

View file

@ -0,0 +1,26 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\PaymentBundle\Processor;
use Sylius\Bundle\ResourceBundle\Controller\RequestConfiguration;
use Sylius\Component\Payment\Model\PaymentRequestInterface;
use Symfony\Component\HttpFoundation\Response;
interface HttpResponseProcessorInterface
{
public function process(
RequestConfiguration $requestConfiguration,
PaymentRequestInterface $paymentRequest
): ?Response;
}

View file

@ -21,5 +21,11 @@
<argument type="service" id="sylius.normalizer.symfony_request" />
</service>
<service id="Sylius\Bundle\PaymentBundle\Processor\NotifyPayloadProcessorInterface" alias="sylius.processor.notify_payload" />
<service id="sylius.processor.payment_request.http_response" class="Sylius\Bundle\PaymentBundle\Processor\HttpResponseProcessor">
<argument type="service" id="sylius.announcer.payment_request" />
<argument type="service" id="sylius.provider.http_response.default" />
</service>
<service id="Sylius\Bundle\PaymentBundle\Processor\HttpResponseProcessorInterface" alias="sylius.processor.payment_request.http_response" />
</services>
</container>

View file

@ -69,6 +69,17 @@ final class Configuration implements ConfigurationInterface
->arrayNode('order_pay')
->addDefaultsIfNotSet()
->children()
->scalarNode('payment_request_pay_route')
->defaultValue('sylius_shop_payment_request_pay')
->end()
->arrayNode('payment_request_pay_route_parameters')
->addDefaultsIfNotSet()
->children()
->scalarNode('hash')
->defaultValue('paymentRequest.getHash()')
->end()
->end()
->end()
->scalarNode('after_pay_route')
->defaultValue('sylius_shop_order_after_pay')
->end()

View file

@ -127,6 +127,8 @@ final class SyliusShopExtension extends Extension implements PrependExtensionInt
private function configureOrderPay(array $config, ContainerBuilder $container): void
{
$container->setParameter('sylius_shop.order_pay.payment_request_pay_route', $config['payment_request_pay_route']);
$container->setParameter('sylius_shop.order_pay.payment_request_pay_route_parameters', $config['payment_request_pay_route_parameters']);
$container->setParameter('sylius_shop.order_pay.after_pay_route', $config['after_pay_route']);
$container->setParameter('sylius_shop.order_pay.after_pay_route_parameters', $config['after_pay_route_parameters']);
$container->setParameter('sylius_shop.order_pay.final_route', $config['final_route']);

View file

@ -42,6 +42,10 @@ sylius_shop_order:
resource: "@SyliusShopBundle/Resources/config/routing/order.yml"
prefix: /order
sylius_shop_payment_request:
resource: "@SyliusShopBundle/Resources/config/routing/payment_request.yaml"
prefix: /payment-request
sylius_shop_account:
resource: "@SyliusShopBundle/Resources/config/routing/account.yml"
prefix: /account

View file

@ -0,0 +1,7 @@
# This file is part of the Sylius package.
# (c) Sylius Sp. z o.o.
sylius_shop_payment_request_pay:
path: /pay/{hash}
defaults:
_controller: sylius_shop.controller.payment_request_pay

View file

@ -21,5 +21,9 @@
<argument type="tagged" tag="sylius_shop.provider.order_pay.pay_response" />
<argument type="tagged" tag="sylius_shop.provider.order_pay.after_pay_response" />
</service>
<service id="sylius_shop.controller.payment_request_pay" parent="sylius.controller.payment_request_pay">
<argument type="service" id="sylius_shop.provider.order_pay.after_pay_url" />
</service>
</services>
</container>

View file

@ -17,6 +17,11 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"
>
<services>
<service id="sylius_shop.provider.order_pay.payment_request_pay_url" parent="sylius.provider.order_pay.payment_request_pay_url">
<argument type="string">%sylius_shop.order_pay.payment_request_pay_route%</argument>
<argument type="string">%sylius_shop.order_pay.payment_request_pay_route_parameters%</argument>
</service>
<service id="sylius_shop.provider.order_pay.after_pay_url" parent="sylius.provider.order_pay.after_pay_url">
<argument type="string">%sylius_shop.order_pay.after_pay_route%</argument>
<argument type="string">%sylius_shop.order_pay.after_pay_route_parameters%</argument>
@ -39,7 +44,7 @@
<service id="sylius_shop.provider.order_pay.pay_response.payment_request" parent="sylius.provider.order_pay.pay_response.payment_request">
<argument type="service" id="sylius_shop.resolver.order_pay.payment_to_pay" />
<argument type="service" id="sylius_shop.provider.order_pay.after_pay_url" />
<argument type="service" id="sylius_shop.provider.order_pay.payment_request_pay_url" />
<tag name="sylius_shop.provider.order_pay.pay_response" priority="-300" />
</service>

View file

@ -41,7 +41,7 @@ class PaymentRequest implements PaymentRequestInterface
public function getId(): ?string
{
return $this->getHash()?->toBinary();
return $this->getHash()?->__toString();
}
public function getHash(): ?Uuid

View file

@ -14,6 +14,8 @@ declare(strict_types=1);
namespace Sylius\Component\Payment\Repository;
use Doctrine\ORM\QueryBuilder;
use Sylius\Component\Payment\Model\PaymentInterface;
use Sylius\Component\Payment\Model\PaymentMethodInterface;
use Sylius\Component\Payment\Model\PaymentRequestInterface;
use Sylius\Resource\Doctrine\Persistence\RepositoryInterface;
@ -38,4 +40,10 @@ interface PaymentRequestRepositoryInterface extends RepositoryInterface
* @return array<PaymentRequestInterface>
*/
public function findByPaymentIdAndStates(mixed $paymentId, array $states): array;
public function findOneByActionPaymentAndMethod(
string $action,
PaymentInterface $payment,
PaymentMethodInterface $paymentMethod
): ?PaymentRequestInterface;
}