From d0d407ee10640c5155fd2daf2bd36f678038e6ee Mon Sep 17 00:00:00 2001 From: Francis Hilaire Date: Tue, 2 Jan 2024 16:10:15 +0100 Subject: [PATCH 1/6] Change callbacks to after instead of before to avoid double call --- src/Resources/config/config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Resources/config/config.yaml b/src/Resources/config/config.yaml index 7dffe24..86420e9 100644 --- a/src/Resources/config/config.yaml +++ b/src/Resources/config/config.yaml @@ -5,7 +5,7 @@ twig: winzou_state_machine: sylius_payment: callbacks: - before: + after: flux_se.sylius_payum_stripe_refund: # By default this callback is disabled to avoid mistake # you can enable it using this plugin config : `refund_disabled: false` @@ -19,4 +19,4 @@ winzou_state_machine: flux_se.sylius_payum_stripe_complete_authorized: on: ["complete"] do: ["@flux_se.sylius_payum_stripe.state_machine.complete_authorized", "__invoke"] - args: ["object"] \ No newline at end of file + args: ["object"] From ae256910f3c44ff1b16655e1459517e5034617e2 Mon Sep 17 00:00:00 2001 From: Francis Hilaire Date: Thu, 4 Jan 2024 00:40:37 +0100 Subject: [PATCH 2/6] Refactor state machine callbacks --- src/Resources/config/config.yaml | 10 +++++----- src/Resources/config/services/state_machine.yaml | 4 ++-- src/StateMachine/AbstractOrderProcessor.php | 5 +++-- ...izedOrderProcessor.php => CancelOrderProcessor.php} | 9 +++------ src/StateMachine/CompleteAuthorizedOrderProcessor.php | 5 +++-- src/StateMachine/RefundOrderProcessor.php | 5 +++-- 6 files changed, 19 insertions(+), 19 deletions(-) rename src/StateMachine/{CancelAuthorizedOrderProcessor.php => CancelOrderProcessor.php} (79%) diff --git a/src/Resources/config/config.yaml b/src/Resources/config/config.yaml index 86420e9..80b7ea5 100644 --- a/src/Resources/config/config.yaml +++ b/src/Resources/config/config.yaml @@ -11,12 +11,12 @@ winzou_state_machine: # you can enable it using this plugin config : `refund_disabled: false` on: ["refund"] do: ["@flux_se.sylius_payum_stripe.state_machine.refund", "__invoke"] - args: ["object"] - flux_se.sylius_payum_stripe_cancel_authorized: + args: ["object", "event"] + flux_se.sylius_payum_stripe_cancel: on: ["cancel"] - do: ["@flux_se.sylius_payum_stripe.state_machine.cancel_authorized", "__invoke"] - args: ["object"] + do: ["@flux_se.sylius_payum_stripe.state_machine.cancel", "__invoke"] + args: ["object", "event"] flux_se.sylius_payum_stripe_complete_authorized: on: ["complete"] do: ["@flux_se.sylius_payum_stripe.state_machine.complete_authorized", "__invoke"] - args: ["object"] + args: ["object", "event"] diff --git a/src/Resources/config/services/state_machine.yaml b/src/Resources/config/services/state_machine.yaml index 5681f21..ab97d29 100644 --- a/src/Resources/config/services/state_machine.yaml +++ b/src/Resources/config/services/state_machine.yaml @@ -12,10 +12,10 @@ services: arguments: $refundRequestFactory: '@flux_se.sylius_payum_stripe.factory.refund_request' - flux_se.sylius_payum_stripe.state_machine.cancel_authorized: + flux_se.sylius_payum_stripe.state_machine.cancel: public: true parent: FluxSE\SyliusPayumStripePlugin\StateMachine\AbstractOrderProcessor - class: FluxSE\SyliusPayumStripePlugin\StateMachine\CancelAuthorizedOrderProcessor + class: FluxSE\SyliusPayumStripePlugin\StateMachine\CancelOrderProcessor arguments: $cancelRequestFactory: '@flux_se.sylius_payum_stripe.factory.cancel_request' diff --git a/src/StateMachine/AbstractOrderProcessor.php b/src/StateMachine/AbstractOrderProcessor.php index de56c83..0ca20b3 100644 --- a/src/StateMachine/AbstractOrderProcessor.php +++ b/src/StateMachine/AbstractOrderProcessor.php @@ -7,6 +7,7 @@ use Payum\Core\Payum; use Payum\Core\Security\TokenFactoryInterface; use Payum\Core\Security\TokenInterface; +use SM\Event\TransitionEvent; use Sylius\Bundle\PayumBundle\Model\GatewayConfigInterface; use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Core\Model\PaymentMethodInterface; @@ -26,6 +27,8 @@ public function __construct(Payum $payum) $this->payum = $payum; } + abstract public function __invoke(PaymentInterface $payment, TransitionEvent $event): void; + protected function getGatewayNameFromPayment(PaymentInterface $payment): ?string { /** @var PaymentMethodInterface|null $paymentMethod */ @@ -57,6 +60,4 @@ protected function buildToken(string $gatewayName, PaymentInterface $payment): T return $tokenFactory->createToken($gatewayName, $payment, 'payum_notify_do'); } - - abstract public function __invoke(PaymentInterface $payment): void; } diff --git a/src/StateMachine/CancelAuthorizedOrderProcessor.php b/src/StateMachine/CancelOrderProcessor.php similarity index 79% rename from src/StateMachine/CancelAuthorizedOrderProcessor.php rename to src/StateMachine/CancelOrderProcessor.php index 4f656b3..58b563a 100644 --- a/src/StateMachine/CancelAuthorizedOrderProcessor.php +++ b/src/StateMachine/CancelOrderProcessor.php @@ -6,9 +6,10 @@ use FluxSE\SyliusPayumStripePlugin\Factory\CancelRequestFactoryInterface; use Payum\Core\Payum; +use SM\Event\TransitionEvent; use Sylius\Component\Core\Model\PaymentInterface; -final class CancelAuthorizedOrderProcessor extends AbstractOrderProcessor +final class CancelOrderProcessor extends AbstractOrderProcessor { /** @var CancelRequestFactoryInterface */ private $cancelRequestFactory; @@ -21,12 +22,8 @@ public function __construct( parent::__construct($payum); } - public function __invoke(PaymentInterface $payment): void + public function __invoke(PaymentInterface $payment, TransitionEvent $event): void { - if (PaymentInterface::STATE_AUTHORIZED !== $payment->getState()) { - return; - } - $gatewayName = $this->getGatewayNameFromPayment($payment); if (null === $gatewayName) { diff --git a/src/StateMachine/CompleteAuthorizedOrderProcessor.php b/src/StateMachine/CompleteAuthorizedOrderProcessor.php index 1d01ed9..fbca964 100644 --- a/src/StateMachine/CompleteAuthorizedOrderProcessor.php +++ b/src/StateMachine/CompleteAuthorizedOrderProcessor.php @@ -7,6 +7,7 @@ use FluxSE\SyliusPayumStripePlugin\Factory\CaptureRequestFactoryInterface; use Payum\Core\Payum; use Payum\Core\Reply\ReplyInterface; +use SM\Event\TransitionEvent; use Sylius\Component\Core\Model\PaymentInterface; use Webmozart\Assert\Assert; @@ -23,9 +24,9 @@ public function __construct( parent::__construct($payum); } - public function __invoke(PaymentInterface $payment): void + public function __invoke(PaymentInterface $payment, TransitionEvent $event): void { - if (PaymentInterface::STATE_AUTHORIZED !== $payment->getState()) { + if (PaymentInterface::STATE_AUTHORIZED !== $event->getState()) { return; } diff --git a/src/StateMachine/RefundOrderProcessor.php b/src/StateMachine/RefundOrderProcessor.php index 737199c..5f9cc37 100644 --- a/src/StateMachine/RefundOrderProcessor.php +++ b/src/StateMachine/RefundOrderProcessor.php @@ -6,6 +6,7 @@ use FluxSE\SyliusPayumStripePlugin\Factory\RefundRequestFactoryInterface; use Payum\Core\Payum; +use SM\Event\TransitionEvent; use Sylius\Component\Core\Model\PaymentInterface; final class RefundOrderProcessor extends AbstractOrderProcessor @@ -21,9 +22,9 @@ public function __construct( parent:: __construct($payum); } - public function __invoke(PaymentInterface $payment): void + public function __invoke(PaymentInterface $payment, TransitionEvent $event): void { - if (PaymentInterface::STATE_COMPLETED !== $payment->getState()) { + if (PaymentInterface::STATE_COMPLETED !== $event->getState()) { return; } From cd5702a77c8a48f7e21081a660d96c3080bd5dee Mon Sep 17 00:00:00 2001 From: Francis Hilaire Date: Thu, 4 Jan 2024 00:41:34 +0100 Subject: [PATCH 3/6] Add Behat tests for each admin order action triggering the state machine callbacks --- .../admin/cancel_authorized_order.feature | 25 ++ features/admin/cancel_order.feature | 25 ++ .../admin/capture_authorization_order.feature | 25 ++ features/admin/refunding_order.feature | 25 ++ .../Ui/Admin/ManagingOrdersContext.php | 150 ++++++++++ .../Mocker/Api/CheckoutSessionMocker.php | 164 +++++++++++ .../Behat/Mocker/Api/PaymentIntentMocker.php | 157 +++++++++++ tests/Behat/Mocker/Api/RefundMocker.php | 57 ++++ .../Mocker/StripeCheckoutSessionMocker.php | 266 +++++------------- tests/Behat/Resources/services.xml | 1 + tests/Behat/Resources/services/context.xml | 19 +- tests/Behat/Resources/services/mocker.xml | 35 +-- tests/Behat/Resources/services/page.xml | 8 +- .../Behat/Resources/services/payum_mocks.xml | 55 ++++ tests/Behat/Resources/suites.yaml | 42 ++- 15 files changed, 822 insertions(+), 232 deletions(-) create mode 100644 features/admin/cancel_authorized_order.feature create mode 100644 features/admin/cancel_order.feature create mode 100644 features/admin/capture_authorization_order.feature create mode 100644 features/admin/refunding_order.feature create mode 100644 tests/Behat/Context/Ui/Admin/ManagingOrdersContext.php create mode 100644 tests/Behat/Mocker/Api/CheckoutSessionMocker.php create mode 100644 tests/Behat/Mocker/Api/PaymentIntentMocker.php create mode 100644 tests/Behat/Mocker/Api/RefundMocker.php create mode 100644 tests/Behat/Resources/services/payum_mocks.xml diff --git a/features/admin/cancel_authorized_order.feature b/features/admin/cancel_authorized_order.feature new file mode 100644 index 0000000..ea306a4 --- /dev/null +++ b/features/admin/cancel_authorized_order.feature @@ -0,0 +1,25 @@ +@managing_orders +Feature: Canceling an authorized order + In order to cancel an order already authorized + As an Administrator + I want to be able to cancel a Stripe authorized order + + Background: + Given the store operates on a single channel in "United States" + And the store has a product "Green Arrow" + And the store ships everywhere for free + And the store has a payment method "Stripe" with a code "stripe" and Stripe payment gateway using authorize + And there is a customer "oliver@teamarrow.com" that placed an order "#00000001" + And the customer bought a single "Green Arrow" + And the customer chose "Free" shipping method to "United States" with "Stripe" payment + And this order is already authorized as "pi_123" Stripe payment intent + And I am logged in as an administrator + + @ui + Scenario: Initializing the Stripe refund + Given I am viewing the summary of this order + And I am prepared to cancel this order + When I cancel this order + Then I should be notified that it has been successfully updated + And it should have payment with state cancelled + And it should have payment state cancelled diff --git a/features/admin/cancel_order.feature b/features/admin/cancel_order.feature new file mode 100644 index 0000000..e15b7ec --- /dev/null +++ b/features/admin/cancel_order.feature @@ -0,0 +1,25 @@ +@managing_orders +Feature: Canceling an order + In order to cancel a not paid order + As an Administrator + I want to be able to cancel a Stripe not paid order + + Background: + Given the store operates on a single channel in "United States" + And the store has a product "Green Arrow" + And the store ships everywhere for free + And the store has a payment method "Stripe" with a code "stripe" and Stripe payment gateway without using authorize + And there is a customer "oliver@teamarrow.com" that placed an order "#00000001" + And the customer bought a single "Green Arrow" + And the customer chose "Free" shipping method to "United States" with "Stripe" payment + And this order is not yet paid as "cs_123" Stripe checkout session + And I am logged in as an administrator + + @ui + Scenario: Initializing the Stripe refund + Given I am viewing the summary of this order + And I am prepared to expire the checkout session this order + When I cancel this order + Then I should be notified that it has been successfully updated + And it should have payment with state cancelled + And it should have payment state cancelled diff --git a/features/admin/capture_authorization_order.feature b/features/admin/capture_authorization_order.feature new file mode 100644 index 0000000..4ac3d05 --- /dev/null +++ b/features/admin/capture_authorization_order.feature @@ -0,0 +1,25 @@ +@managing_orders +Feature: Capturing the authorization of an order + In order to complete a payment + As an Administrator + I want to be able to capture the authorization of a Stripe paid order + + Background: + Given the store operates on a single channel in "United States" + And the store has a product "Green Arrow" + And the store ships everywhere for free + And the store has a payment method "Stripe" with a code "stripe" and Stripe payment gateway using authorize + And there is a customer "oliver@teamarrow.com" that placed an order "#00000001" + And the customer bought a single "Green Arrow" + And the customer chose "Free" shipping method to "United States" with "Stripe" payment + And this order is already authorized as "pi_123" Stripe payment intent + And I am logged in as an administrator + + @ui + Scenario: Initializing the Stripe refund + Given I am viewing the summary of this order + And I am prepared to capture authorization of this order + When I mark this order as paid + Then I should be notified that the order's payment has been successfully completed + And it should have payment with state completed + And it should have payment state completed diff --git a/features/admin/refunding_order.feature b/features/admin/refunding_order.feature new file mode 100644 index 0000000..502f661 --- /dev/null +++ b/features/admin/refunding_order.feature @@ -0,0 +1,25 @@ +@managing_orders +Feature: Refunding an order + In order to return the money to the Customer + As an Administrator + I want to be able to refund a Stripe paid order + + Background: + Given the store operates on a single channel in "United States" + And the store has a product "Green Arrow" + And the store ships everywhere for free + And the store has a payment method "Stripe" with a code "stripe" and Stripe payment gateway without using authorize + And there is a customer "oliver@teamarrow.com" that placed an order "#00000001" + And the customer bought a single "Green Arrow" + And the customer chose "Free" shipping method to "United States" with "Stripe" payment + And this order is already paid as "pi_123" Stripe payment intent + And I am logged in as an administrator + + @ui + Scenario: Initializing the Stripe refund + Given I am viewing the summary of this order + And I am prepared to refund this order + When I mark this order's payment as refunded + Then I should be notified that the order's payment has been successfully refunded + And it should have payment with state refunded + And it's payment state should be refunded diff --git a/tests/Behat/Context/Ui/Admin/ManagingOrdersContext.php b/tests/Behat/Context/Ui/Admin/ManagingOrdersContext.php new file mode 100644 index 0000000..492fbeb --- /dev/null +++ b/tests/Behat/Context/Ui/Admin/ManagingOrdersContext.php @@ -0,0 +1,150 @@ +stateMachineFactory = $stateMachineFactory; + $this->objectManager = $objectManager; + $this->stripeSessionCheckoutMocker = $stripeSessionCheckoutMocker; + } + + /** + * @Given /^(this order) is already paid as "([^"]+)" Stripe payment intent$/ + */ + public function thisOrderIsAlreadyPaid(OrderInterface $order, string $stripePaymentIntentId): void + { + /** @var PaymentInterface $payment */ + $payment = $order->getPayments()->first(); + + $details = [ + 'object' => PaymentIntent::OBJECT_NAME, + 'id' => $stripePaymentIntentId, + 'status' => PaymentIntent::STATUS_SUCCEEDED, + 'capture_method' => PaymentIntent::CAPTURE_METHOD_AUTOMATIC, + ]; + $payment->setDetails($details); + + /** @var StateMachineInterface $stateMachine */ + $stateMachine = $this->stateMachineFactory->get($payment, PaymentTransitions::GRAPH); + $stateMachine->apply(PaymentTransitions::TRANSITION_COMPLETE); + + $this->objectManager->flush(); + } + + /** + * @Given /^(this order) is already authorized as "([^"]+)" Stripe payment intent$/ + */ + public function thisOrderIsAlreadyAuthorized(OrderInterface $order, string $stripePaymentIntentId): void + { + /** @var PaymentInterface $payment */ + $payment = $order->getPayments()->first(); + + $details = [ + 'object' => PaymentIntent::OBJECT_NAME, + 'id' => $stripePaymentIntentId, + 'status' => PaymentIntent::STATUS_REQUIRES_CAPTURE, + 'capture_method' => PaymentIntent::CAPTURE_METHOD_MANUAL, + ]; + $payment->setDetails($details); + + /** @var StateMachineInterface $stateMachine */ + $stateMachine = $this->stateMachineFactory->get($payment, PaymentTransitions::GRAPH); + $stateMachine->apply(PaymentTransitions::TRANSITION_AUTHORIZE); + + $this->objectManager->flush(); + } + + /** + * @Given /^(this order) is not yet paid as "([^"]+)" Stripe checkout session$/ + */ + public function thisOrderIsNotYetPaid(OrderInterface $order, string $stripeCheckoutSessionId): void + { + /** @var PaymentInterface $payment */ + $payment = $order->getPayments()->first(); + + $details = [ + 'object' => Session::OBJECT_NAME, + 'id' => $stripeCheckoutSessionId, + 'status' => Session::STATUS_OPEN, + 'payment_status' => Session::PAYMENT_STATUS_UNPAID, + ]; + $payment->setDetails($details); + + $this->objectManager->flush(); + } + + /** + * @Given /^I am prepared to cancel (this order)$/ + */ + public function iAmPreparedToCancelThisOrder(OrderInterface $order): void + { + /** @var PaymentInterface $payment */ + $payment = $order->getPayments()->first(); + + $details = $payment->getDetails(); + $status = $details['status'] ?? PaymentIntent::STATUS_REQUIRES_PAYMENT_METHOD; + $captureMethod = $details['capture_method'] ?? PaymentIntent::CAPTURE_METHOD_AUTOMATIC; + + $this->stripeSessionCheckoutMocker->mockCancelPayment($status, $captureMethod); + } + + /** + * @Given I am prepared to expire the checkout session this order + */ + public function iAmPreparedToExpireTheCheckoutSessionThisOrder(): void + { + $this->stripeSessionCheckoutMocker->mockExpirePayment(); + } + + /** + * @Given I am prepared to refund this order + */ + public function iAmPreparedToRefundThisOrder(): void + { + $this->stripeSessionCheckoutMocker->mockRefundPayment(); + } + + /** + * @Given /^I am prepared to capture authorization of (this order)$/ + */ + public function iAmPreparedToCaptureAuthorizationOfThisOrder(OrderInterface $order): void + { + /** @var PaymentInterface $payment */ + $payment = $order->getPayments()->first(); + + $details = $payment->getDetails(); + $status = $details['status'] ?? PaymentIntent::STATUS_REQUIRES_CAPTURE; + $captureMethod = $details['capture_method'] ?? PaymentIntent::CAPTURE_METHOD_MANUAL; + + $this->stripeSessionCheckoutMocker->mockCaptureAuthorization($status, $captureMethod); + } +} diff --git a/tests/Behat/Mocker/Api/CheckoutSessionMocker.php b/tests/Behat/Mocker/Api/CheckoutSessionMocker.php new file mode 100644 index 0000000..0ec81c8 --- /dev/null +++ b/tests/Behat/Mocker/Api/CheckoutSessionMocker.php @@ -0,0 +1,164 @@ +mocker = $mocker; + } + + public function mockCreateAction(): void + { + $mockCreateSession = $this->mocker->mockService( + 'tests.flux_se.sylius_payum_stripe_plugin.behat.mocker.action.create_session', + AbstractCreateAction::class + ); + + $mockCreateSession + ->shouldReceive('setApi') + ->once(); + $mockCreateSession + ->shouldReceive('setGateway') + ->once(); + + $mockCreateSession + ->shouldReceive('supports') + ->andReturnUsing(function ($request) { + return $request instanceof CreateSession; + }); + + $mockCreateSession + ->shouldReceive('execute') + ->once() + ->andReturnUsing(function (CreateSession $request) { + /** @var ArrayObject $rModel */ + $rModel = $request->getModel(); + $session = Session::constructFrom(array_merge([ + 'id' => 'cs_1', + 'object' => Session::OBJECT_NAME, + 'payment_intent' => 'pi_1', + ], $rModel->getArrayCopy())); + $request->setApiResource($session); + }); + } + + public function mockRetrieveAction(string $status, string $paymentStatus): void + { + $mock = $this->mocker->mockService( + 'tests.flux_se.sylius_payum_stripe_plugin.behat.mocker.action.retrieve_session', + AbstractRetrieveAction::class + ); + + $mock + ->shouldReceive('setApi') + ->once(); + $mock + ->shouldReceive('setGateway') + ->once(); + + $mock + ->shouldReceive('supports') + ->andReturnUsing(function ($request) { + return $request instanceof RetrieveSession; + }); + + $mock + ->shouldReceive('execute') + ->once() + ->andReturnUsing(function (RetrieveSession $request) use ($status, $paymentStatus) { + $request->setApiResource(Session::constructFrom([ + 'id' => $request->getId(), + 'object' => Session::OBJECT_NAME, + 'status' => $status, + 'payment_status' => $paymentStatus, + 'payment_intent' => 'pi_1', + ])); + }); + } + + public function mockAllAction(string $status): void + { + $mock = $this->mocker->mockService( + 'tests.flux_se.sylius_payum_stripe_plugin.behat.mocker.action.all_session', + AbstractAllAction::class + ); + + $mock + ->shouldReceive('setApi') + ->once(); + $mock + ->shouldReceive('setGateway') + ->once(); + + $mock + ->shouldReceive('supports') + ->andReturnUsing(function ($request) { + return $request instanceof AllSession; + }); + + $mock + ->shouldReceive('execute') + ->once() + ->andReturnUsing(function (AllSession $request) use ($status) { + $request->setApiResources( + Collection::constructFrom(['data' => [ + [ + 'id' => 'cs_1', + 'object' => Session::OBJECT_NAME, + 'status' => $status, + ], + ]])); + }); + } + + public function mockExpireAction(): void + { + $mock = $this->mocker->mockService( + 'tests.flux_se.sylius_payum_stripe_plugin.behat.mocker.action.expire_session', + AbstractRetrieveAction::class + ); + + $mock + ->shouldReceive('setApi') + ->once(); + $mock + ->shouldReceive('setGateway') + ->once(); + + $mock + ->shouldReceive('supports') + ->andReturnUsing(function ($request) { + return $request instanceof ExpireSession; + }); + + $mock + ->shouldReceive('execute') + ->once() + ->andReturnUsing(function (ExpireSession $request) { + $request->setApiResource(Session::constructFrom([ + 'id' => $request->getId(), + 'object' => Session::OBJECT_NAME, + 'status' => Session::STATUS_EXPIRED, + ])); + }); + } +} diff --git a/tests/Behat/Mocker/Api/PaymentIntentMocker.php b/tests/Behat/Mocker/Api/PaymentIntentMocker.php new file mode 100644 index 0000000..f2350bc --- /dev/null +++ b/tests/Behat/Mocker/Api/PaymentIntentMocker.php @@ -0,0 +1,157 @@ +mocker = $mocker; + } + + public function mockRetrieveAction(string $status): void + { + $mock = $this->mocker->mockService( + 'tests.flux_se.sylius_payum_stripe_plugin.behat.mocker.action.retrieve_payment_intent', + AbstractRetrieveAction::class + ); + + $mock + ->shouldReceive('setApi') + ->once(); + $mock + ->shouldReceive('setGateway') + ->once(); + + $mock + ->shouldReceive('supports') + ->andReturnUsing(function ($request) { + return $request instanceof RetrievePaymentIntent; + }); + + $mock + ->shouldReceive('execute') + ->once() + ->andReturnUsing(function (RetrievePaymentIntent $request) use ($status) { + $request->setApiResource(PaymentIntent::constructFrom([ + 'id' => $request->getId(), + 'object' => PaymentIntent::OBJECT_NAME, + 'status' => $status, + ])); + }); + } + + public function mockUpdateAction(string $status, string $captureMethod): void + { + $mock = $this->mocker->mockService( + 'tests.flux_se.sylius_payum_stripe_plugin.behat.mocker.action.update_payment_intent', + AbstractUpdateAction::class + ); + + $mock + ->shouldReceive('setApi') + ->once(); + $mock + ->shouldReceive('setGateway') + ->once(); + + $mock + ->shouldReceive('supports') + ->andReturnUsing(function ($request) { + return $request instanceof UpdatePaymentIntent; + }); + + $mock + ->shouldReceive('execute') + ->once() + ->andReturnUsing(function (UpdatePaymentIntent $request) use ($status, $captureMethod) { + $values = array_merge([ + 'id' => $request->getId(), + 'object' => PaymentIntent::OBJECT_NAME, + 'status' => $status, + 'capture_method' => $captureMethod, + ], $request->getParameters()); + $request->setApiResource(PaymentIntent::constructFrom($values)); + }); + } + + public function mockCancelAction(string $captureMethod): void + { + $mock = $this->mocker->mockService( + 'tests.flux_se.sylius_payum_stripe_plugin.behat.mocker.action.cancel_payment_intent', + AbstractRetrieveAction::class + ); + + $mock + ->shouldReceive('setApi') + ->once(); + $mock + ->shouldReceive('setGateway') + ->once(); + + $mock + ->shouldReceive('supports') + ->andReturnUsing(function ($request) { + return $request instanceof CancelPaymentIntent; + }); + + $mock + ->shouldReceive('execute') + ->once() + ->andReturnUsing(function (CancelPaymentIntent $request) use ($captureMethod) { + $request->setApiResource(PaymentIntent::constructFrom([ + 'id' => $request->getId(), + 'object' => PaymentIntent::OBJECT_NAME, + 'capture_method' => $captureMethod, + 'status' => PaymentIntent::STATUS_CANCELED, + ])); + }); + } + + public function mockCaptureAction(string $status): void + { + $mock = $this->mocker->mockService( + 'tests.flux_se.sylius_payum_stripe_plugin.behat.mocker.action.capture_payment_intent', + AbstractRetrieveAction::class + ); + + $mock + ->shouldReceive('setApi') + ->once(); + $mock + ->shouldReceive('setGateway') + ->once(); + + $mock + ->shouldReceive('supports') + ->andReturnUsing(function ($request) { + return $request instanceof CapturePaymentIntent; + }); + + $mock + ->shouldReceive('execute') + ->once() + ->andReturnUsing(function (CapturePaymentIntent $request) use ($status) { + $request->setApiResource(PaymentIntent::constructFrom([ + 'id' => $request->getId(), + 'object' => PaymentIntent::OBJECT_NAME, + 'status' => $status, + 'capture_method' => PaymentIntent::CAPTURE_METHOD_MANUAL, + ])); + }); + } +} diff --git a/tests/Behat/Mocker/Api/RefundMocker.php b/tests/Behat/Mocker/Api/RefundMocker.php new file mode 100644 index 0000000..f297a1d --- /dev/null +++ b/tests/Behat/Mocker/Api/RefundMocker.php @@ -0,0 +1,57 @@ +mocker = $mocker; + } + + public function mockCreateAction(): void + { + $mockCreateSession = $this->mocker->mockService( + 'tests.flux_se.sylius_payum_stripe_plugin.behat.mocker.action.create_refund', + AbstractCreateAction::class + ); + + $mockCreateSession + ->shouldReceive('setApi') + ->once(); + $mockCreateSession + ->shouldReceive('setGateway') + ->once(); + + $mockCreateSession + ->shouldReceive('supports') + ->andReturnUsing(function ($request) { + return $request instanceof CreateRefund; + }); + + $mockCreateSession + ->shouldReceive('execute') + ->once() + ->andReturnUsing(function (CreateRefund $request) { + /** @var ArrayObject $rModel */ + $rModel = $request->getModel(); + $refund = Session::constructFrom(array_merge([ + 'id' => 're_1', + 'object' => Refund::OBJECT_NAME, + ], $rModel->getArrayCopy())); + $request->setApiResource($refund); + }); + } +} diff --git a/tests/Behat/Mocker/StripeCheckoutSessionMocker.php b/tests/Behat/Mocker/StripeCheckoutSessionMocker.php index 01af9d6..6bedca7 100644 --- a/tests/Behat/Mocker/StripeCheckoutSessionMocker.php +++ b/tests/Behat/Mocker/StripeCheckoutSessionMocker.php @@ -4,33 +4,43 @@ namespace Tests\FluxSE\SyliusPayumStripePlugin\Behat\Mocker; -use ArrayObject; -use FluxSE\PayumStripe\Action\Api\Resource\AbstractAllAction; -use FluxSE\PayumStripe\Action\Api\Resource\AbstractCreateAction; -use FluxSE\PayumStripe\Action\Api\Resource\AbstractRetrieveAction; -use FluxSE\PayumStripe\Request\Api\Resource\AllSession; -use FluxSE\PayumStripe\Request\Api\Resource\CreateSession; -use FluxSE\PayumStripe\Request\Api\Resource\ExpireSession; -use FluxSE\PayumStripe\Request\Api\Resource\RetrievePaymentIntent; -use FluxSE\PayumStripe\Request\Api\Resource\RetrieveSession; use Stripe\Checkout\Session; -use Stripe\Collection; use Stripe\PaymentIntent; +use Stripe\Refund; use Sylius\Behat\Service\Mocker\MockerInterface; +use Tests\FluxSE\SyliusPayumStripePlugin\Behat\Mocker\Api\CheckoutSessionMocker; +use Tests\FluxSE\SyliusPayumStripePlugin\Behat\Mocker\Api\PaymentIntentMocker; +use Tests\FluxSE\SyliusPayumStripePlugin\Behat\Mocker\Api\RefundMocker; final class StripeCheckoutSessionMocker { /** @var MockerInterface */ private $mocker; - public function __construct(MockerInterface $mocker) - { + /** @var CheckoutSessionMocker */ + private $checkoutSessionMocker; + + /** @var PaymentIntentMocker */ + private $paymentIntentMocker; + + /** @var RefundMocker */ + private $refundMocker; + + public function __construct( + MockerInterface $mocker, + CheckoutSessionMocker $checkoutSessionMocker, + PaymentIntentMocker $paymentIntentMocker, + RefundMocker $refundMocker + ) { $this->mocker = $mocker; + $this->checkoutSessionMocker = $checkoutSessionMocker; + $this->paymentIntentMocker = $paymentIntentMocker; + $this->refundMocker = $refundMocker; } public function mockCreatePayment(callable $action): void { - $this->mockCreateSessionAction(); + $this->checkoutSessionMocker->mockCreateAction(); $this->mockSessionSync( $action, @@ -40,9 +50,42 @@ public function mockCreatePayment(callable $action): void ); } - public function mockGoBackPayment( - callable $action - ): void { + public function mockCancelPayment(string $status, string $captureMethod): void + { + $this->mocker->unmockAll(); + + $this->paymentIntentMocker->mockUpdateAction($status, $captureMethod); + $this->paymentIntentMocker->mockCancelAction($captureMethod); + $this->paymentIntentMocker->mockRetrieveAction(PaymentIntent::STATUS_CANCELED); + } + + public function mockRefundPayment(): void + { + $this->mocker->unmockAll(); + + $this->refundMocker->mockCreateAction(); + } + + public function mockExpirePayment(): void + { + $this->mocker->unmockAll(); + + $this->checkoutSessionMocker->mockExpireAction(); + $this->checkoutSessionMocker->mockRetrieveAction(Session::STATUS_EXPIRED, Session::PAYMENT_STATUS_UNPAID); + $this->paymentIntentMocker->mockRetrieveAction(PaymentIntent::STATUS_CANCELED); + } + + public function mockCaptureAuthorization(string $status, string $captureMethod): void + { + $this->mocker->unmockAll(); + + $this->paymentIntentMocker->mockUpdateAction($status, $captureMethod); + $this->paymentIntentMocker->mockCaptureAction(PaymentIntent::STATUS_SUCCEEDED); + $this->paymentIntentMocker->mockRetrieveAction(PaymentIntent::STATUS_SUCCEEDED); + } + + public function mockGoBackPayment(callable $action): void + { $this->mockExpireSession(Session::STATUS_OPEN); $this->mockSessionSync( $action, @@ -52,10 +95,8 @@ public function mockGoBackPayment( ); } - public function mockSuccessfulPayment( - callable $notifyAction, - callable $action - ): void { + public function mockSuccessfulPayment(callable $notifyAction, callable $action): void + { $this->mockSessionSync( $notifyAction, Session::STATUS_COMPLETE, @@ -65,10 +106,8 @@ public function mockSuccessfulPayment( $this->mockPaymentIntentSync($action,PaymentIntent::STATUS_SUCCEEDED); } - public function mockAuthorizePayment( - callable $notifyAction, - callable $action - ): void { + public function mockAuthorizePayment(callable $notifyAction, callable $action): void + { $this->mockSessionSync( $notifyAction, Session::STATUS_COMPLETE, @@ -78,9 +117,8 @@ public function mockAuthorizePayment( $this->mockPaymentIntentSync($action, PaymentIntent::STATUS_REQUIRES_CAPTURE); } - public function mockSuccessfulPaymentWithoutWebhook( - callable $action - ): void { + public function mockSuccessfulPaymentWithoutWebhook(callable $action ): void + { $this->mockSessionSync( $action, Session::STATUS_COMPLETE, @@ -89,9 +127,8 @@ public function mockSuccessfulPaymentWithoutWebhook( ); } - public function mockSuccessfulPaymentWithoutWebhookUsingAuthorize( - callable $action - ): void { + public function mockSuccessfulPaymentWithoutWebhookUsingAuthorize(callable $action): void + { $this->mockSessionSync( $action, Session::STATUS_COMPLETE, @@ -102,7 +139,7 @@ public function mockSuccessfulPaymentWithoutWebhookUsingAuthorize( public function mockPaymentIntentSync(callable $action, string $status): void { - $this->mockRetrievePaymentIntentAction($status); + $this->paymentIntentMocker->mockRetrieveAction($status); $action(); @@ -115,174 +152,13 @@ public function mockSessionSync( string $paymentStatus, string $paymentIntentStatus ): void { - $this->mockRetrieveSessionAction($sessionStatus, $paymentStatus); + $this->checkoutSessionMocker->mockRetrieveAction($sessionStatus, $paymentStatus); $this->mockPaymentIntentSync($action, $paymentIntentStatus); } public function mockExpireSession(string $sessionStatus): void { - $this->mockAllSessionAction($sessionStatus); - $this->mockExpireSessionAction(); - } - - private function mockCreateSessionAction(): void - { - $mockCreateSession = $this->mocker->mockService( - 'tests.flux_se.sylius_payum_stripe_checkout_session_plugin.behat.mocker.action.create_session', - AbstractCreateAction::class - ); - - $mockCreateSession - ->shouldReceive('setApi') - ->once(); - $mockCreateSession - ->shouldReceive('setGateway') - ->once(); - - $mockCreateSession - ->shouldReceive('supports') - ->andReturnUsing(function ($request) { - return $request instanceof CreateSession; - }); - - $mockCreateSession - ->shouldReceive('execute') - ->once() - ->andReturnUsing(function (CreateSession $request) { - /** @var ArrayObject $rModel */ - $rModel = $request->getModel(); - $session = Session::constructFrom(array_merge([ - 'id' => 'cs_1', - 'object' => Session::OBJECT_NAME, - 'payment_intent' => 'pi_1', - ], $rModel->getArrayCopy())); - $request->setApiResource($session); - }); - } - - private function mockRetrievePaymentIntentAction(string $status): void - { - $mock = $this->mocker->mockService( - 'tests.flux_se.sylius_payum_stripe_checkout_session_plugin.behat.mocker.action.retrieve_payment_intent', - AbstractRetrieveAction::class - ); - - $mock - ->shouldReceive('setApi') - ->once(); - $mock - ->shouldReceive('setGateway') - ->once(); - - $mock - ->shouldReceive('supports') - ->andReturnUsing(function ($request) { - return $request instanceof RetrievePaymentIntent; - }); - - $mock - ->shouldReceive('execute') - ->once() - ->andReturnUsing(function (RetrievePaymentIntent $request) use ($status) { - $request->setApiResource(PaymentIntent::constructFrom([ - 'id' => $request->getId(), - 'object' => PaymentIntent::OBJECT_NAME, - 'status' => $status, - ])); - }); - } - - private function mockRetrieveSessionAction(string $status, string $paymentStatus): void - { - $mock = $this->mocker->mockService( - 'tests.flux_se.sylius_payum_stripe_checkout_session_plugin.behat.mocker.action.retrieve_session', - AbstractRetrieveAction::class - ); - - $mock - ->shouldReceive('setApi') - ->once(); - $mock - ->shouldReceive('setGateway') - ->once(); - - $mock - ->shouldReceive('supports') - ->andReturnUsing(function ($request) { - return $request instanceof RetrieveSession; - }); - - $mock - ->shouldReceive('execute') - ->once() - ->andReturnUsing(function (RetrieveSession $request) use ($status, $paymentStatus) { - $request->setApiResource(Session::constructFrom([ - 'id' => $request->getId(), - 'object' => Session::OBJECT_NAME, - 'status' => $status, - 'payment_status' => $paymentStatus, - 'payment_intent' => 'pi_1', - ])); - }); - } - - private function mockAllSessionAction(string $status): void - { - $mock = $this->mocker->mockService( - 'tests.flux_se.sylius_payum_stripe_checkout_session_plugin.behat.mocker.action.all_session', - AbstractAllAction::class - ); - - $mock - ->shouldReceive('setApi') - ->once(); - $mock - ->shouldReceive('setGateway') - ->once(); - - $mock - ->shouldReceive('supports') - ->andReturnUsing(function ($request) { - return $request instanceof AllSession; - }); - - $mock - ->shouldReceive('execute') - ->once() - ->andReturnUsing(function (AllSession $request) use ($status) { - $request->setApiResources( - Collection::constructFrom(['data' => [ - [ - 'id' => 'cs_1', - 'object' => Session::OBJECT_NAME, - 'status' => $status, - ], - ]])); - }); - } - - private function mockExpireSessionAction(): void - { - $mock = $this->mocker->mockService( - 'tests.flux_se.sylius_payum_stripe_checkout_session_plugin.behat.mocker.action.expire_session', - AbstractRetrieveAction::class - ); - - $mock - ->shouldReceive('setApi') - ->once(); - $mock - ->shouldReceive('setGateway') - ->once(); - - $mock - ->shouldReceive('supports') - ->andReturnUsing(function ($request) { - return $request instanceof ExpireSession; - }); - - $mock - ->shouldReceive('execute') - ->once(); + $this->checkoutSessionMocker->mockAllAction($sessionStatus); + $this->checkoutSessionMocker->mockExpireAction(); } } diff --git a/tests/Behat/Resources/services.xml b/tests/Behat/Resources/services.xml index bc7fa29..27485e3 100644 --- a/tests/Behat/Resources/services.xml +++ b/tests/Behat/Resources/services.xml @@ -3,6 +3,7 @@ + diff --git a/tests/Behat/Resources/services/context.xml b/tests/Behat/Resources/services/context.xml index f3ca0e3..ce3b165 100644 --- a/tests/Behat/Resources/services/context.xml +++ b/tests/Behat/Resources/services/context.xml @@ -4,7 +4,7 @@ - @@ -12,17 +12,24 @@ - - + - + + + + + + - + - + diff --git a/tests/Behat/Resources/services/mocker.xml b/tests/Behat/Resources/services/mocker.xml index 63ca4da..8dcdd10 100644 --- a/tests/Behat/Resources/services/mocker.xml +++ b/tests/Behat/Resources/services/mocker.xml @@ -3,33 +3,26 @@ - - - - - - - - - - + + + + + - - + + - - + + - + diff --git a/tests/Behat/Resources/services/page.xml b/tests/Behat/Resources/services/page.xml index 3b7476c..9faf3cd 100644 --- a/tests/Behat/Resources/services/page.xml +++ b/tests/Behat/Resources/services/page.xml @@ -3,23 +3,23 @@ - - payum_notify_do_unsafe - - + diff --git a/tests/Behat/Resources/services/payum_mocks.xml b/tests/Behat/Resources/services/payum_mocks.xml new file mode 100644 index 0000000..f0d98e5 --- /dev/null +++ b/tests/Behat/Resources/services/payum_mocks.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/Behat/Resources/suites.yaml b/tests/Behat/Resources/suites.yaml index 4f6fe56..1ededfd 100644 --- a/tests/Behat/Resources/suites.yaml +++ b/tests/Behat/Resources/suites.yaml @@ -23,15 +23,45 @@ default: - sylius.behat.context.setup.user - sylius.behat.context.setup.zone - - tests.flux_se.sylius_payum_stripe_checkout_session_plugin.behat.context.setup.stripe + - tests.flux_se.sylius_payum_stripe_plugin.behat.context.setup.stripe - sylius.behat.context.ui.admin.managing_payment_methods - sylius.behat.context.ui.admin.notification - - sylius.behat.context.ui.shop.locale - - tests.flux_se.sylius_payum_stripe_checkout_session_plugin.behat.context.ui.admin.managing_payment_methods + - tests.flux_se.sylius_payum_stripe_plugin.behat.context.ui.admin.managing_payment_methods filters: tags: "@managing_payment_methods&&@ui" + ui_managing_orders: + contexts: + - sylius.behat.context.hook.doctrine_orm + + - sylius.behat.context.transform.address + - sylius.behat.context.transform.customer + - sylius.behat.context.transform.locale + - sylius.behat.context.transform.payment + - sylius.behat.context.transform.product + - sylius.behat.context.transform.shared_storage + - sylius.behat.context.transform.shipping_method + + - sylius.behat.context.setup.channel + - sylius.behat.context.setup.currency + - sylius.behat.context.setup.locale + - sylius.behat.context.setup.order + - sylius.behat.context.setup.payment + - sylius.behat.context.setup.product + - sylius.behat.context.setup.admin_security + - sylius.behat.context.setup.shipping + - sylius.behat.context.setup.user + - sylius.behat.context.setup.zone + + - tests.flux_se.sylius_payum_stripe_plugin.behat.context.setup.stripe + + - sylius.behat.context.ui.admin.managing_orders + - sylius.behat.context.ui.admin.notification + + - tests.flux_se.sylius_payum_stripe_plugin.behat.context.ui.admin.managing_orders + filters: + tags: "@managing_orders&&@ui" paying_with_stripe_session_checkout_during_checkout: contexts: - sylius.behat.context.hook.doctrine_orm @@ -60,7 +90,7 @@ default: - sylius.behat.context.setup.taxation - sylius.behat.context.setup.user - - tests.flux_se.sylius_payum_stripe_checkout_session_plugin.behat.context.setup.stripe + - tests.flux_se.sylius_payum_stripe_plugin.behat.context.setup.stripe - sylius.behat.context.ui.paypal - sylius.behat.context.ui.shop.cart @@ -72,6 +102,6 @@ default: - sylius.behat.context.ui.shop.checkout.shipping - sylius.behat.context.ui.shop.checkout.thank_you - - tests.flux_se.sylius_payum_stripe_checkout_session_plugin.behat.context.ui.shop_stripe + - tests.flux_se.sylius_payum_stripe_plugin.behat.context.ui.shop_stripe filters: - tags: "@paying_with_stripe_session_checkout_during_checkout&&@ui" \ No newline at end of file + tags: "@paying_with_stripe_session_checkout_during_checkout&&@ui" From 439c4726715b1d65a464a3b525734271ded47482 Mon Sep 17 00:00:00 2001 From: Francis Hilaire Date: Thu, 4 Jan 2024 00:47:59 +0100 Subject: [PATCH 4/6] Fix PHPStan moved methods --- phpstan.neon.dist | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index a028a2f..005ec74 100755 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -23,4 +23,7 @@ parameters: - '/Parameter #1 \$configuration of method Symfony\\Component\\DependencyInjection\\Extension\\Extension::processConfiguration\(\) expects Symfony\\Component\\Config\\Definition\\ConfigurationInterface, Symfony\\Component\\Config\\Definition\\ConfigurationInterface\|null given\./' - '/Parameter #1 \$request \(Payum\\Core\\Request\\Convert\) of method FluxSE\\SyliusPayumStripePlugin\\Action\\ConvertPaymentAction::execute\(\) should be contravariant with parameter \$request \(mixed\) of method Payum\\Core\\Action\\ActionInterface::execute\(\)/' - message: '/Call to an undefined method Mockery\\ExpectationInterface\|Mockery\\HigherOrderMessage::(once|andReturnUsing)\(\)\./' - path: tests/Behat/Mocker/StripeCheckoutSessionMocker.php + paths: + - tests/Behat/Mocker/Api/CheckoutSessionMocker.php + - tests/Behat/Mocker/Api/PaymentIntentMocker.php + - tests/Behat/Mocker/Api/RefundMocker.php From a6ee2f4d06199fe9fb3280b407232052ba48c83b Mon Sep 17 00:00:00 2001 From: Francis Hilaire Date: Thu, 4 Jan 2024 12:43:46 +0100 Subject: [PATCH 5/6] Add newest PHP version to the build and remove Sylius 1.9 --- .github/workflows/build.yml | 29 +++-------------------------- composer.json | 12 ++++++------ 2 files changed, 9 insertions(+), 32 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5fa8677..444daa1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,7 +28,7 @@ jobs: strategy: fail-fast: false matrix: - php: [ 8.0 ] + php: [ 8.1 ] symfony: [ ^5.4, ^6.0 ] steps: @@ -108,40 +108,25 @@ jobs: strategy: fail-fast: false matrix: - php: [7.3, 7.4, 8.0] + php: [7.4, 8.0, 8.1, 8.2, 8.3] symfony: [^5.4, ^6.0] - sylius: [~1.9.0, ~1.10.0, ~1.11.0, ~1.12.0] + sylius: [~1.10.0, ~1.11.0, ~1.12.0] node: [18.x] mysql: [5.7] exclude: - - - sylius: ~1.9.0 - php: 8.0 - - - sylius: ~1.10.0 - php: 7.3 - sylius: ~1.10.0 symfony: ^6.0 - - - sylius: ~1.11.0 - php: 7.3 - sylius: ~1.11.0 php: 7.4 - sylius: ~1.11.0 symfony: ^6.0 - - - sylius: ~1.12.0 - php: 7.3 - sylius: ~1.12.0 php: 7.4 - - - php: 7.3 - symfony: ^6.0 - php: 7.4 symfony: ^6.0 @@ -220,14 +205,6 @@ jobs: if: matrix.sylius != '' run: composer require "sylius/sylius:${{ matrix.sylius }}" --no-update --no-scripts --no-interaction - - - name: Fix build on Sylius 1.9 - if: matrix.sylius == '~1.9.0' - run: | - composer require "friendsofsymfony/oauth-server-bundle:>2.0.0-alpha.0 ^2.0@dev" --no-update --no-scripts --no-interaction - # Sylius issues on `json_array` type not existing - composer require "doctrine/dbal:^2.6" --no-update --no-scripts --no-interaction - composer require "nyholm/psr7" --no-update --no-scripts --no-interaction - name: Fix build on Sylius 1.10 if: matrix.sylius == '~1.10.0' diff --git a/composer.json b/composer.json index 641cdff..34c5784 100644 --- a/composer.json +++ b/composer.json @@ -28,10 +28,10 @@ "friends-of-behat/symfony-extension": "^2.1", "friends-of-behat/variadic-extension": "^1.3", "phpspec/phpspec": "^7.0", - "phpstan/extension-installer": "^1.2", - "phpstan/phpstan-doctrine": "^1", - "phpstan/phpstan-strict-rules": "^1", - "phpstan/phpstan-webmozart-assert": "^1", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan-doctrine": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpstan/phpstan-webmozart-assert": "^1.0", "phpunit/phpunit": "^9.5", "polishsymfonycommunity/symfony-mocker-container": "^1.0", "sylius-labs/coding-standard": "^4.0", @@ -42,8 +42,8 @@ "symfony/intl": "^5.4|^6.0", "symfony/runtime": "^5.4|^6.0", "symfony/web-profiler-bundle": "^5.4|^6.0", - "symfony/webpack-encore-bundle": "^1.16", - "vimeo/psalm": "^4|^5" + "symfony/webpack-encore-bundle": "^1|^2", + "vimeo/psalm": "^5" }, "autoload": { "psr-4": { "FluxSE\\SyliusPayumStripePlugin\\": "src/" } From 6dc653c000b5ce34d560ffa0cfd6599b47dd5db8 Mon Sep 17 00:00:00 2001 From: Francis Hilaire Date: Thu, 4 Jan 2024 13:56:57 +0100 Subject: [PATCH 6/6] Re-add phpspec --- .github/workflows/build.yml | 11 ++++++- ...rSpec.php => CancelOrderProcessorSpec.php} | 22 +++++++++----- .../CompleteAuthorizedOrderProcessorSpec.php | 30 +++++++++++++++---- .../StateMachine/RefundOrderProcessorSpec.php | 30 +++++++++++++++---- 4 files changed, 72 insertions(+), 21 deletions(-) rename spec/StateMachine/{CancelAuthorizedOrderProcessorSpec.php => CancelOrderProcessorSpec.php} (71%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 444daa1..c1b9d0c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -100,6 +100,11 @@ jobs: name: Run PHPStan run: vendor/bin/phpstan analyse if: always() && steps.end-of-setup.outcome == 'success' + + - + name: Run PHPSpec + run: vendor/bin/phpspec run --ansi -f progress --no-interaction + if: always() && steps.end-of-setup.outcome == 'success' tests: runs-on: ubuntu-latest @@ -215,7 +220,11 @@ jobs: if: matrix.sylius == '~1.11.0' run: | composer require "nyholm/psr7" --no-update --no-scripts --no-interaction - + - + name: Fix build with PHP 8.3 + if: matrix.php == '8.3' + run: | + composer remove --dev "phpspec/phpspec" --no-update --no-scripts --no-interaction - name: Install PHP dependencies run: composer install --no-interaction diff --git a/spec/StateMachine/CancelAuthorizedOrderProcessorSpec.php b/spec/StateMachine/CancelOrderProcessorSpec.php similarity index 71% rename from spec/StateMachine/CancelAuthorizedOrderProcessorSpec.php rename to spec/StateMachine/CancelOrderProcessorSpec.php index f1346b4..138f1f5 100644 --- a/spec/StateMachine/CancelAuthorizedOrderProcessorSpec.php +++ b/spec/StateMachine/CancelOrderProcessorSpec.php @@ -11,11 +11,12 @@ use Payum\Core\Security\TokenFactoryInterface; use Payum\Core\Security\TokenInterface; use PhpSpec\ObjectBehavior; +use SM\Event\TransitionEvent; use Sylius\Bundle\PayumBundle\Model\GatewayConfigInterface; use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Core\Model\PaymentMethodInterface; -final class CancelAuthorizedOrderProcessorSpec extends ObjectBehavior +final class CancelOrderProcessorSpec extends ObjectBehavior { public function let( CancelRequestFactoryInterface $cancelRequestFactory, @@ -27,6 +28,7 @@ public function let( public function it_is_invokable( Payum $payum, PaymentInterface $payment, + TransitionEvent $event, PaymentMethodInterface $paymentMethod, GatewayConfigInterface $gatewayConfig, GatewayInterface $gateway, @@ -35,7 +37,6 @@ public function it_is_invokable( CancelRequestFactoryInterface $cancelRequestFactory, ModelAggregateInterface $request ): void { - $payment->getState()->willReturn(PaymentInterface::STATE_AUTHORIZED); $payment->getMethod()->willReturn($paymentMethod); $paymentMethod->getGatewayConfig()->willReturn($gatewayConfig); @@ -46,21 +47,26 @@ public function it_is_invokable( $payum->getGateway($gatewayName)->willReturn($gateway); $payum->getTokenFactory()->willReturn($tokenFactory); - $tokenFactory->createToken($gatewayName, $payment, 'sylius_shop_order_after_pay')->willReturn($token); + $tokenFactory->createToken($gatewayName, $payment, 'payum_notify_do')->willReturn($token); $request->beConstructedWith([$token]); $cancelRequestFactory->createNewWithToken($token)->willReturn($request); $gateway->execute($request)->shouldBeCalled(); - $this->__invoke($payment); + $this->__invoke($payment, $event); } - public function it_do_nothing_when_it_is_not_an_authorized_state( - PaymentInterface $payment + public function it_do_nothing_when_gateway_is_unknown( + PaymentInterface $payment, + TransitionEvent $event, + PaymentMethodInterface $paymentMethod, + GatewayConfigInterface $gatewayConfig ): void { - $payment->getState()->willReturn(PaymentInterface::STATE_COMPLETED); + $payment->getMethod()->willReturn($paymentMethod); + $paymentMethod->getGatewayConfig()->willReturn($gatewayConfig); + $gatewayConfig->getConfig()->willReturn(['factory' => 'foo']); - $this->__invoke($payment); + $this->__invoke($payment, $event); } } diff --git a/spec/StateMachine/CompleteAuthorizedOrderProcessorSpec.php b/spec/StateMachine/CompleteAuthorizedOrderProcessorSpec.php index b6819be..9efe7b9 100644 --- a/spec/StateMachine/CompleteAuthorizedOrderProcessorSpec.php +++ b/spec/StateMachine/CompleteAuthorizedOrderProcessorSpec.php @@ -11,6 +11,7 @@ use Payum\Core\Security\TokenFactoryInterface; use Payum\Core\Security\TokenInterface; use PhpSpec\ObjectBehavior; +use SM\Event\TransitionEvent; use Sylius\Bundle\PayumBundle\Model\GatewayConfigInterface; use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Core\Model\PaymentMethodInterface; @@ -27,6 +28,7 @@ public function let( public function it_is_invokable( Payum $payum, PaymentInterface $payment, + TransitionEvent $event, PaymentMethodInterface $paymentMethod, GatewayConfigInterface $gatewayConfig, GatewayInterface $gateway, @@ -35,7 +37,7 @@ public function it_is_invokable( CaptureRequestFactoryInterface $captureRequestFactory, ModelAggregateInterface $request ): void { - $payment->getState()->willReturn(PaymentInterface::STATE_AUTHORIZED); + $event->getState()->willReturn(PaymentInterface::STATE_AUTHORIZED); $payment->getMethod()->willReturn($paymentMethod); $paymentMethod->getGatewayConfig()->willReturn($gatewayConfig); @@ -46,21 +48,37 @@ public function it_is_invokable( $payum->getGateway($gatewayName)->willReturn($gateway); $payum->getTokenFactory()->willReturn($tokenFactory); - $tokenFactory->createToken($gatewayName, $payment, 'sylius_shop_order_after_pay')->willReturn($token); + $tokenFactory->createToken($gatewayName, $payment, 'payum_notify_do')->willReturn($token); $request->beConstructedWith([$token]); $captureRequestFactory->createNewWithToken($token)->willReturn($request); $gateway->execute($request)->shouldBeCalled(); - $this->__invoke($payment); + $this->__invoke($payment, $event); } public function it_do_nothing_when_it_is_not_an_authorized_state( - PaymentInterface $payment + PaymentInterface $payment, + TransitionEvent $event + ): void { + $event->getState()->willReturn(PaymentInterface::STATE_COMPLETED); + + $this->__invoke($payment, $event); + } + + public function it_do_nothing_when_gateway_is_unknown( + PaymentInterface $payment, + TransitionEvent $event, + PaymentMethodInterface $paymentMethod, + GatewayConfigInterface $gatewayConfig ): void { - $payment->getState()->willReturn(PaymentInterface::STATE_COMPLETED); + $event->getState()->willReturn(PaymentInterface::STATE_AUTHORIZED); + + $payment->getMethod()->willReturn($paymentMethod); + $paymentMethod->getGatewayConfig()->willReturn($gatewayConfig); + $gatewayConfig->getConfig()->willReturn(['factory' => 'foo']); - $this->__invoke($payment); + $this->__invoke($payment, $event); } } diff --git a/spec/StateMachine/RefundOrderProcessorSpec.php b/spec/StateMachine/RefundOrderProcessorSpec.php index fd788ea..a0133a6 100644 --- a/spec/StateMachine/RefundOrderProcessorSpec.php +++ b/spec/StateMachine/RefundOrderProcessorSpec.php @@ -11,6 +11,7 @@ use Payum\Core\Security\TokenFactoryInterface; use Payum\Core\Security\TokenInterface; use PhpSpec\ObjectBehavior; +use SM\Event\TransitionEvent; use Sylius\Bundle\PayumBundle\Model\GatewayConfigInterface; use Sylius\Component\Core\Model\PaymentInterface; use Sylius\Component\Core\Model\PaymentMethodInterface; @@ -27,6 +28,7 @@ public function let( public function it_is_invokable( Payum $payum, PaymentInterface $payment, + TransitionEvent $event, PaymentMethodInterface $paymentMethod, GatewayConfigInterface $gatewayConfig, GatewayInterface $gateway, @@ -35,7 +37,7 @@ public function it_is_invokable( RefundRequestFactoryInterface $refundRequestFactory, ModelAggregateInterface $request ): void { - $payment->getState()->willReturn(PaymentInterface::STATE_COMPLETED); + $event->getState()->willReturn(PaymentInterface::STATE_COMPLETED); $payment->getMethod()->willReturn($paymentMethod); $paymentMethod->getGatewayConfig()->willReturn($gatewayConfig); @@ -46,21 +48,37 @@ public function it_is_invokable( $payum->getGateway($gatewayName)->willReturn($gateway); $payum->getTokenFactory()->willReturn($tokenFactory); - $tokenFactory->createToken($gatewayName, $payment, 'sylius_shop_order_after_pay')->willReturn($token); + $tokenFactory->createToken($gatewayName, $payment, 'payum_notify_do')->willReturn($token); $request->beConstructedWith([$token]); $refundRequestFactory->createNewWithToken($token)->willReturn($request); $gateway->execute($request)->shouldBeCalled(); - $this->__invoke($payment); + $this->__invoke($payment, $event); } public function it_do_nothing_when_it_is_not_a_completed_state( - PaymentInterface $payment + PaymentInterface $payment, + TransitionEvent $event + ): void { + $event->getState()->willReturn(PaymentInterface::STATE_AUTHORIZED); + + $this->__invoke($payment, $event); + } + + public function it_do_nothing_when_gateway_is_unknown( + PaymentInterface $payment, + TransitionEvent $event, + PaymentMethodInterface $paymentMethod, + GatewayConfigInterface $gatewayConfig ): void { - $payment->getState()->willReturn(PaymentInterface::STATE_AUTHORIZED); + $event->getState()->willReturn(PaymentInterface::STATE_COMPLETED); + + $payment->getMethod()->willReturn($paymentMethod); + $paymentMethod->getGatewayConfig()->willReturn($gatewayConfig); + $gatewayConfig->getConfig()->willReturn(['factory' => 'foo']); - $this->__invoke($payment); + $this->__invoke($payment, $event); } }