From 53ccf944d55b64e551793e52a2934c2b7f3d35da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jib=C3=A9=20Barth?= Date: Tue, 27 May 2025 11:27:43 +0200 Subject: [PATCH 01/11] PPSYL-138 - Use Payment request flow for payplug Only for redirect, add CapturePaymentRequest, StatusPaymentRequest --- config/services.yaml | 23 ++++++- .../PayPlugApiClientFactoryInterface.php | 3 + src/Command/CapturePaymentRequest.php | 15 ++++ .../Handler/CapturePaymentRequestHandler.php | 68 +++++++++++++++++++ .../Handler/StatusPaymentRequestHandler.php | 64 +++++++++++++++++ .../CapturePaymentRequestCommandProvider.php | 25 +++++++ .../StatusPaymentRequestCommandProvider.php | 25 +++++++ src/Command/StatusPaymentRequest.php | 15 ++++ .../CaptureAuthorizedPaymentCommand.php | 2 +- src/Creator/PayPlugPaymentDataCreator.php | 6 +- .../Type/AbstractGatewayConfigurationType.php | 6 +- .../Type/PayPlugGatewayConfigurationType.php | 8 +++ .../Provider/CaptureHttpResponseProvider.php | 32 +++++++++ 13 files changed, 283 insertions(+), 9 deletions(-) create mode 100644 src/Command/CapturePaymentRequest.php create mode 100644 src/Command/Handler/CapturePaymentRequestHandler.php create mode 100644 src/Command/Handler/StatusPaymentRequestHandler.php create mode 100644 src/Command/Provider/CapturePaymentRequestCommandProvider.php create mode 100644 src/Command/Provider/StatusPaymentRequestCommandProvider.php create mode 100644 src/Command/StatusPaymentRequest.php rename src/{ => Console}/Command/CaptureAuthorizedPaymentCommand.php (98%) create mode 100644 src/OrderPay/Provider/CaptureHttpResponseProvider.php diff --git a/config/services.yaml b/config/services.yaml index 277215c9..663d2046 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -6,7 +6,7 @@ services: PayPlug\SyliusPayPlugPlugin\: resource: '../src/*' - exclude: '../src/{ApiClient,DependencyInjection,Entity,Exception,Gateway,Model,Repository,PayPlugSyliusPayPlugPlugin.php}' + exclude: '../src/{ApiClient,DependencyInjection,Entity,Exception,Model,Repository,PayPlugSyliusPayPlugPlugin.php}' PayPlug\SyliusPayPlugPlugin\Repository\PaymentRepositoryInterface: class: PayPlug\SyliusPayPlugPlugin\Repository\PaymentRepository @@ -33,3 +33,24 @@ services: payplug_sylius_payplug_plugin.action.notify: class: PayPlug\SyliusPayPlugPlugin\Action\NotifyAction + + payplug_sylius_payplug_plugin.command_provider.payplug: + class: Sylius\Bundle\PaymentBundle\CommandProvider\ActionsCommandProvider + arguments: + - !tagged_locator + tag: payplug_sylius_payplug_plugin.command_provider.payplug + index_by: 'action' + tags: + - name: sylius.payment_request.command_provider + gateway_factory: 'payplug' # The gateway factory name should match the one used in the payment method configuration + + #Redirection performer + payplug_sylius_payplug_plugin.provider.order_pay.http_response.payplug: + class: Sylius\Bundle\PaymentBundle\Provider\ActionsHttpResponseProvider + arguments: + - !tagged_locator + tag: payplug_sylius_payplug_plugin.http_response_provider.payplug + index_by: action + tags: + - name: sylius.payment_request.provider.http_response + gateway_factory: 'payplug' diff --git a/src/ApiClient/PayPlugApiClientFactoryInterface.php b/src/ApiClient/PayPlugApiClientFactoryInterface.php index 68ebc009..5ce7d366 100644 --- a/src/ApiClient/PayPlugApiClientFactoryInterface.php +++ b/src/ApiClient/PayPlugApiClientFactoryInterface.php @@ -4,7 +4,10 @@ namespace PayPlug\SyliusPayPlugPlugin\ApiClient; +use Sylius\Component\Core\Model\PaymentMethodInterface; + interface PayPlugApiClientFactoryInterface { public function create(string $factoryName, ?string $key = null): PayPlugApiClientInterface; + public function createForPaymentMethod(PaymentMethodInterface $paymentMethod): PayPlugApiClientInterface; } diff --git a/src/Command/CapturePaymentRequest.php b/src/Command/CapturePaymentRequest.php new file mode 100644 index 00000000..83fac007 --- /dev/null +++ b/src/Command/CapturePaymentRequest.php @@ -0,0 +1,15 @@ +paymentRequestProvider->provide($capturePaymentRequest); + $payment = $paymentRequest->getPayment(); + + $client = $this->apiClientFactory->createForPaymentMethod($paymentRequest->getPayment()->getMethod()); + $data = $this->paymentDataCreator->create($payment)->getArrayCopy(); + + $data['hosted_payment'] = [ + 'return_url' => $this->afterPayUrlProvider->getUrl($paymentRequest, UrlGeneratorInterface::ABSOLUTE_URL), + 'cancel_url' => $this->afterPayUrlProvider->getUrl($paymentRequest, UrlGeneratorInterface::ABSOLUTE_URL) + ]; + + $this->afterPayUrlProvider->getUrl($paymentRequest, UrlGeneratorInterface::ABSOLUTE_URL); + + $paymentRequest->setPayload($data); + $payplugPayment = $client->createPayment($data); + $arrayPayplugPayment = (array) $payplugPayment; + $payment->setDetails([ + ...$payment->getDetails(), + 'status' => PayPlugApiClientInterface::STATUS_CREATED, + 'payment_id' => $payplugPayment->__get('id'), + ['payplug_response' => $arrayPayplugPayment], + ]); + + $paymentRequest->setResponseData(array_merge($arrayPayplugPayment, [ + 'payment_id' => $payplugPayment->__get('id'), + 'redirect_url' => $payplugPayment->hosted_payment->payment_url + ])); + + $this->stateMachine->apply( + $paymentRequest, + PaymentRequestTransitions::GRAPH, + PaymentRequestTransitions::TRANSITION_COMPLETE + ); + } +} diff --git a/src/Command/Handler/StatusPaymentRequestHandler.php b/src/Command/Handler/StatusPaymentRequestHandler.php new file mode 100644 index 00000000..a648f7da --- /dev/null +++ b/src/Command/Handler/StatusPaymentRequestHandler.php @@ -0,0 +1,64 @@ +paymentRequestProvider->provide($statusPaymentRequest); + $payment = $paymentRequest->getPayment(); + + $client = $this->apiClientFactory->createForPaymentMethod($paymentRequest->getPayment()->getMethod()); + $payplugPayment = $client->retrieve($payment->getDetails()['payment_id'] ?? throw new \LogicException('No PayPlug payment ID found in payment details.')); + + $paymentRequest->setResponseData((array) $payplugPayment); + $details = new \ArrayObject($payment->getDetails()); + $this->paymentNotificationHandler->treat($payment, $payplugPayment, $details); + + $payment->setDetails($details->getArrayCopy()); + $this->updatePaymentState($payment); + + // Mark the PaymentRequest as completed + $this->stateMachine->apply( + $paymentRequest, + PaymentRequestTransitions::GRAPH, + PaymentRequestTransitions::TRANSITION_COMPLETE + ); + } + + private function updatePaymentState(PaymentInterface $payment): void + { + match ($payment->getDetails()['status'] ?? '') { + PayPlugApiClientInterface::STATUS_CANCELED => $this->stateMachine + ->apply($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_CANCEL), + PayPlugApiClientInterface::STATUS_AUTHORIZED => $this->stateMachine + ->apply($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_AUTHORIZE), + PayPlugApiClientInterface::STATUS_CAPTURED => $this->stateMachine + ->apply($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_COMPLETE), + PayPlugApiClientInterface::STATUS_ABORTED, PayPlugApiClientInterface::FAILED => $this->stateMachine + ->apply($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_FAIL), + default => throw new \LogicException(sprintf('Unknown payment status "%s".', $payment->getDetails()['status'] ?? '')), + }; + } +} diff --git a/src/Command/Provider/CapturePaymentRequestCommandProvider.php b/src/Command/Provider/CapturePaymentRequestCommandProvider.php new file mode 100644 index 00000000..5c6ea377 --- /dev/null +++ b/src/Command/Provider/CapturePaymentRequestCommandProvider.php @@ -0,0 +1,25 @@ + PaymentRequestInterface::ACTION_CAPTURE] +)] +final class CapturePaymentRequestCommandProvider implements PaymentRequestCommandProviderInterface +{ + public function supports(PaymentRequestInterface $paymentRequest): bool + { + return $paymentRequest->getAction() === PaymentRequestInterface::ACTION_CAPTURE; + } + + public function provide(PaymentRequestInterface $paymentRequest): object + { + return new CapturePaymentRequest($paymentRequest->getId()); + } +} diff --git a/src/Command/Provider/StatusPaymentRequestCommandProvider.php b/src/Command/Provider/StatusPaymentRequestCommandProvider.php new file mode 100644 index 00000000..43fd3354 --- /dev/null +++ b/src/Command/Provider/StatusPaymentRequestCommandProvider.php @@ -0,0 +1,25 @@ + PaymentRequestInterface::ACTION_STATUS] +)] +final class StatusPaymentRequestCommandProvider implements PaymentRequestCommandProviderInterface +{ + public function supports(PaymentRequestInterface $paymentRequest): bool + { + return $paymentRequest->getAction() === PaymentRequestInterface::ACTION_STATUS; + } + + public function provide(PaymentRequestInterface $paymentRequest): object + { + return new StatusPaymentRequest($paymentRequest->getId()); + } +} diff --git a/src/Command/StatusPaymentRequest.php b/src/Command/StatusPaymentRequest.php new file mode 100644 index 00000000..355ef81c --- /dev/null +++ b/src/Command/StatusPaymentRequest.php @@ -0,0 +1,15 @@ +getCustomer(); - $details = ArrayObject::ensureArrayObject($payment->getDetails()); + $details = new ArrayObject($payment->getDetails()); $details['amount'] = $payment->getAmount(); $details['currency'] = $payment->getCurrencyCode(); @@ -81,6 +80,7 @@ public function create( $this->addShippingInfo($shipping, $customer, $order, $deliveryType, $details); $paymentMethod = $payment->getMethod(); + $gatewayFactoryName = $paymentMethod->getGatewayConfig()?->getFactoryName(); if ( PayPlugGatewayFactory::FACTORY_NAME === $gatewayFactoryName && diff --git a/src/Gateway/Form/Type/AbstractGatewayConfigurationType.php b/src/Gateway/Form/Type/AbstractGatewayConfigurationType.php index bd38092c..1f8418e9 100644 --- a/src/Gateway/Form/Type/AbstractGatewayConfigurationType.php +++ b/src/Gateway/Form/Type/AbstractGatewayConfigurationType.php @@ -5,6 +5,7 @@ namespace PayPlug\SyliusPayPlugPlugin\Gateway\Form\Type; use Doctrine\Common\Collections\Collection; +use PayPlug\SyliusPayPlugPlugin\Gateway\PayPlugGatewayFactory; use PayPlug\SyliusPayPlugPlugin\Gateway\Validator\Constraints\IsCanSavePaymentMethod; use PayPlug\SyliusPayPlugPlugin\Gateway\Validator\Constraints\IsOneyEnabled; use PayPlug\SyliusPayPlugPlugin\Gateway\Validator\Constraints\IsPayPlugSecretKeyValid; @@ -27,14 +28,11 @@ class AbstractGatewayConfigurationType extends AbstractType public const VALIDATION_GROUPS = ['Default', 'sylius']; protected string $noTestKeyMessage = ''; - protected string $noAccessMessage = ''; - protected string $gatewayFactoryTitle = ''; - protected string $gatewayFactoryName = ''; - protected string $gatewayBaseCurrencyCode = 'EUR'; + protected string $gatewayBaseCurrencyCode = PayPlugGatewayFactory::BASE_CURRENCY_CODE; public function __construct( protected TranslatorInterface $translator, diff --git a/src/Gateway/Form/Type/PayPlugGatewayConfigurationType.php b/src/Gateway/Form/Type/PayPlugGatewayConfigurationType.php index 4f1f6c5d..9c315417 100644 --- a/src/Gateway/Form/Type/PayPlugGatewayConfigurationType.php +++ b/src/Gateway/Form/Type/PayPlugGatewayConfigurationType.php @@ -5,7 +5,15 @@ namespace PayPlug\SyliusPayPlugPlugin\Gateway\Form\Type; use PayPlug\SyliusPayPlugPlugin\Gateway\PayPlugGatewayFactory; +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; +#[AutoconfigureTag( + 'sylius.gateway_configuration_type', + [ + 'type' => 'sylius_payment', + 'label' => 'payplug' + ] +)] final class PayPlugGatewayConfigurationType extends AbstractGatewayConfigurationType { protected string $gatewayFactoryTitle = PayPlugGatewayFactory::FACTORY_TITLE; diff --git a/src/OrderPay/Provider/CaptureHttpResponseProvider.php b/src/OrderPay/Provider/CaptureHttpResponseProvider.php new file mode 100644 index 00000000..079787dd --- /dev/null +++ b/src/OrderPay/Provider/CaptureHttpResponseProvider.php @@ -0,0 +1,32 @@ + PaymentRequestInterface::ACTION_CAPTURE] +)] +class CaptureHttpResponseProvider implements HttpResponseProviderInterface +{ + public function supports(RequestConfiguration $requestConfiguration, PaymentRequestInterface $paymentRequest,): bool + { + return $paymentRequest->getAction() === PaymentRequestInterface::ACTION_CAPTURE && + ($paymentRequest->getResponseData()['redirect_url'] ?? null) !== null; + } + + public function getResponse( + RequestConfiguration $requestConfiguration, + PaymentRequestInterface $paymentRequest, + ): Response { + // This is called after the capture payment request has been handled + $data = $paymentRequest->getResponseData(); + return new RedirectResponse($data['redirect_url']); + } +} From d362e253964c56462f9d5bd6318d0ad3f59cbcf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jib=C3=A9=20Barth?= Date: Wed, 28 May 2025 10:34:50 +0200 Subject: [PATCH 02/11] PPSYL-138 - Autoconfigure gateway_configuration_type --- config/services/gateway.xml | 43 ------------------- ...mericanExpressGatewayConfigurationType.php | 10 ++++- .../Type/ApplePayGatewayConfigurationType.php | 10 ++++- .../BancontactGatewayConfigurationType.php | 10 ++++- .../Type/OneyGatewayConfigurationType.php | 8 ++++ .../Type/PayPlugGatewayConfigurationType.php | 4 +- 6 files changed, 37 insertions(+), 48 deletions(-) diff --git a/config/services/gateway.xml b/config/services/gateway.xml index f4e1b187..ca844892 100644 --- a/config/services/gateway.xml +++ b/config/services/gateway.xml @@ -12,17 +12,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Gateway/Form/Type/AmericanExpressGatewayConfigurationType.php b/src/Gateway/Form/Type/AmericanExpressGatewayConfigurationType.php index 28b31bc7..2fcbc2d8 100644 --- a/src/Gateway/Form/Type/AmericanExpressGatewayConfigurationType.php +++ b/src/Gateway/Form/Type/AmericanExpressGatewayConfigurationType.php @@ -5,7 +5,15 @@ namespace PayPlug\SyliusPayPlugPlugin\Gateway\Form\Type; use PayPlug\SyliusPayPlugPlugin\Gateway\AmericanExpressGatewayFactory; - +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; + +#[AutoconfigureTag( + 'sylius.gateway_configuration_type', + [ + 'type' => 'payplug_american_express', + 'label' => 'payplug_sylius_payplug_plugin.ui.american_express_gateway_label' + ] +)] final class AmericanExpressGatewayConfigurationType extends AbstractGatewayConfigurationType { protected string $noTestKeyMessage = 'payplug_sylius_payplug_plugin.american_express.can_not_save_method_with_test_key'; diff --git a/src/Gateway/Form/Type/ApplePayGatewayConfigurationType.php b/src/Gateway/Form/Type/ApplePayGatewayConfigurationType.php index b2af26db..db16493d 100644 --- a/src/Gateway/Form/Type/ApplePayGatewayConfigurationType.php +++ b/src/Gateway/Form/Type/ApplePayGatewayConfigurationType.php @@ -5,7 +5,15 @@ namespace PayPlug\SyliusPayPlugPlugin\Gateway\Form\Type; use PayPlug\SyliusPayPlugPlugin\Gateway\ApplePayGatewayFactory; - +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; + +#[AutoconfigureTag( + 'sylius.gateway_configuration_type', + [ + 'type' => 'payplug_apple_pay', + 'label' => 'payplug_sylius_payplug_plugin.ui.apple_pay_gateway_label' + ] +)] final class ApplePayGatewayConfigurationType extends AbstractGatewayConfigurationType { protected string $noTestKeyMessage = 'payplug_sylius_payplug_plugin.apple_pay.can_not_save_method_with_test_key'; diff --git a/src/Gateway/Form/Type/BancontactGatewayConfigurationType.php b/src/Gateway/Form/Type/BancontactGatewayConfigurationType.php index c3c70862..5804d4a0 100644 --- a/src/Gateway/Form/Type/BancontactGatewayConfigurationType.php +++ b/src/Gateway/Form/Type/BancontactGatewayConfigurationType.php @@ -5,7 +5,15 @@ namespace PayPlug\SyliusPayPlugPlugin\Gateway\Form\Type; use PayPlug\SyliusPayPlugPlugin\Gateway\BancontactGatewayFactory; - +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; + +#[AutoconfigureTag( + 'sylius.gateway_configuration_type', + [ + 'type' => 'payplug_bancontact', + 'label' => 'payplug_sylius_payplug_plugin.ui.bancontact_gateway_label' + ] +)] final class BancontactGatewayConfigurationType extends AbstractGatewayConfigurationType { protected string $noTestKeyMessage = 'payplug_sylius_payplug_plugin.bancontact.can_not_save_method_with_test_key'; diff --git a/src/Gateway/Form/Type/OneyGatewayConfigurationType.php b/src/Gateway/Form/Type/OneyGatewayConfigurationType.php index c024f96a..8b81e429 100644 --- a/src/Gateway/Form/Type/OneyGatewayConfigurationType.php +++ b/src/Gateway/Form/Type/OneyGatewayConfigurationType.php @@ -5,7 +5,15 @@ namespace PayPlug\SyliusPayPlugPlugin\Gateway\Form\Type; use PayPlug\SyliusPayPlugPlugin\Gateway\OneyGatewayFactory; +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; +#[AutoconfigureTag( + 'sylius.gateway_configuration_type', + [ + 'type' => 'payplug_oney', + 'label' => 'payplug_sylius_payplug_plugin.ui.oney_gateway_label' + ] +)] final class OneyGatewayConfigurationType extends AbstractGatewayConfigurationType { protected string $gatewayFactoryTitle = OneyGatewayFactory::FACTORY_TITLE; diff --git a/src/Gateway/Form/Type/PayPlugGatewayConfigurationType.php b/src/Gateway/Form/Type/PayPlugGatewayConfigurationType.php index 9c315417..7459060d 100644 --- a/src/Gateway/Form/Type/PayPlugGatewayConfigurationType.php +++ b/src/Gateway/Form/Type/PayPlugGatewayConfigurationType.php @@ -10,8 +10,8 @@ #[AutoconfigureTag( 'sylius.gateway_configuration_type', [ - 'type' => 'sylius_payment', - 'label' => 'payplug' + 'type' => 'payplug', + 'label' => 'payplug_sylius_payplug_plugin.ui.payplug_gateway_label' ] )] final class PayPlugGatewayConfigurationType extends AbstractGatewayConfigurationType From 9f2272aba84e02bcf63e3a2fd9967af5bb780445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jib=C3=A9=20Barth?= Date: Wed, 28 May 2025 10:42:15 +0200 Subject: [PATCH 03/11] PPSYL-138 - Disable payum gateway for gateway payplug --- config/services/gateway.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/config/services/gateway.xml b/config/services/gateway.xml index ca844892..1dc4b421 100644 --- a/config/services/gateway.xml +++ b/config/services/gateway.xml @@ -5,13 +5,6 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - PayPlug\SyliusPayPlugPlugin\Gateway\PayPlugGatewayFactory - - Date: Sun, 1 Jun 2025 18:30:27 +0200 Subject: [PATCH 04/11] PPSYL-138 - Fix call to paymentDataCreator --- src/Action/ConvertPaymentAction.php | 2 +- src/Controller/IntegratedPaymentController.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Action/ConvertPaymentAction.php b/src/Action/ConvertPaymentAction.php index 04c782fd..4b00ebe3 100644 --- a/src/Action/ConvertPaymentAction.php +++ b/src/Action/ConvertPaymentAction.php @@ -62,7 +62,7 @@ public function execute($request): void /** @var PaymentInterface $payment */ $payment = $request->getSource(); - $details = $this->paymentDataCreator->create($payment, $this->payPlugApiClient->getGatewayFactoryName()); + $details = $this->paymentDataCreator->create($payment); $request->setResult((array) $details); } diff --git a/src/Controller/IntegratedPaymentController.php b/src/Controller/IntegratedPaymentController.php index e4dbf6f0..a5b53925 100644 --- a/src/Controller/IntegratedPaymentController.php +++ b/src/Controller/IntegratedPaymentController.php @@ -79,7 +79,7 @@ public function initPaymentAction(Request $request, int $paymentMethodId): Respo throw new BadRequestHttpException('Unsupported payment method of Integrated Payment'); } - $paymentData = $this->paymentDataCreator->create($payment, $factoryName); + $paymentData = $this->paymentDataCreator->create($payment); // Mandatory $paymentData['integration'] = PayPlugApiClientInterface::INTEGRATED_PAYMENT_INTEGRATION; $this->logger->debug('Payplug Payment data for creation', $paymentData->getArrayCopy()); From ec468e4450f735c16b3bbec2d42cf76e8e197b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jib=C3=A9=20Barth?= Date: Sun, 1 Jun 2025 18:31:09 +0200 Subject: [PATCH 05/11] PPSYL-138 - Add command provider for others gateway --- config/services.yaml | 89 ++++++++++++++++++- .../CapturePaymentRequestCommandProvider.php | 16 ++++ .../StatusPaymentRequestCommandProvider.php | 16 ++++ .../Provider/CaptureHttpResponseProvider.php | 16 ++++ 4 files changed, 133 insertions(+), 4 deletions(-) diff --git a/config/services.yaml b/config/services.yaml index 663d2046..3a290b5f 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -34,6 +34,7 @@ services: payplug_sylius_payplug_plugin.action.notify: class: PayPlug\SyliusPayPlugPlugin\Action\NotifyAction + ## Default Payplug Gateway ## payplug_sylius_payplug_plugin.command_provider.payplug: class: Sylius\Bundle\PaymentBundle\CommandProvider\ActionsCommandProvider arguments: @@ -42,9 +43,7 @@ services: index_by: 'action' tags: - name: sylius.payment_request.command_provider - gateway_factory: 'payplug' # The gateway factory name should match the one used in the payment method configuration - - #Redirection performer + gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\PayPlugGatewayFactory::FACTORY_NAME payplug_sylius_payplug_plugin.provider.order_pay.http_response.payplug: class: Sylius\Bundle\PaymentBundle\Provider\ActionsHttpResponseProvider arguments: @@ -53,4 +52,86 @@ services: index_by: action tags: - name: sylius.payment_request.provider.http_response - gateway_factory: 'payplug' + gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\PayPlugGatewayFactory::FACTORY_NAME + + ## Oney Payplug Gateway ## + payplug_sylius_payplug_plugin.command_provider.payplug_oney: + class: Sylius\Bundle\PaymentBundle\CommandProvider\ActionsCommandProvider + arguments: + - !tagged_locator + tag: payplug_sylius_payplug_plugin.command_provider.payplug_oney + index_by: 'action' + tags: + - name: sylius.payment_request.command_provider + gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\OneyGatewayFactory::FACTORY_NAME + payplug_sylius_payplug_plugin.provider.order_pay.http_response.payplug_oney: + class: Sylius\Bundle\PaymentBundle\Provider\ActionsHttpResponseProvider + arguments: + - !tagged_locator + tag: payplug_sylius_payplug_plugin.http_response_provider.payplug_oney + index_by: action + tags: + - name: sylius.payment_request.provider.http_response + gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\OneyGatewayFactory::FACTORY_NAME + + + ## Bancontact Payplug Gateway ## + payplug_sylius_payplug_plugin.command_provider.payplug_bancontact: + class: Sylius\Bundle\PaymentBundle\CommandProvider\ActionsCommandProvider + arguments: + - !tagged_locator + tag: payplug_sylius_payplug_plugin.command_provider.payplug_bancontact + index_by: 'action' + tags: + - name: sylius.payment_request.command_provider + gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\BancontactGatewayFactory::FACTORY_NAME + payplug_sylius_payplug_plugin.provider.order_pay.http_response.payplug_bancontact: + class: Sylius\Bundle\PaymentBundle\Provider\ActionsHttpResponseProvider + arguments: + - !tagged_locator + tag: payplug_sylius_payplug_plugin.http_response_provider.payplug_bancontact + index_by: action + tags: + - name: sylius.payment_request.provider.http_response + gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\BancontactGatewayFactory::FACTORY_NAME + + + ## Amex Payplug Gateway ## + payplug_sylius_payplug_plugin.command_provider.payplug_american_express: + class: Sylius\Bundle\PaymentBundle\CommandProvider\ActionsCommandProvider + arguments: + - !tagged_locator + tag: payplug_sylius_payplug_plugin.command_provider.payplug_american_express + index_by: 'action' + tags: + - name: sylius.payment_request.command_provider + gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\AmericanExpressGatewayFactory::FACTORY_NAME + payplug_sylius_payplug_plugin.provider.order_pay.http_response.payplug_american_express: + class: Sylius\Bundle\PaymentBundle\Provider\ActionsHttpResponseProvider + arguments: + - !tagged_locator + tag: payplug_sylius_payplug_plugin.http_response_provider.payplug_american_express + index_by: action + tags: + - name: sylius.payment_request.provider.http_response + gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\AmericanExpressGatewayFactory::FACTORY_NAME + + ## Apple Pay Payplug Gateway ## + payplug_sylius_payplug_plugin.command_provider.payplug_apple_pay: + class: Sylius\Bundle\PaymentBundle\CommandProvider\ActionsCommandProvider + arguments: + - !tagged_locator + tag: payplug_sylius_payplug_plugin.command_provider.payplug_apple_pay + index_by: 'action' + tags: + - name: sylius.payment_request.command_provider + gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\ApplePayGatewayFactory::FACTORY_NAME + payplug_sylius_payplug_plugin.provider.order_pay.http_response.payplug_apple_pay: + class: Sylius\Bundle\PaymentBundle\Provider\ActionsHttpResponseProvider + arguments: + - !tagged_locator + tag: payplug_sylius_payplug_plugin.http_response_provider.payplug_apple_pay + index_by: action + tags: + - name: sylius.payment_request.provider.http_response + gateway_factory: !php/const PayPlug\SyliusPayPlugPlugin\Gateway\ApplePayGatewayFactory::FACTORY_NAME diff --git a/src/Command/Provider/CapturePaymentRequestCommandProvider.php b/src/Command/Provider/CapturePaymentRequestCommandProvider.php index 5c6ea377..5b60f397 100644 --- a/src/Command/Provider/CapturePaymentRequestCommandProvider.php +++ b/src/Command/Provider/CapturePaymentRequestCommandProvider.php @@ -11,6 +11,22 @@ 'payplug_sylius_payplug_plugin.command_provider.payplug', ['action' => PaymentRequestInterface::ACTION_CAPTURE] )] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.command_provider.payplug_oney', + ['action' => PaymentRequestInterface::ACTION_CAPTURE] +)] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.command_provider.payplug_bancontact', + ['action' => PaymentRequestInterface::ACTION_CAPTURE] +)] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.command_provider.payplug_american_express', + ['action' => PaymentRequestInterface::ACTION_CAPTURE] +)] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.command_provider.payplug_apple_pay', + ['action' => PaymentRequestInterface::ACTION_CAPTURE] +)] final class CapturePaymentRequestCommandProvider implements PaymentRequestCommandProviderInterface { public function supports(PaymentRequestInterface $paymentRequest): bool diff --git a/src/Command/Provider/StatusPaymentRequestCommandProvider.php b/src/Command/Provider/StatusPaymentRequestCommandProvider.php index 43fd3354..573dd383 100644 --- a/src/Command/Provider/StatusPaymentRequestCommandProvider.php +++ b/src/Command/Provider/StatusPaymentRequestCommandProvider.php @@ -11,6 +11,22 @@ 'payplug_sylius_payplug_plugin.command_provider.payplug', ['action' => PaymentRequestInterface::ACTION_STATUS] )] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.command_provider.payplug_oney', + ['action' => PaymentRequestInterface::ACTION_STATUS] +)] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.command_provider.payplug_bancontact', + ['action' => PaymentRequestInterface::ACTION_STATUS] +)] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.command_provider.payplug_american_express', + ['action' => PaymentRequestInterface::ACTION_STATUS] +)] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.command_provider.payplug_apple_pay', + ['action' => PaymentRequestInterface::ACTION_STATUS] +)] final class StatusPaymentRequestCommandProvider implements PaymentRequestCommandProviderInterface { public function supports(PaymentRequestInterface $paymentRequest): bool diff --git a/src/OrderPay/Provider/CaptureHttpResponseProvider.php b/src/OrderPay/Provider/CaptureHttpResponseProvider.php index 079787dd..50eba2f8 100644 --- a/src/OrderPay/Provider/CaptureHttpResponseProvider.php +++ b/src/OrderPay/Provider/CaptureHttpResponseProvider.php @@ -13,6 +13,22 @@ 'payplug_sylius_payplug_plugin.http_response_provider.payplug', ['action' => PaymentRequestInterface::ACTION_CAPTURE] )] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.http_response_provider.payplug_oney', + ['action' => PaymentRequestInterface::ACTION_CAPTURE] +)] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.http_response_provider.payplug_bancontact', + ['action' => PaymentRequestInterface::ACTION_CAPTURE] +)] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.http_response_provider.payplug_apple_pay', + ['action' => PaymentRequestInterface::ACTION_CAPTURE] +)] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.http_response_provider.payplug_american_express', + ['action' => PaymentRequestInterface::ACTION_CAPTURE] +)] class CaptureHttpResponseProvider implements HttpResponseProviderInterface { public function supports(RequestConfiguration $requestConfiguration, PaymentRequestInterface $paymentRequest,): bool From 4a9eea9c90e1a093cf83a87090e56d0d5737bf29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jib=C3=A9=20Barth?= Date: Sun, 1 Jun 2025 18:35:49 +0200 Subject: [PATCH 06/11] PPSYL-138 - Allow cancel url --- .../Handler/CapturePaymentRequestHandler.php | 7 +++-- .../Handler/StatusPaymentRequestHandler.php | 30 +++++++++++++++++-- .../StatusPaymentRequestCommandProvider.php | 13 +++++++- src/Command/StatusPaymentRequest.php | 7 ++++- 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/Command/Handler/CapturePaymentRequestHandler.php b/src/Command/Handler/CapturePaymentRequestHandler.php index 6eef752b..0ceddb5c 100644 --- a/src/Command/Handler/CapturePaymentRequestHandler.php +++ b/src/Command/Handler/CapturePaymentRequestHandler.php @@ -37,9 +37,10 @@ public function __invoke(CapturePaymentRequest $capturePaymentRequest): void $client = $this->apiClientFactory->createForPaymentMethod($paymentRequest->getPayment()->getMethod()); $data = $this->paymentDataCreator->create($payment)->getArrayCopy(); + $returnUrl = $this->afterPayUrlProvider->getUrl($paymentRequest, UrlGeneratorInterface::ABSOLUTE_URL); $data['hosted_payment'] = [ - 'return_url' => $this->afterPayUrlProvider->getUrl($paymentRequest, UrlGeneratorInterface::ABSOLUTE_URL), - 'cancel_url' => $this->afterPayUrlProvider->getUrl($paymentRequest, UrlGeneratorInterface::ABSOLUTE_URL) + 'return_url' => $returnUrl, + 'cancel_url' => $returnUrl . '?&' . http_build_query(['status' => PayPlugApiClientInterface::STATUS_CANCELED]), ]; $this->afterPayUrlProvider->getUrl($paymentRequest, UrlGeneratorInterface::ABSOLUTE_URL); @@ -51,7 +52,7 @@ public function __invoke(CapturePaymentRequest $capturePaymentRequest): void ...$payment->getDetails(), 'status' => PayPlugApiClientInterface::STATUS_CREATED, 'payment_id' => $payplugPayment->__get('id'), - ['payplug_response' => $arrayPayplugPayment], + 'payplug_response' => $arrayPayplugPayment, ]); $paymentRequest->setResponseData(array_merge($arrayPayplugPayment, [ diff --git a/src/Command/Handler/StatusPaymentRequestHandler.php b/src/Command/Handler/StatusPaymentRequestHandler.php index a648f7da..4a812457 100644 --- a/src/Command/Handler/StatusPaymentRequestHandler.php +++ b/src/Command/Handler/StatusPaymentRequestHandler.php @@ -11,6 +11,7 @@ use Sylius\Abstraction\StateMachine\StateMachineInterface; use Sylius\Bundle\PaymentBundle\Provider\PaymentRequestProviderInterface; use Sylius\Component\Payment\Model\PaymentInterface; +use Sylius\Component\Payment\Model\PaymentRequestInterface; use Sylius\Component\Payment\PaymentRequestTransitions; use Sylius\Component\Payment\PaymentTransitions; use Symfony\Component\Messenger\Attribute\AsMessageHandler; @@ -28,7 +29,11 @@ public function __invoke(StatusPaymentRequest $statusPaymentRequest): void { $paymentRequest = $this->paymentRequestProvider->provide($statusPaymentRequest); $payment = $paymentRequest->getPayment(); - + if ('' !== $statusPaymentRequest->getForcedStatus()) { + $this->handleForcedStatus($statusPaymentRequest, $paymentRequest); + return; + } + // We don't have a forced status, so we retrieve the payment status from PayPlug $client = $this->apiClientFactory->createForPaymentMethod($paymentRequest->getPayment()->getMethod()); $payplugPayment = $client->retrieve($payment->getDetails()['payment_id'] ?? throw new \LogicException('No PayPlug payment ID found in payment details.')); @@ -47,16 +52,35 @@ public function __invoke(StatusPaymentRequest $statusPaymentRequest): void ); } + private function handleForcedStatus(StatusPaymentRequest $statusPaymentRequest, PaymentRequestInterface $paymentRequest): void + { + $payment = $paymentRequest->getPayment(); + + $payment->setDetails([ + ...$payment->getDetails(), + 'status' => $statusPaymentRequest->getForcedStatus(), + ]); + + $this->updatePaymentState($payment); + + // Mark the PaymentRequest as completed + $this->stateMachine->apply( + $paymentRequest, + PaymentRequestTransitions::GRAPH, + PaymentRequestTransitions::TRANSITION_COMPLETE + ); + } + private function updatePaymentState(PaymentInterface $payment): void { match ($payment->getDetails()['status'] ?? '') { - PayPlugApiClientInterface::STATUS_CANCELED => $this->stateMachine + PayPlugApiClientInterface::STATUS_ABORTED, PayPlugApiClientInterface::STATUS_CANCELED => $this->stateMachine ->apply($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_CANCEL), PayPlugApiClientInterface::STATUS_AUTHORIZED => $this->stateMachine ->apply($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_AUTHORIZE), PayPlugApiClientInterface::STATUS_CAPTURED => $this->stateMachine ->apply($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_COMPLETE), - PayPlugApiClientInterface::STATUS_ABORTED, PayPlugApiClientInterface::FAILED => $this->stateMachine + PayPlugApiClientInterface::FAILED => $this->stateMachine ->apply($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_FAIL), default => throw new \LogicException(sprintf('Unknown payment status "%s".', $payment->getDetails()['status'] ?? '')), }; diff --git a/src/Command/Provider/StatusPaymentRequestCommandProvider.php b/src/Command/Provider/StatusPaymentRequestCommandProvider.php index 573dd383..92518bd7 100644 --- a/src/Command/Provider/StatusPaymentRequestCommandProvider.php +++ b/src/Command/Provider/StatusPaymentRequestCommandProvider.php @@ -6,6 +6,7 @@ use Sylius\Bundle\PaymentBundle\CommandProvider\PaymentRequestCommandProviderInterface; use Sylius\Component\Payment\Model\PaymentRequestInterface; use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; +use Symfony\Component\HttpFoundation\RequestStack; #[AutoconfigureTag( 'payplug_sylius_payplug_plugin.command_provider.payplug', @@ -29,6 +30,10 @@ )] final class StatusPaymentRequestCommandProvider implements PaymentRequestCommandProviderInterface { + public function __construct(private RequestStack $requestStack) + { + } + public function supports(PaymentRequestInterface $paymentRequest): bool { return $paymentRequest->getAction() === PaymentRequestInterface::ACTION_STATUS; @@ -36,6 +41,12 @@ public function supports(PaymentRequestInterface $paymentRequest): bool public function provide(PaymentRequestInterface $paymentRequest): object { - return new StatusPaymentRequest($paymentRequest->getId()); + $request = $this->requestStack->getCurrentRequest(); + if (null === $request) { + return new StatusPaymentRequest($paymentRequest->getId()); + } + $forcedStatus = $request->query->getString('status'); + + return new StatusPaymentRequest($paymentRequest->getId(), $forcedStatus); } } diff --git a/src/Command/StatusPaymentRequest.php b/src/Command/StatusPaymentRequest.php index 355ef81c..2d64545a 100644 --- a/src/Command/StatusPaymentRequest.php +++ b/src/Command/StatusPaymentRequest.php @@ -11,5 +11,10 @@ class StatusPaymentRequest implements PaymentRequestHashAwareInterface { use PaymentRequestHashAwareTrait; - public function __construct(protected ?string $hash) {} + public function __construct(protected ?string $hash, private string $forcedStatus = '') {} + + public function getForcedStatus() : string + { + return $this->forcedStatus; + } } From f1a0ac204da22962a2f3debd7e1e3825db5a0a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jib=C3=A9=20Barth?= Date: Sun, 1 Jun 2025 18:36:30 +0200 Subject: [PATCH 07/11] PPSYL-138 - IntegratedPayment - do not dispatch CapturePaymentRequest that could redirect to payplug --- .../CapturePaymentRequestCommandProvider.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/Command/Provider/CapturePaymentRequestCommandProvider.php b/src/Command/Provider/CapturePaymentRequestCommandProvider.php index 5b60f397..54033b16 100644 --- a/src/Command/Provider/CapturePaymentRequestCommandProvider.php +++ b/src/Command/Provider/CapturePaymentRequestCommandProvider.php @@ -2,7 +2,9 @@ namespace PayPlug\SyliusPayPlugPlugin\Command\Provider; +use PayPlug\SyliusPayPlugPlugin\ApiClient\PayPlugApiClientInterface; use PayPlug\SyliusPayPlugPlugin\Command\CapturePaymentRequest; +use Sylius\Bundle\PaymentBundle\Command\Offline\CapturePaymentRequest as OfflineCapturePaymentRequest; use Sylius\Bundle\PaymentBundle\CommandProvider\PaymentRequestCommandProviderInterface; use Sylius\Component\Payment\Model\PaymentRequestInterface; use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; @@ -36,6 +38,35 @@ public function supports(PaymentRequestInterface $paymentRequest): bool public function provide(PaymentRequestInterface $paymentRequest): object { + $payment = $paymentRequest->getPayment(); + $details = $payment->getDetails(); + + if ($this->isAlreadyCreated($paymentRequest)) { + // The payment has already been created, let's use the offline capture request to be redirected to the thank-you page + // TODO: use our own AlreadyPaidCapturePaymentRequest class + return new OfflineCapturePaymentRequest($paymentRequest->getId()); + } + return new CapturePaymentRequest($paymentRequest->getId()); } + + private function isAlreadyCreated(PaymentRequestInterface $paymentRequest): bool + { + $payment = $paymentRequest->getPayment(); + $details = $payment->getDetails(); + + if ( + isset($details['status'], $details['payment_id']) && + PayPlugApiClientInterface::STATUS_CREATED !== $details['status'] + ) { + return true; + } + + if ( + array_key_exists('status', $details) && + PayPlugApiClientInterface::STATUS_CAPTURED === $details['status'] + ) { + return true; + } + } } From 2df316bd4309fa8d9b2b15f57b94972988d1b882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jib=C3=A9=20Barth?= Date: Sun, 1 Jun 2025 19:17:56 +0200 Subject: [PATCH 08/11] Add priority to gateways --- .../Form/Type/AmericanExpressGatewayConfigurationType.php | 3 ++- src/Gateway/Form/Type/ApplePayGatewayConfigurationType.php | 3 ++- src/Gateway/Form/Type/BancontactGatewayConfigurationType.php | 3 ++- src/Gateway/Form/Type/OneyGatewayConfigurationType.php | 3 ++- src/Gateway/Form/Type/PayPlugGatewayConfigurationType.php | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Gateway/Form/Type/AmericanExpressGatewayConfigurationType.php b/src/Gateway/Form/Type/AmericanExpressGatewayConfigurationType.php index 2fcbc2d8..a78e7b24 100644 --- a/src/Gateway/Form/Type/AmericanExpressGatewayConfigurationType.php +++ b/src/Gateway/Form/Type/AmericanExpressGatewayConfigurationType.php @@ -11,7 +11,8 @@ 'sylius.gateway_configuration_type', [ 'type' => 'payplug_american_express', - 'label' => 'payplug_sylius_payplug_plugin.ui.american_express_gateway_label' + 'label' => 'payplug_sylius_payplug_plugin.ui.american_express_gateway_label', + 'priority' => 70, ] )] final class AmericanExpressGatewayConfigurationType extends AbstractGatewayConfigurationType diff --git a/src/Gateway/Form/Type/ApplePayGatewayConfigurationType.php b/src/Gateway/Form/Type/ApplePayGatewayConfigurationType.php index db16493d..cd9613c9 100644 --- a/src/Gateway/Form/Type/ApplePayGatewayConfigurationType.php +++ b/src/Gateway/Form/Type/ApplePayGatewayConfigurationType.php @@ -11,7 +11,8 @@ 'sylius.gateway_configuration_type', [ 'type' => 'payplug_apple_pay', - 'label' => 'payplug_sylius_payplug_plugin.ui.apple_pay_gateway_label' + 'label' => 'payplug_sylius_payplug_plugin.ui.apple_pay_gateway_label', + 'priority' => 70, ] )] final class ApplePayGatewayConfigurationType extends AbstractGatewayConfigurationType diff --git a/src/Gateway/Form/Type/BancontactGatewayConfigurationType.php b/src/Gateway/Form/Type/BancontactGatewayConfigurationType.php index 5804d4a0..10b9d029 100644 --- a/src/Gateway/Form/Type/BancontactGatewayConfigurationType.php +++ b/src/Gateway/Form/Type/BancontactGatewayConfigurationType.php @@ -11,7 +11,8 @@ 'sylius.gateway_configuration_type', [ 'type' => 'payplug_bancontact', - 'label' => 'payplug_sylius_payplug_plugin.ui.bancontact_gateway_label' + 'label' => 'payplug_sylius_payplug_plugin.ui.bancontact_gateway_label', + 'priority' => 80, ] )] final class BancontactGatewayConfigurationType extends AbstractGatewayConfigurationType diff --git a/src/Gateway/Form/Type/OneyGatewayConfigurationType.php b/src/Gateway/Form/Type/OneyGatewayConfigurationType.php index 8b81e429..1d7b5624 100644 --- a/src/Gateway/Form/Type/OneyGatewayConfigurationType.php +++ b/src/Gateway/Form/Type/OneyGatewayConfigurationType.php @@ -11,7 +11,8 @@ 'sylius.gateway_configuration_type', [ 'type' => 'payplug_oney', - 'label' => 'payplug_sylius_payplug_plugin.ui.oney_gateway_label' + 'label' => 'payplug_sylius_payplug_plugin.ui.oney_gateway_label', + 'priority' => 90, ] )] final class OneyGatewayConfigurationType extends AbstractGatewayConfigurationType diff --git a/src/Gateway/Form/Type/PayPlugGatewayConfigurationType.php b/src/Gateway/Form/Type/PayPlugGatewayConfigurationType.php index 7459060d..858b5e72 100644 --- a/src/Gateway/Form/Type/PayPlugGatewayConfigurationType.php +++ b/src/Gateway/Form/Type/PayPlugGatewayConfigurationType.php @@ -11,7 +11,8 @@ 'sylius.gateway_configuration_type', [ 'type' => 'payplug', - 'label' => 'payplug_sylius_payplug_plugin.ui.payplug_gateway_label' + 'label' => 'payplug_sylius_payplug_plugin.ui.payplug_gateway_label', + 'priority' => 100, ] )] final class PayPlugGatewayConfigurationType extends AbstractGatewayConfigurationType From 759bd9e06893dcbce69c79a4502fdb012b8a36b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jib=C3=A9=20Barth?= Date: Sun, 1 Jun 2025 19:20:16 +0200 Subject: [PATCH 09/11] fixup! PPSYL-138 - IntegratedPayment - do not dispatch CapturePaymentRequest that could redirect to payplug --- src/Command/Provider/CapturePaymentRequestCommandProvider.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Command/Provider/CapturePaymentRequestCommandProvider.php b/src/Command/Provider/CapturePaymentRequestCommandProvider.php index 54033b16..a702f382 100644 --- a/src/Command/Provider/CapturePaymentRequestCommandProvider.php +++ b/src/Command/Provider/CapturePaymentRequestCommandProvider.php @@ -68,5 +68,7 @@ private function isAlreadyCreated(PaymentRequestInterface $paymentRequest): bool ) { return true; } + + return false; } } From df00d8dc4122f11556df1f796817f83420ab32ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jib=C3=A9=20Barth?= Date: Tue, 15 Jul 2025 16:32:34 +0200 Subject: [PATCH 10/11] PPSYL-138 - Handle Notification --- src/Command/AbstractPayplugPaymentRequest.php | 15 ++++ src/Command/CapturePaymentRequest.php | 8 +- .../Handler/CapturePaymentRequestHandler.php | 4 + .../Handler/NotifyPaymentRequestHandler.php | 86 +++++++++++++++++++ .../Handler/StatusPaymentRequestHandler.php | 1 + src/Command/NotifyPaymentRequest.php | 9 ++ .../NotifyPaymentRequestCommandProvider.php | 41 +++++++++ src/Command/StatusPaymentRequest.php | 6 +- .../Provider/NotifyPaymentProvider.php | 69 +++++++++++++++ 9 files changed, 230 insertions(+), 9 deletions(-) create mode 100644 src/Command/AbstractPayplugPaymentRequest.php create mode 100644 src/Command/Handler/NotifyPaymentRequestHandler.php create mode 100644 src/Command/NotifyPaymentRequest.php create mode 100644 src/Command/Provider/NotifyPaymentRequestCommandProvider.php create mode 100644 src/OrderPay/Provider/NotifyPaymentProvider.php diff --git a/src/Command/AbstractPayplugPaymentRequest.php b/src/Command/AbstractPayplugPaymentRequest.php new file mode 100644 index 00000000..fc600a04 --- /dev/null +++ b/src/Command/AbstractPayplugPaymentRequest.php @@ -0,0 +1,15 @@ +afterPayUrlProvider->getUrl($paymentRequest, UrlGeneratorInterface::ABSOLUTE_URL); + $notificationUrl = $this->urlGenerator->generate('sylius_payment_method_notify', ['code' => $payment->getMethod()?->getCode()], UrlGeneratorInterface::ABSOLUTE_URL); + $details['notification_url'] = $notificationUrl; + $paymentRequest->setPayload($data); $payplugPayment = $client->createPayment($data); $arrayPayplugPayment = (array) $payplugPayment; diff --git a/src/Command/Handler/NotifyPaymentRequestHandler.php b/src/Command/Handler/NotifyPaymentRequestHandler.php new file mode 100644 index 00000000..a700a850 --- /dev/null +++ b/src/Command/Handler/NotifyPaymentRequestHandler.php @@ -0,0 +1,86 @@ +paymentRequestProvider->provide($notifyPaymentRequest); + $payment = $paymentRequest->getPayment(); + if ($payment->getState() !== PaymentInterface::STATE_COMPLETED) { + // If the payment is already completed, we do not need to notify again + $this->stateMachine->apply( + $paymentRequest, + PaymentRequestTransitions::GRAPH, + PaymentRequestTransitions::TRANSITION_COMPLETE + ); + + return; + } + try { + + // Payload contains what's send by payplug, no need to retrieve it from PayPlug + $payplugPayment = Payment::fromAttributes(json_decode($paymentRequest->getPayload()['http_request']['content'] ?? '{}', true)); + $details = new \ArrayObject($payment->getDetails()); + $this->paymentNotificationHandler->treat($payment, $payplugPayment, $details); + + $payment->setDetails($details->getArrayCopy()); + $this->updatePaymentState($payment); + throw new \LogicException(sprintf('Unknown payment status "%s".', $payment->getDetails()['status'] ?? '')); + + $this->stateMachine->apply( + $paymentRequest, + PaymentRequestTransitions::GRAPH, + PaymentRequestTransitions::TRANSITION_COMPLETE + ); + + } catch (\Throwable $e) { + $paymentRequest->setResponseData([ + 'error' => $e->getMessage(), + ]); + $this->stateMachine->apply( + $paymentRequest, + PaymentRequestTransitions::GRAPH, + PaymentRequestTransitions::TRANSITION_FAIL + ); + } + } + + private function updatePaymentState(PaymentInterface $payment): void + { + match ($payment->getDetails()['status'] ?? '') { + PayPlugApiClientInterface::STATUS_ABORTED, PayPlugApiClientInterface::STATUS_CANCELED => $this->stateMachine + ->apply($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_CANCEL), + PayPlugApiClientInterface::STATUS_AUTHORIZED => $this->stateMachine + ->apply($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_AUTHORIZE), + PayPlugApiClientInterface::STATUS_CAPTURED => $this->stateMachine + ->apply($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_COMPLETE), + PayPlugApiClientInterface::FAILED => $this->stateMachine + ->apply($payment, PaymentTransitions::GRAPH, PaymentTransitions::TRANSITION_FAIL), + default => throw new \LogicException(sprintf('Unknown payment status "%s".', $payment->getDetails()['status'] ?? '')), + }; + } +} diff --git a/src/Command/Handler/StatusPaymentRequestHandler.php b/src/Command/Handler/StatusPaymentRequestHandler.php index 4a812457..3ddc21b6 100644 --- a/src/Command/Handler/StatusPaymentRequestHandler.php +++ b/src/Command/Handler/StatusPaymentRequestHandler.php @@ -25,6 +25,7 @@ public function __construct( private PayPlugApiClientFactoryInterface $apiClientFactory, private PaymentNotificationHandler $paymentNotificationHandler, ) {} + public function __invoke(StatusPaymentRequest $statusPaymentRequest): void { $paymentRequest = $this->paymentRequestProvider->provide($statusPaymentRequest); diff --git a/src/Command/NotifyPaymentRequest.php b/src/Command/NotifyPaymentRequest.php new file mode 100644 index 00000000..50f7feff --- /dev/null +++ b/src/Command/NotifyPaymentRequest.php @@ -0,0 +1,9 @@ + PaymentRequestInterface::ACTION_NOTIFY] +)] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.command_provider.payplug_oney', + ['action' => PaymentRequestInterface::ACTION_NOTIFY] +)] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.command_provider.payplug_bancontact', + ['action' => PaymentRequestInterface::ACTION_NOTIFY] +)] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.command_provider.payplug_american_express', + ['action' => PaymentRequestInterface::ACTION_NOTIFY] +)] +#[AutoconfigureTag( + 'payplug_sylius_payplug_plugin.command_provider.payplug_apple_pay', + ['action' => PaymentRequestInterface::ACTION_NOTIFY] +)] +final class NotifyPaymentRequestCommandProvider implements PaymentRequestCommandProviderInterface +{ + public function supports(PaymentRequestInterface $paymentRequest): bool + { + return $paymentRequest->getAction() === PaymentRequestInterface::ACTION_NOTIFY; + } + + public function provide(PaymentRequestInterface $paymentRequest): object + { + return new NotifyPaymentRequest($paymentRequest->getId()); + } +} diff --git a/src/Command/StatusPaymentRequest.php b/src/Command/StatusPaymentRequest.php index 2d64545a..5271bcad 100644 --- a/src/Command/StatusPaymentRequest.php +++ b/src/Command/StatusPaymentRequest.php @@ -7,11 +7,13 @@ use Sylius\Bundle\PaymentBundle\Command\PaymentRequestHashAwareInterface; use Sylius\Bundle\PaymentBundle\Command\PaymentRequestHashAwareTrait; -class StatusPaymentRequest implements PaymentRequestHashAwareInterface +class StatusPaymentRequest extends AbstractPayplugPaymentRequest { use PaymentRequestHashAwareTrait; - public function __construct(protected ?string $hash, private string $forcedStatus = '') {} + public function __construct(protected ?string $hash, private string $forcedStatus = '') { + parent::__construct($hash); + } public function getForcedStatus() : string { diff --git a/src/OrderPay/Provider/NotifyPaymentProvider.php b/src/OrderPay/Provider/NotifyPaymentProvider.php new file mode 100644 index 00000000..46b0e491 --- /dev/null +++ b/src/OrderPay/Provider/NotifyPaymentProvider.php @@ -0,0 +1,69 @@ +getPayload()->all('metadata')['order_number'] ?? null; + if (null === $orderNumber) { + throw new \InvalidArgumentException('Order number not found in request payload'); + } + $order = $this->getOrderFromReference($orderNumber); + + $payId = $request->getPayload()->getString('id'); + $payment = $order->getPayments()->filter(function (PaymentInterface $payment) use ($payId) { + return $payment->getDetails()['payment_id'] === $payId; + })->first(); + if (false === $payment) { + throw new \InvalidArgumentException(sprintf('Payment with ID "%s" not found in order "%s"', $payId, $orderNumber)); + } + + return $payment; + } + + public function supports(Request $request, PaymentMethodInterface $paymentMethod): bool + { + return \str_contains($paymentMethod->getGatewayConfig()?->getFactoryName(), 'payplug') && + $request->getPayload()->has('id') && + $request->getPayload()->has('metadata'); + } + + private function getOrderFromReference(string $orderReference): OrderInterface + { + $order = $this->orderRepository->findOneBy(['number' => $orderReference]); + if (null === $order) { + // @see \PayPlug\SyliusPayPlugPlugin\Creator\PayPlugPaymentDataCreator order_number can be number/token or id + $order = $this->orderRepository->findOneBy(['tokenValue' => $orderReference]); + } + if (null === $order) { + $order = $this->orderRepository->findOneBy(['id' => $orderReference]); + } + if (!$order instanceof OrderInterface) { + throw new \InvalidArgumentException(sprintf('Order with reference "%s" not found', $orderReference)); + } + + return $order; + } +} From f7bba93485ff77e5f7f2882b1cc892c31c32b878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jib=C3=A9=20Barth?= Date: Thu, 17 Jul 2025 17:46:29 +0200 Subject: [PATCH 11/11] PPSYL-138 - Coding standards --- src/Command/Handler/CapturePaymentRequestHandler.php | 2 -- src/Command/Handler/NotifyPaymentRequestHandler.php | 2 -- .../Provider/CapturePaymentRequestCommandProvider.php | 5 ----- 3 files changed, 9 deletions(-) diff --git a/src/Command/Handler/CapturePaymentRequestHandler.php b/src/Command/Handler/CapturePaymentRequestHandler.php index a77026bb..3899d31d 100644 --- a/src/Command/Handler/CapturePaymentRequestHandler.php +++ b/src/Command/Handler/CapturePaymentRequestHandler.php @@ -44,8 +44,6 @@ public function __invoke(CapturePaymentRequest $capturePaymentRequest): void 'cancel_url' => $returnUrl . '?&' . http_build_query(['status' => PayPlugApiClientInterface::STATUS_CANCELED]), ]; - $this->afterPayUrlProvider->getUrl($paymentRequest, UrlGeneratorInterface::ABSOLUTE_URL); - $notificationUrl = $this->urlGenerator->generate('sylius_payment_method_notify', ['code' => $payment->getMethod()?->getCode()], UrlGeneratorInterface::ABSOLUTE_URL); $details['notification_url'] = $notificationUrl; diff --git a/src/Command/Handler/NotifyPaymentRequestHandler.php b/src/Command/Handler/NotifyPaymentRequestHandler.php index a700a850..00d8cc6c 100644 --- a/src/Command/Handler/NotifyPaymentRequestHandler.php +++ b/src/Command/Handler/NotifyPaymentRequestHandler.php @@ -49,14 +49,12 @@ public function __invoke(NotifyPaymentRequest $notifyPaymentRequest): void $payment->setDetails($details->getArrayCopy()); $this->updatePaymentState($payment); - throw new \LogicException(sprintf('Unknown payment status "%s".', $payment->getDetails()['status'] ?? '')); $this->stateMachine->apply( $paymentRequest, PaymentRequestTransitions::GRAPH, PaymentRequestTransitions::TRANSITION_COMPLETE ); - } catch (\Throwable $e) { $paymentRequest->setResponseData([ 'error' => $e->getMessage(), diff --git a/src/Command/Provider/CapturePaymentRequestCommandProvider.php b/src/Command/Provider/CapturePaymentRequestCommandProvider.php index a702f382..495feb9f 100644 --- a/src/Command/Provider/CapturePaymentRequestCommandProvider.php +++ b/src/Command/Provider/CapturePaymentRequestCommandProvider.php @@ -38,12 +38,8 @@ public function supports(PaymentRequestInterface $paymentRequest): bool public function provide(PaymentRequestInterface $paymentRequest): object { - $payment = $paymentRequest->getPayment(); - $details = $payment->getDetails(); - if ($this->isAlreadyCreated($paymentRequest)) { // The payment has already been created, let's use the offline capture request to be redirected to the thank-you page - // TODO: use our own AlreadyPaidCapturePaymentRequest class return new OfflineCapturePaymentRequest($paymentRequest->getId()); } @@ -54,7 +50,6 @@ private function isAlreadyCreated(PaymentRequestInterface $paymentRequest): bool { $payment = $paymentRequest->getPayment(); $details = $payment->getDetails(); - if ( isset($details['status'], $details['payment_id']) && PayPlugApiClientInterface::STATUS_CREATED !== $details['status']