Completed
Push — master ( d7a535...6374f5 )
by
unknown
02:29
created

PaymentController::successResponse()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 1
1
<?php
2
namespace Cardinity\ClientBundle\Controller;
3
4
use Cardinity\Client;
5
use Cardinity\ClientBundle\Form\CreditCardType;
6
use Cardinity\Exception;
7
use Cardinity\Method\Payment;
8
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
9
use Symfony\Component\Form\Form;
10
use Symfony\Component\Form\FormFactoryInterface;
11
use Symfony\Component\HttpFoundation\RedirectResponse;
12
use Symfony\Component\HttpFoundation\Request;
13
use Symfony\Component\HttpFoundation\Response;
14
use Symfony\Component\HttpFoundation\Session\SessionInterface;
15
use Symfony\Component\Routing\RouterInterface;
16
17
class PaymentController
18
{
19
    /** @var EngineInterface */
20
    private $templating;
21
22
    /** @var RouterInterface */
23
    private $router;
24
25
    /** @var FormFactoryInterface */
26
    private $formFactory;
27
28
    /** @var SessionInterface */
29
    private $session;
30
31
    /** @var Client */
32
    private $payment;
33
34
    /**
35
     * @param EngineInterface $templating
36
     * @param RouterInterface $router
37
     * @param FormFactoryInterface $formFactory
38
     * @param SessionInterface $session
39
     * @param Client $payment
40
     */
41
    public function __construct(
42
        EngineInterface $templating,
43
        RouterInterface $router,
44
        FormFactoryInterface $formFactory,
45
        SessionInterface $session,
46
        Client $payment
47
    ) {
48
        $this->templating = $templating;
49
        $this->router = $router;
50
        $this->formFactory = $formFactory;
51
        $this->session = $session;
52
        $this->payment = $payment;
53
    }
54
55
    /**
56
     * Dummy page to start payment
57
     * @return Response
58
     */
59
    public function indexAction()
60
    {
61
        return $this->templating->renderResponse(
62
            'CardinityClientBundle:Payment:index.html.twig'
63
        );
64
    }
65
66
    /**
67
     * Credit card details for
68
     * @return Response
69
     */
70
    public function detailsAction()
71
    {
72
        $form = $this->createForm();
73
74
        return $this->renderForm($form);
75
    }
76
77
    /**
78
     * Process credit card
79
     * @param Request $request
80
     * @return Response
81
     */
82
    public function processAction(Request $request)
83
    {
84
        $form = $this->createForm();
85
        $form->handleRequest($request);
86
87
        if ($form->isValid()) {
88
            /** @var array dummy payment params */
89
            $params = $this->getPaymentParams();
90
            $params['payment_instrument'] = $form->getData();
91
92
            $method = new Payment\Create($params);
93
            try {
94
                /** @var Cardinity\Method\Payment\Payment */
95
                $payment = $this->payment->call($method);
96
                if ($payment->isPending()) {
97
                    $this->session->set('cardinity_payment', $payment->serialize());
98
99
                    return new RedirectResponse(
100
                        $this->router->generate('cardinity_client.payment_begin_authorization')
101
                    );
102
                } elseif ($payment->isApproved()) {
103
                    return $this->successResponse($payment);
104
                }
105
            } catch (Exception\Declined $e) {
106
                return $this->errorResponse('Payment declined: ' . print_r($e->getErrors(), true));
107
            } catch (Exception\Runtime $e) {
108
                return $this->errorResponse('Unexpected error occured: ' . print_r($e, true));
109
            };
110
        }
111
112
        return $this->renderForm($form);
113
    }
114
115
    /**
116
     * 3-D secure form page
117
     * @return Response
118
     */
119
    public function beginAuthorizationAction()
120
    {
121
        if (!$this->session->has('cardinity_payment')) {
122
            return $this->errorResponse('Session expired.');
123
        }
124
125
        $payment = new Payment\Payment();
126
        $payment->unserialize($this->session->get('cardinity_payment'));
127
128
        return $this->templating->renderResponse(
129
            'CardinityClientBundle:Payment:begin_authorization.html.twig',
130
            [
131
                'auth' => $payment->getAuthorizationInformation(),
132
                'callbackUrl' => $this->router->generate('cardinity_client.payment_process_authorization', [], RouterInterface::ABSOLUTE_URL),
133
                'identifier' => $payment->getOrderId(),
134
            ]
135
        );
136
    }
137
138
    /**
139
     * 3-D secure callback action
140
     * @param Request $request
141
     * @return Response
142
     */
143
    public function processAuthorizationAction(Request $request)
144
    {
145
        $identifier = $request->request->get('MD');
146
        $pares = $request->request->get('PaRes');
147
148
        $payment = new Payment\Payment();
149
        $payment->unserialize($this->session->get('cardinity_payment'));
150
151
        if ($payment->getOrderId() != $identifier) {
152
            return $this->errorResponse('Invalid callback data');
153
        }
154
        try {
155
            if ($payment->isPending()) {
156
                $method = new Payment\Finalize(
157
                    $payment->getId(),
158
                    $pares
159
                );
160
                /** @var Cardinity\Method\Payment\Payment */
161
                $payment = $this->payment->call($method);
162
            }
163
            
164
            if ($payment->isApproved()) {
165
                return new RedirectResponse($this->router->generate('cardinity_client.payment_success'));
166
            }
167
        } catch (Exception\Runtime $e) {
168
            return $this->errorResponse('Unexpected error occured. ' . $e->getMessage() . ': ' . print_r($e->getErrors(), true));
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Cardinity\Exception\Runtime as the method getErrors() does only exist in the following sub-classes of Cardinity\Exception\Runtime: Cardinity\Exception\Declined, Cardinity\Exception\Forbidden, Cardinity\Exception\InternalServerError, Cardinity\Exception\MethodNotAllowed, Cardinity\Exception\NotAcceptable, Cardinity\Exception\NotFound, Cardinity\Exception\Request, Cardinity\Exception\RequestTimeout, Cardinity\Exception\ServiceUnavailable, Cardinity\Exception\Unauthorized, Cardinity\Exception\UnexpectedResponse, Cardinity\Exception\ValidationFailed. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

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

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
169
        };
170
171
        return $this->errorResponse('Unexpected response while finalizing payment');
172
    }
173
174
    /**
175
     * Dummy success page
176
     * @return Response
177
     */
178
    public function successAction()
179
    {
180
        if (!$this->session->has('cardinity_payment')) {
181
            return $this->errorResponse('Session expired.');
182
        }
183
184
        $payment = new Payment\Payment();
185
        $payment->unserialize($this->session->get('cardinity_payment'));
186
        $this->session->remove('cardinity_payment');
187
188
        return $this->successResponse($payment);
189
    }
190
191
    private function renderForm(Form $form)
192
    {
193
        return $this->templating->renderResponse(
194
            'CardinityClientBundle:Payment:details.html.twig',
195
            ['form' => $form->createView()]
196
        );
197
    }
198
199
    private function createForm()
200
    {
201
        return $this->formFactory->create(new CreditCardType(), null, [
202
            'action' => $this->router->generate('cardinity_client.payment_process'),
203
        ]);
204
    }
205
206
    private function getPaymentParams()
207
    {
208
        return [
209
            'amount' => 50.00,
210
            'currency' => 'EUR',
211
            'settle' => false,
212
            'description' => '3d-pass',
213
            'order_id' => '123456',
214
            'country' => 'LT',
215
            'payment_method' => Payment\Create::CARD,
216
            'payment_instrument' => []
217
        ];
218
    }
219
220
    private function successResponse($payment)
221
    {
222
        return $this->templating->renderResponse(
223
            'CardinityClientBundle:Payment:success.html.twig',
224
            ['payment' => $payment]
225
        );
226
    }
227
228
    private function errorResponse($message)
229
    {
230
        $content = $this->templating->render(
231
            'CardinityClientBundle:Payment:error.html.twig',
232
            ['message' => $message]
233
        );
234
235
        return new Response($content, 400);
236
    }
237
}
238