PaymentController   A
last analyzed

Complexity

Total Complexity 28

Size/Duplication

Total Lines 329
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 182
dl 0
loc 329
rs 10
c 2
b 0
f 0
wmc 28

9 Methods

Rating   Name   Duplication   Size   Complexity  
A failureAction() 0 45 4
A getPaymentUriForAction() 0 17 1
A validateHmacForAction() 0 10 2
A cancelAction() 0 45 4
A notifyAction() 0 32 2
A redirectAction() 0 50 2
B proceedWithAction() 0 26 7
A processRequest() 0 22 4
A successAction() 0 32 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Extension "sf_event_mgt" for TYPO3 CMS.
7
 *
8
 * For the full copyright and license information, please read the
9
 * LICENSE.txt file that was distributed with this source code.
10
 */
11
12
namespace DERHANSEN\SfEventMgt\Controller;
13
14
use DERHANSEN\SfEventMgt\Domain\Model\Registration;
15
use DERHANSEN\SfEventMgt\Event\ModifyPaymentRedirectResponseEvent;
16
use DERHANSEN\SfEventMgt\Event\ProceedWithPaymentActionEvent;
17
use DERHANSEN\SfEventMgt\Event\ProcessPaymentCancelEvent;
18
use DERHANSEN\SfEventMgt\Event\ProcessPaymentFailureEvent;
19
use DERHANSEN\SfEventMgt\Event\ProcessPaymentInitializeEvent;
20
use DERHANSEN\SfEventMgt\Event\ProcessPaymentNotifyEvent;
21
use DERHANSEN\SfEventMgt\Event\ProcessPaymentSuccessEvent;
22
use DERHANSEN\SfEventMgt\Exception;
23
use DERHANSEN\SfEventMgt\Payment\Exception\PaymentException;
24
use DERHANSEN\SfEventMgt\Security\HashScope;
25
use Psr\Http\Message\ResponseInterface;
26
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
0 ignored issues
show
Bug introduced by
The type TYPO3\CMS\Extbase\Mvc\RequestInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
27
use TYPO3\CMS\Extbase\Security\Exception\InvalidHashException;
0 ignored issues
show
Bug introduced by
The type TYPO3\CMS\Extbase\Securi...on\InvalidHashException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
28
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
0 ignored issues
show
Bug introduced by
The type TYPO3\CMS\Extbase\Utility\LocalizationUtility was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
29
30
class PaymentController extends AbstractController
31
{
32
    /**
33
     * Catches all PaymentExceptions and sets the Exception message to the response content
34
     */
35
    public function processRequest(RequestInterface $request): ResponseInterface
36
    {
37
        try {
38
            $response = parent::processRequest($request);
39
        } catch (Exception $e) {
40
            $response = $this->responseFactory->createResponse()
41
                ->withStatus(200)
42
                ->withHeader('Content-Type', 'text/html; charset=utf-8');
43
            $response->getBody()->write('<div class="payment-error">' . $e->getMessage() . '</div>');
44
        } catch (InvalidHashException $e) {
45
            $response = $this->responseFactory->createResponse()
46
                ->withStatus(403)
47
                ->withHeader('Content-Type', 'text/html; charset=utf-8');
48
            $response->getBody()->write('<div class="payment-error">' . $e->getMessage() . '</div>');
49
        } catch (\Exception $e) {
50
            $response = $this->responseFactory->createResponse()
51
                ->withStatus(500)
52
                ->withHeader('Content-Type', 'text/html; charset=utf-8');
53
            $response->getBody()->write('<div class="payment-error">' . $e->getMessage() . '</div>');
54
        }
55
56
        return $response;
57
    }
58
59
    /**
60
     * Redirect to payment provider
61
     */
62
    public function redirectAction(Registration $registration, string $hmac): ResponseInterface
63
    {
64
        $this->validateHmacForAction($registration, $hmac, $this->actionMethodName);
65
        $this->proceedWithAction($registration, $this->actionMethodName);
66
67
        $variables = [
68
            'sfEventMgtSettings' => $this->settings,
69
            'successUrl' => $this->getPaymentUriForAction('success', $registration),
70
            'failureUrl' => $this->getPaymentUriForAction('failure', $registration),
71
            'cancelUrl' => $this->getPaymentUriForAction('cancel', $registration),
72
            'notifyUrl' => $this->getPaymentUriForAction('notify', $registration),
73
            'registration' => $registration,
74
            'html' => '',
75
        ];
76
77
        $paymentMethod = $registration->getPaymentmethod();
78
79
        // If true, an external event listener requested the registration to be updated
80
        $updateRegistration = false;
81
82
        $processPaymentInitializeEvent = new ProcessPaymentInitializeEvent(
83
            $variables,
84
            $paymentMethod,
85
            $updateRegistration,
86
            $registration,
87
            $this,
88
            $this->request
89
        );
90
        $this->eventDispatcher->dispatch($processPaymentInitializeEvent);
91
        $variables = $processPaymentInitializeEvent->getVariables();
92
        $updateRegistration = $processPaymentInitializeEvent->getUpdateRegistration();
93
94
        if ($updateRegistration) {
95
            $this->registrationRepository->update($registration);
96
        }
97
98
        $this->view->assign('result', $variables);
99
        $response = $this->htmlResponse();
100
101
        $modifyPaymentRedirectResponseEvent = new ModifyPaymentRedirectResponseEvent(
102
            $response,
103
            $this->settings,
104
            $variables,
105
            $registration,
106
            $this,
107
            $this->request
108
        );
109
        $this->eventDispatcher->dispatch($modifyPaymentRedirectResponseEvent);
110
111
        return $modifyPaymentRedirectResponseEvent->getResponse();
112
    }
113
114
    /**
115
     * Action is called when payment was successful
116
     */
117
    public function successAction(Registration $registration, string $hmac): ResponseInterface
118
    {
119
        $this->validateHmacForAction($registration, $hmac, $this->actionMethodName);
120
        $this->proceedWithAction($registration, $this->actionMethodName);
121
122
        $variables = ['html' => ''];
123
124
        $paymentMethod = $registration->getPaymentmethod();
125
126
        // If true, an external event listener requested the registration to be updated
127
        $updateRegistration = false;
128
129
        $processPaymentSuccessEvent = new ProcessPaymentSuccessEvent(
130
            $variables,
131
            $paymentMethod,
132
            $updateRegistration,
133
            $registration,
134
            $this->request->getQueryParams(),
135
            $this,
136
            $this->request
137
        );
138
        $this->eventDispatcher->dispatch($processPaymentSuccessEvent);
139
        $variables = $processPaymentSuccessEvent->getVariables();
140
        $updateRegistration = $processPaymentSuccessEvent->getUpdateRegistration();
141
142
        if ($updateRegistration) {
143
            $this->registrationRepository->update($registration);
144
        }
145
146
        $this->view->assign('result', $variables);
147
148
        return $this->htmlResponse();
149
    }
150
151
    /**
152
     * Action is called when payment failed
153
     */
154
    public function failureAction(Registration $registration, string $hmac): ResponseInterface
155
    {
156
        $this->validateHmacForAction($registration, $hmac, $this->actionMethodName);
157
        $this->proceedWithAction($registration, $this->actionMethodName);
158
159
        $variables = ['html' => ''];
160
161
        $paymentMethod = $registration->getPaymentmethod();
162
163
        /**
164
         * Update- and remove flags
165
         */
166
        $updateRegistration = false;
167
        $removeRegistration = false;
168
169
        $processPaymentFailureEvent = new ProcessPaymentFailureEvent(
170
            $variables,
171
            $paymentMethod,
172
            $updateRegistration,
173
            $removeRegistration,
174
            $registration,
175
            $this->request->getQueryParams(),
176
            $this,
177
            $this->request
178
        );
179
        $this->eventDispatcher->dispatch($processPaymentFailureEvent);
180
        $variables = $processPaymentFailureEvent->getVariables();
181
        $updateRegistration = $processPaymentFailureEvent->getUpdateRegistration();
182
        $removeRegistration = $processPaymentFailureEvent->getRemoveRegistration();
183
184
        if ($updateRegistration) {
185
            $this->registrationRepository->update($registration);
186
        }
187
188
        if ($removeRegistration) {
189
            // First cancel depending registrations
190
            if ($registration->getAmountOfRegistrations() > 1) {
191
                $this->registrationService->cancelDependingRegistrations($registration);
192
            }
193
            $this->registrationRepository->remove($registration);
194
        }
195
196
        $this->view->assign('result', $variables);
197
198
        return $this->htmlResponse();
199
    }
200
201
    /**
202
     * Action is called, when payment was cancelled
203
     */
204
    public function cancelAction(Registration $registration, string $hmac): ResponseInterface
205
    {
206
        $this->validateHmacForAction($registration, $hmac, $this->actionMethodName);
207
        $this->proceedWithAction($registration, $this->actionMethodName);
208
209
        $variables = ['html' => ''];
210
211
        $paymentMethod = $registration->getPaymentmethod();
212
213
        /**
214
         * Update- and remove flags
215
         */
216
        $updateRegistration = false;
217
        $removeRegistration = false;
218
219
        $processPaymentCancelEvent = new ProcessPaymentCancelEvent(
220
            $variables,
221
            $paymentMethod,
222
            $updateRegistration,
223
            $removeRegistration,
224
            $registration,
225
            $this->request->getQueryParams(),
226
            $this,
227
            $this->request
228
        );
229
        $this->eventDispatcher->dispatch($processPaymentCancelEvent);
230
        $variables = $processPaymentCancelEvent->getVariables();
231
        $updateRegistration = $processPaymentCancelEvent->getUpdateRegistration();
232
        $removeRegistration = $processPaymentCancelEvent->getRemoveRegistration();
233
234
        if ($updateRegistration) {
235
            $this->registrationRepository->update($registration);
236
        }
237
238
        if ($removeRegistration) {
239
            // First cancel depending registrations
240
            if ($registration->getAmountOfRegistrations() > 1) {
241
                $this->registrationService->cancelDependingRegistrations($registration);
242
            }
243
            $this->registrationRepository->remove($registration);
244
        }
245
246
        $this->view->assign('result', $variables);
247
248
        return $this->htmlResponse();
249
    }
250
251
    /**
252
     * Action can be called by payment provider to perform custom logic after the payment process
253
     */
254
    public function notifyAction(Registration $registration, string $hmac): ResponseInterface
255
    {
256
        $this->validateHmacForAction($registration, $hmac, $this->actionMethodName);
257
        $this->proceedWithAction($registration, $this->actionMethodName);
258
259
        $variables = ['html' => ''];
260
261
        $paymentMethod = $registration->getPaymentmethod();
262
263
        // If true, an external event listener requested the registration to be updated
264
        $updateRegistration = false;
265
266
        $processPaymentNotifyEvent = new ProcessPaymentNotifyEvent(
267
            $variables,
268
            $paymentMethod,
269
            $updateRegistration,
270
            $registration,
271
            $this->request->getQueryParams(),
272
            $this,
273
            $this->request
274
        );
275
        $this->eventDispatcher->dispatch($processPaymentNotifyEvent);
276
        $variables = $processPaymentNotifyEvent->getVariables();
277
        $updateRegistration = $processPaymentNotifyEvent->getUpdateRegistration();
278
279
        if ($updateRegistration) {
280
            $this->registrationRepository->update($registration);
281
        }
282
283
        $this->view->assign('result', $variables);
284
285
        return $this->htmlResponse();
286
    }
287
288
    /**
289
     * Checks if the given action can be called for the given registration / event and throws
290
     * an exception if action should not proceed
291
     *
292
     * @throws PaymentException
293
     */
294
    protected function proceedWithAction(Registration $registration, string $actionName): void
295
    {
296
        if ($registration->getEvent()->getEnablePayment() === false) {
297
            $message = LocalizationUtility::translate('payment.messages.paymentNotEnabled', 'SfEventMgt');
298
            throw new PaymentException($message, 1899934881);
299
        }
300
301
        if ($this->paymentService->paymentActionEnabled($registration->getPaymentmethod(), $actionName) === false) {
302
            $message = LocalizationUtility::translate('payment.messages.actionNotEnabled', 'SfEventMgt');
303
            throw new PaymentException($message, 1899934882);
304
        }
305
306
        if ($registration->getEvent()->getRestrictPaymentMethods()) {
307
            $selectedPaymentMethods = explode(',', $registration->getEvent()->getSelectedPaymentMethods());
308
            if (!in_array($registration->getPaymentmethod(), $selectedPaymentMethods)) {
309
                $message = LocalizationUtility::translate('payment.messages.paymentMethodNotAvailable', 'SfEventMgt');
310
                throw new PaymentException($message, 1899934884);
311
            }
312
        }
313
314
        $proceedWithPaymentActionEvent = new ProceedWithPaymentActionEvent($registration, $actionName, $this->request);
315
        $this->eventDispatcher->dispatch($proceedWithPaymentActionEvent);
316
317
        if ($proceedWithPaymentActionEvent->getPerformPaidCheck() && $registration->getPaid()) {
318
            $message = LocalizationUtility::translate('payment.messages.paymentAlreadyProcessed', 'SfEventMgt');
319
            throw new PaymentException($message, 1899934883);
320
        }
321
    }
322
323
    /**
324
     * Checks the HMAC for the given action and registration
325
     */
326
    protected function validateHmacForAction(Registration $registration, string $hmac, string $action): void
327
    {
328
        $isValidHmac = $this->hashService->validateHmac(
329
            $action . '-' . $registration->getUid(),
330
            HashScope::PaymentAction->value,
331
            $hmac
332
        );
333
        if (!$isValidHmac) {
334
            $message = LocalizationUtility::translate('payment.messages.invalidHmac', 'SfEventMgt');
335
            throw new InvalidHashException($message, 1899934890);
336
        }
337
    }
338
339
    /**
340
     * Returns the payment Uri for the given action and registration
341
     */
342
    protected function getPaymentUriForAction(string $action, Registration $registration): string
343
    {
344
        $this->uriBuilder
345
            ->setCreateAbsoluteUri(true);
346
347
        return $this->uriBuilder->uriFor(
348
            $action,
349
            [
350
                'registration' => $registration,
351
                'hmac' => $this->hashService->hmac(
352
                    $action . 'Action-' . $registration->getUid(),
353
                    HashScope::PaymentAction->value
354
                ),
355
            ],
356
            'Payment',
357
            'sfeventmgt',
358
            'Pipayment'
359
        );
360
    }
361
}
362