Completed
Push — master ( 10cdad...b32cee )
by Kamil
16:22 queued 08:25
created

PayumController   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 146
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
wmc 11
lcom 1
cbo 10
dl 0
loc 146
c 0
b 0
f 0
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 19 1
B prepareCaptureAction() 0 26 3
A afterCaptureAction() 0 22 2
A getTokenFactory() 0 4 1
A getHttpRequestVerifier() 0 4 1
B provideTokenBasedOnPayment() 0 27 3
1
<?php
2
3
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Sylius\Bundle\PayumBundle\Controller;
15
16
use FOS\RestBundle\View\View;
17
use Payum\Core\Model\GatewayConfigInterface;
18
use Payum\Core\Payum;
19
use Payum\Core\Security\GenericTokenFactoryInterface;
20
use Payum\Core\Security\HttpRequestVerifierInterface;
21
use Payum\Core\Security\TokenInterface;
22
use Sylius\Bundle\PayumBundle\Factory\GetStatusFactoryInterface;
23
use Sylius\Bundle\PayumBundle\Factory\ResolveNextRouteFactoryInterface;
24
use Sylius\Bundle\ResourceBundle\Controller\RequestConfigurationFactoryInterface;
25
use Sylius\Bundle\ResourceBundle\Controller\ViewHandlerInterface;
26
use Sylius\Component\Core\Model\OrderInterface;
27
use Sylius\Component\Order\Repository\OrderRepositoryInterface;
28
use Sylius\Component\Payment\Model\PaymentInterface;
29
use Sylius\Component\Resource\Metadata\MetadataInterface;
30
use Symfony\Component\HttpFoundation\RedirectResponse;
31
use Symfony\Component\HttpFoundation\Request;
32
use Symfony\Component\HttpFoundation\Response;
33
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
34
use Symfony\Component\Routing\RouterInterface;
35
36
final class PayumController
37
{
38
    /**
39
     * @var Payum
40
     */
41
    private $payum;
42
43
    /**
44
     * @var OrderRepositoryInterface
45
     */
46
    private $orderRepository;
47
48
    /**
49
     * @var MetadataInterface
50
     */
51
    private $orderMetadata;
52
53
    /**
54
     * @var RequestConfigurationFactoryInterface
55
     */
56
    private $requestConfigurationFactory;
57
58
    /**
59
     * @var ViewHandlerInterface
60
     */
61
    private $viewHandler;
62
63
    /**
64
     * @var RouterInterface
65
     */
66
    private $router;
67
68
    /** @var GetStatusFactoryInterface */
69
    private $getStatusRequestFactory;
70
71
    /** @var ResolveNextRouteFactoryInterface */
72
    private $resolveNextRouteRequestFacotry;
73
74
    public function __construct(
75
        Payum $payum,
76
        OrderRepositoryInterface $orderRepository,
77
        MetadataInterface $orderMetadata,
78
        RequestConfigurationFactoryInterface $requestConfigurationFactory,
79
        ViewHandlerInterface $viewHandler,
80
        RouterInterface $router,
81
        GetStatusFactoryInterface $getStatusFactory,
82
        ResolveNextRouteFactoryInterface $resolveNextRouteFactory
83
    ) {
84
        $this->payum = $payum;
85
        $this->orderRepository = $orderRepository;
86
        $this->orderMetadata = $orderMetadata;
87
        $this->requestConfigurationFactory = $requestConfigurationFactory;
88
        $this->viewHandler = $viewHandler;
89
        $this->router = $router;
90
        $this->getStatusRequestFactory = $getStatusFactory;
91
        $this->resolveNextRouteRequestFacotry = $resolveNextRouteFactory;
92
    }
93
94
    public function prepareCaptureAction(Request $request, $tokenValue): Response
95
    {
96
        $configuration = $this->requestConfigurationFactory->create($this->orderMetadata, $request);
97
98
        /** @var OrderInterface $order */
99
        $order = $this->orderRepository->findOneByTokenValue($tokenValue);
100
101
        if (null === $order) {
102
            throw new NotFoundHttpException(sprintf('Order with token "%s" does not exist.', $tokenValue));
103
        }
104
105
        $request->getSession()->set('sylius_order_id', $order->getId());
106
        $payment = $order->getLastPayment(PaymentInterface::STATE_NEW);
107
108
        if (null === $payment) {
109
            $url = $this->router->generate('sylius_shop_order_thank_you');
110
111
            return new RedirectResponse($url);
112
        }
113
114
        $token = $this->provideTokenBasedOnPayment($payment, $configuration->getParameters()->get('redirect'));
115
116
        $view = View::createRedirect($token->getTargetUrl());
117
118
        return $this->viewHandler->handle($configuration, $view);
119
    }
120
121
    public function afterCaptureAction(Request $request): Response
122
    {
123
        $configuration = $this->requestConfigurationFactory->create($this->orderMetadata, $request);
124
125
        $token = $this->getHttpRequestVerifier()->verify($request);
126
127
        $status = $this->getStatusRequestFactory->createNewWithModel($token);
128
        $this->payum->getGateway($token->getGatewayName())->execute($status);
129
        $resolveNextRoute = $this->resolveNextRouteRequestFacotry->createNewWithModel($status->getFirstModel());
130
        $this->payum->getGateway($token->getGatewayName())->execute($resolveNextRoute);
131
132
        $this->getHttpRequestVerifier()->invalidate($token);
133
134
        if (PaymentInterface::STATE_NEW !== $status->getValue()) {
135
            $request->getSession()->getBag('flashes')->add('info', sprintf('sylius.payment.%s', $status->getValue()));
136
        }
137
138
        return $this->viewHandler->handle(
139
            $configuration,
140
            View::createRouteRedirect($resolveNextRoute->getRouteName(), $resolveNextRoute->getRouteParameters())
141
        );
142
    }
143
144
    private function getTokenFactory(): GenericTokenFactoryInterface
145
    {
146
        return $this->payum->getTokenFactory();
147
    }
148
149
    private function getHttpRequestVerifier(): HttpRequestVerifierInterface
150
    {
151
        return $this->payum->getHttpRequestVerifier();
152
    }
153
154
    private function provideTokenBasedOnPayment(PaymentInterface $payment, array $redirectOptions): TokenInterface
155
    {
156
        /** @var GatewayConfigInterface $gatewayConfig */
157
        $gatewayConfig = $payment->getMethod()->getGatewayConfig();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Sylius\Component\Payment...\PaymentMethodInterface as the method getGatewayConfig() does only exist in the following implementations of said interface: Sylius\Component\Core\Model\PaymentMethod.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
158
159
        if (isset($gatewayConfig->getConfig()['use_authorize']) && $gatewayConfig->getConfig()['use_authorize'] == true) {
160
            $token = $this->getTokenFactory()->createAuthorizeToken(
161
                $gatewayConfig->getGatewayName(),
162
                $payment,
163
                $redirectOptions['route']
164
                    ?? null,
165
                $redirectOptions['parameters']
166
                    ?? []
167
            );
168
        } else {
169
            $token = $this->getTokenFactory()->createCaptureToken(
170
                $gatewayConfig->getGatewayName(),
171
                $payment,
172
                $redirectOptions['route']
173
                    ?? null,
174
                $redirectOptions['parameters']
175
                    ?? []
176
            );
177
        }
178
179
        return $token;
180
    }
181
}
182