Skip to content

Commit

Permalink
feature #11863 [API]Payment Method integrity check (arti0090)
Browse files Browse the repository at this point in the history
This PR was merged into the 1.9-dev branch.

Discussion
----------

| Q               | A
| --------------- | -----
| Branch?         | master
| Bug fix?        | no
| New feature?    | yes
| BC breaks?      | no
| Deprecations?   | no
| License         | MIT


Commits
-------

7bd768e Payment Method integrity check
8265dbd added spec and changed validator for to be used on command
298d4ba fixed psalm issues
58038a7 fixed specs after changing class used in validator
2b4dff3 fix styling issues
3f7b8ae Change names of validators and move validators to one file
  • Loading branch information
GSadee committed Sep 22, 2020
2 parents a0fd383 + 3f7b8ae commit 34c4668
Show file tree
Hide file tree
Showing 7 changed files with 275 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ Feature: Order payment method integrity
And the store allows paying offline
And I am a logged in customer

@ui
@ui @api
Scenario: Preventing customer from completing checkout with no longer available payment method
Given I have product "PHP T-Shirt" in the cart
And I have proceeded selecting "Offline" payment method
But this payment method has been disabled
But the payment method "Offline" is disabled
When I confirm my order
Then I should be informed that this payment method has been disabled
And I should not see the thank you page
22 changes: 21 additions & 1 deletion src/Sylius/Behat/Context/Api/Shop/CheckoutContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ public function iProvideAdditionalNotesLike(string $notes): void
*/
public function iConfirmMyOrder(): void
{
$notes = isset($this->content['additionalNote']) ? $this->content['additionalNote'] : null;
$notes = $this->content['additionalNote'] ?? null;

$this->client->request(
Request::METHOD_PATCH,
Expand Down Expand Up @@ -289,6 +289,7 @@ public function iCompleteTheShippingStepWithFirstShippingMethod(): void
/**
* @When I choose :paymentMethod payment method
* @When I select :paymentMethod payment method
* @When I have proceeded selecting :paymentMethod payment method
* @When /^the (?:customer|visitor) proceed with ("[^"]+" payment)$/
* @Given /^the (?:customer|visitor) has proceeded ("[^"]+" payment)$/
* @Given /^the visitor try to proceed with ("[^"]+" payment) in the customer cart$/
Expand Down Expand Up @@ -660,6 +661,25 @@ public function iShouldSeeInTheBillingAddress(string $provinceName, string $addr
$this->hasProvinceNameInAddress($provinceName, $addressType);
}

/**
* @Then /^I should be informed that (this payment method) has been disabled$/
*/
public function iShouldBeInformedThatThisPaymentMethodHasBeenDisabled(PaymentMethodInterface $paymentMethod): void
{
/** @var Response $response */
$response = $this->client->getResponse();

Assert::same($response->getStatusCode(), 400);

Assert::true($this->isViolationWithMessageInResponse(
$response,
sprintf(
'This payment method %s has been disabled. Please reselect your payment method.',
$paymentMethod->getName()
)
));
}

private function isViolationWithMessageInResponse(Response $response, string $message): bool
{
$violations = $this->responseChecker->getResponseContent($response)['violations'];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,10 @@
<argument type="service" id="sylius.shipping_eligibility_checker" />
<tag name="validator.constraint_validator" alias="sylius_api_validator_order_shipping_method_eligibility" />
</service>

<service id="sylius.api.validator.payment_method_eligibility" class="Sylius\Bundle\ApiBundle\Validator\Constraints\OrderPaymentMethodEligibilityValidator">
<argument type="service" id="sylius.repository.order" />
<tag name="validator.constraint_validator" alias="sylius_api_order_payment_method_eligibility" />
</service>
</services>
</container>
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@
<value>sylius_checkout_complete</value>
</option>
</constraint>
<constraint name="Sylius\Bundle\ApiBundle\Validator\Constraints\OrderPaymentMethodEligibility">
<option name="message">sylius.order.payment_method_eligibility</option>
<option name="groups">
<value>sylius_checkout_complete</value>
</option>
</constraint>
<constraint name="Sylius\Bundle\ApiBundle\Validator\Constraints\OrderShippingMethodEligibility">
<option name="message">sylius.order.shipping_method_eligibility</option>
<option name="groups">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Bundle\ApiBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

/** @experimental */
final class OrderPaymentMethodEligibility extends Constraint
{
/** @var string */
public $message = 'sylius.order.payment_method_eligibility';

public function validatedBy(): string
{
return 'sylius_api_order_payment_method_eligibility';
}

public function getTargets(): string
{
return self::CLASS_CONSTRAINT;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Bundle\ApiBundle\Validator\Constraints;

use Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PaymentInterface;
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Webmozart\Assert\Assert;

/** @experimental */
final class OrderPaymentMethodEligibilityValidator extends ConstraintValidator
{
/** @var OrderRepositoryInterface */
private $orderRepository;

public function __construct(OrderRepositoryInterface $orderRepository)
{
$this->orderRepository = $orderRepository;
}

public function validate($command, Constraint $constraint): void
{
Assert::isInstanceOf($command, OrderTokenValueAwareInterface::class);

/** @var OrderPaymentMethodEligibility $constraint */
Assert::isInstanceOf($constraint, OrderPaymentMethodEligibility::class);

/** @var OrderInterface $order */
Assert::notNull($order = $this->orderRepository->findOneBy(['tokenValue' => $command->getOrderTokenValue()]));

/** @var PaymentInterface $payment */
foreach($order->getPayments() as $payment) {
if (!$payment->getMethod()->isEnabled()) {
$this->context->addViolation(
$constraint->message,
['%paymentMethodName%' => $payment->getMethod()->getName()]
);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace spec\Sylius\Bundle\ApiBundle\Validator\Constraints;

use Doctrine\Common\Collections\ArrayCollection;
use PhpSpec\ObjectBehavior;
use Sylius\Bundle\ApiBundle\Command\Checkout\CompleteOrder;
use Sylius\Bundle\ApiBundle\Command\OrderTokenValueAwareInterface;
use Sylius\Bundle\ApiBundle\Validator\Constraints\OrderPaymentMethodEligibility;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\Model\PaymentInterface;
use Sylius\Component\Core\Model\PaymentMethodInterface;
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidatorInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

final class OrderPaymentMethodEligibilityValidatorSpec extends ObjectBehavior
{
function let(OrderRepositoryInterface $orderRepository): void
{
$this->beConstructedWith($orderRepository);
}

function it_is_a_constraint_validator(): void
{
$this->shouldImplement(ConstraintValidatorInterface::class);
}

function it_throws_an_exception_if_constraint_does_not_extend_order_token_value_aware_interface(): void
{
$this
->shouldThrow(\InvalidArgumentException::class)
->during('validate', ['', new class() extends Constraint {}])
;
}

function it_throws_an_exception_if_constraint_does_not_type_of_order_shipping_method_eligibility(): void {
$constraint = new class() extends Constraint implements OrderTokenValueAwareInterface {
private $orderTokenValue;

function getOrderTokenValue(): ?string
{
return 'abc';
}

function setOrderTokenValue(?string $orderTokenValue): void
{
$this->orderTokenValue = $orderTokenValue;
}
};

$this
->shouldThrow(\InvalidArgumentException::class)
->during('validate', ['', $constraint])
;
}

function it_throws_an_exception_if_order_is_null(OrderRepositoryInterface $orderRepository): void
{
$constraint = new OrderPaymentMethodEligibility();

$value = new CompleteOrder();
$value->setOrderTokenValue('token');

$orderRepository->findOneBy(['tokenValue' => 'token'])->willReturn(null);

$this
->shouldThrow(\InvalidArgumentException::class)
->during('validate', [$value, $constraint])
;
}

function it_adds_violation_if_payment_is_not_available_anymore(
OrderRepositoryInterface $orderRepository,
OrderInterface $order,
PaymentInterface $payment,
PaymentMethodInterface $paymentMethod,
ExecutionContextInterface $executionContext
): void {
$this->initialize($executionContext);

$constraint = new OrderPaymentMethodEligibility();

$value = new CompleteOrder();
$value->setOrderTokenValue('token');

$orderRepository->findOneBy(['tokenValue' => 'token'])->willReturn($order);

$order->getPayments()->willReturn(new ArrayCollection([$payment->getWrappedObject()]));

$payment->getMethod()->willReturn($paymentMethod);

$paymentMethod->getName()->willReturn('bank transfer');

$paymentMethod->isEnabled()->willReturn(false);

$executionContext
->addViolation(
"sylius.order.payment_method_eligibility",
["%paymentMethodName%" => 'bank transfer']
)
->shouldBeCalled()
;

$this->validate($value, $constraint);
}

function it_does_not_add_violation_if_payment_is_available(
OrderRepositoryInterface $orderRepository,
OrderInterface $order,
PaymentInterface $payment,
PaymentMethodInterface $paymentMethod,
ExecutionContextInterface $executionContext
): void {
$this->initialize($executionContext);

$constraint = new OrderPaymentMethodEligibility();

$value = new CompleteOrder();
$value->setOrderTokenValue('token');

$orderRepository->findOneBy(['tokenValue' => 'token'])->willReturn($order);

$order->getPayments()->willReturn(new ArrayCollection([$payment->getWrappedObject()]));

$payment->getMethod()->willReturn($paymentMethod);

$paymentMethod->getName()->willReturn('bank transfer');
$paymentMethod->isEnabled()->willReturn(true);

$executionContext
->addViolation(
"sylius.order.payment_method_eligibility",
["%paymentMethodName%" => 'bank transfer']
)
->shouldNotBeCalled()
;

$this->validate($value, $constraint);
}
}

0 comments on commit 34c4668

Please sign in to comment.