Completed
Push — master ( dfcd91...38e980 )
by Joachim
02:37
created

PaymentController   B

Complexity

Total Complexity 23

Size/Duplication

Total Lines 241
Duplicated Lines 5.39 %

Coupling/Cohesion

Components 1
Dependencies 18

Importance

Changes 0
Metric Value
wmc 23
lcom 1
cbo 18
dl 13
loc 241
rs 7.3333
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A indexAction() 0 13 1
A showAction() 0 11 2
B newAction() 0 43 4
A bulkPaymentAction() 0 14 3
A captureAction() 0 19 4
B refundAction() 0 26 5
A redirectToAltapayPaymentAction() 0 8 1
A getPaymentFromId() 13 13 2
A getPaymentHandler() 0 4 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace Loevgaard\DandomainAltapayBundle\Controller;
4
5
use Loevgaard\Dandomain\Pay\Helper\ChecksumHelper;
6
use Loevgaard\DandomainAltapayBundle\Annotation\LogHttpTransaction;
7
use Loevgaard\DandomainAltapayBundle\Entity\Payment;
8
use Loevgaard\DandomainAltapayBundle\Event\PaymentCreated;
9
use Loevgaard\DandomainAltapayBundle\Exception\AltapayPaymentRequestException;
10
use Loevgaard\DandomainAltapayBundle\Exception\ChecksumMismatchException;
11
use Loevgaard\DandomainAltapayBundle\Exception\PaymentException;
12
use Loevgaard\DandomainAltapayBundle\Exception\TerminalNotFoundException;
13
use Loevgaard\DandomainAltapayBundle\Handler\PaymentHandler;
14
use Loevgaard\DandomainAltapayBundle\PayloadGenerator\PaymentRequestPayloadGenerator;
15
use Loevgaard\DandomainAltapayBundle\PsrHttpMessage\DiactorosTrait;
16
use Loevgaard\DandomainAltapayBundle\Translation\TranslatorTrait;
17
use Money\Currency;
18
use Money\Money;
19
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
20
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
21
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
22
use Symfony\Component\HttpFoundation\RedirectResponse;
23
use Symfony\Component\HttpFoundation\Request;
24
use Symfony\Component\HttpFoundation\Response;
25
26
/**
27
 * @Route("/payment")
28
 */
29
class PaymentController extends Controller
30
{
31
    use TranslatorTrait;
32
    use DiactorosTrait;
33
34
    /**
35
     * @Method("GET")
36
     * @Route("", name="loevgaard_dandomain_altapay_payment_index")
37
     *
38
     * @param Request $request
39
     *
40
     * @return Response
41
     */
42
    public function indexAction(Request $request)
43
    {
44
        $paymentRepository = $this->container->get('loevgaard_dandomain_altapay.payment_repository');
45
46
        /** @var Payment[] $payments */
47
        $payments = $paymentRepository->findAllWithPaging($request->query->getInt('page', 1), 100, [
48
            'e.id' => 'desc'
49
        ]);
50
51
        return $this->render('@LoevgaardDandomainAltapay/payment/index.html.twig', [
52
            'payments' => $payments,
53
        ]);
54
    }
55
56
    /**
57
     * @Method("GET")
58
     * @Route("/{paymentId}/show", name="loevgaard_dandomain_altapay_payment_show")
59
     *
60
     * @param int $paymentId
61
     *
62
     * @return Response
63
     */
64
    public function showAction(int $paymentId)
65
    {
66
        $payment = $this->getPaymentFromId($paymentId);
67
        if (!$payment) {
68
            throw $this->createNotFoundException('Payment with id `'.$paymentId.'` not found');
69
        }
70
71
        return $this->render('@LoevgaardDandomainAltapay/payment/show.html.twig', [
72
            'payment' => $payment,
73
        ]);
74
    }
75
76
    /**
77
     * Payment flow
78
     * 1. The Dandomain payment API POSTs to this page with the terminal slug in the URL
79
     * 2. After validating all input, we create a payment request to the Altapay API
80
     * 3. Finally we redirect the user to the URL given by the Altapay API.
81
     *
82
     * @Method("POST")
83
     * @Route("/{terminal}", name="loevgaard_dandomain_altapay_payment_new")
84
     *
85
     * @LogHttpTransaction()
86
     *
87
     * @param string  $terminal
88
     * @param Request $request
89
     *
90
     * @return RedirectResponse
91
     *
92
     * @throws PaymentException
93
     */
94
    public function newAction(string $terminal, Request $request)
95
    {
96
        $terminalRepository = $this->container->get('loevgaard_dandomain_altapay.terminal_repository');
97
        $paymentRepository = $this->container->get('loevgaard_dandomain_altapay.payment_repository');
98
        $eventRepository = $this->container->get('loevgaard_dandomain_altapay.event_repository');
99
        $translator = $this->getTranslator($this->container);
100
101
        $psrRequest = $this->createPsrRequest($request);
102
        /** @var Payment $paymentEntity */
103
        $paymentEntity = Payment::createFromRequest($psrRequest);
104
105
        $checksumHelper = new ChecksumHelper(
106
            $paymentEntity,
107
            $this->container->getParameter('loevgaard_dandomain_altapay.shared_key_1'),
108
            $this->container->getParameter('loevgaard_dandomain_altapay.shared_key_2')
109
        );
110
111
        $paymentRepository->save($paymentEntity);
112
113
        $event = $eventRepository->createFromDomainEvent(new PaymentCreated($paymentEntity));
114
        $eventRepository->save($event);
115
116
        $terminalEntity = $terminalRepository->findTerminalBySlug($terminal, true);
117
        if (!$terminalEntity) {
118
            throw TerminalNotFoundException::create($translator->trans('payment.exception.terminal_not_found', ['%terminal%' => $terminal], 'LoevgaardDandomainAltapayBundle'), $request, $paymentEntity);
119
        }
120
121
        if (!$checksumHelper->checksumMatches()) {
122
            throw ChecksumMismatchException::create($translator->trans('payment.exception.checksum_mismatch', [], 'LoevgaardDandomainAltapayBundle'), $request, $paymentEntity);
123
        }
124
125
        $paymentRequestPayloadGenerator = new PaymentRequestPayloadGenerator($this->container->get('router'), $paymentEntity, $terminalEntity, $paymentEntity, $checksumHelper, $this->container->getParameter('loevgaard_dandomain_altapay.cookie_payment_id'), $this->container->getParameter('loevgaard_dandomain_altapay.cookie_checksum_complete'));
126
        $paymentRequestPayload = $paymentRequestPayloadGenerator->generate();
127
128
        $altapay = $this->container->get('loevgaard_dandomain_altapay.altapay_client');
129
        $response = $altapay->createPaymentRequest($paymentRequestPayload);
130
131
        if (!$response->isSuccessful()) {
132
            throw AltapayPaymentRequestException::create($translator->trans('payment.exception.altapay_payment_request', ['%gateway_message%' => $response->getErrorMessage()], 'LoevgaardDandomainAltapayBundle'), $request, $paymentEntity);
133
        }
134
135
        return $this->redirect($response->getUrl());
136
    }
137
138
    /**
139
     * @Method("POST")
140
     * @Route("/bulk", name="loevgaard_dandomain_altapay_payment_bulk")
141
     *
142
     * @param Request $request
143
     *
144
     * @return RedirectResponse
145
     */
146
    public function bulkPaymentAction(Request $request)
147
    {
148
        $op = $request->request->getAlpha('bulkOperation');
149
        $paymentRepository = $this->container->get('loevgaard_dandomain_altapay.payment_repository');
150
        $payments = $paymentRepository->findByIds($request->request->get('payments', []));
151
152
        if($op === 'capture') {
153
            foreach ($payments as $payment) {
0 ignored issues
show
Unused Code introduced by
This foreach statement is empty and can be removed.

This check looks for foreach loops that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

Consider removing the loop.

Loading history...
154
                // @todo capture $payment
155
            }
156
        }
157
158
        return $this->redirectToRoute('loevgaard_dandomain_altapay_payment_index');
159
    }
160
161
    /**
162
     * @Method("GET")
163
     * @Route("/{paymentId}/capture", name="loevgaard_dandomain_altapay_payment_capture")
164
     *
165
     * @param int     $paymentId
166
     * @param Request $request
167
     *
168
     * @return RedirectResponse
169
     */
170
    public function captureAction(int $paymentId, Request $request)
171
    {
172
        $payment = $this->getPaymentFromId($paymentId);
173
174
        if ($payment) {
175
            $paymentHandler = $this->getPaymentHandler();
176
            $res = $paymentHandler->capture($payment, $request->query->get('amount'));
177
178
            if ($res->isSuccessful()) {
179
                $this->addFlash('success', 'The payment for order '.$payment->getOrderId().' was captured.'); // @todo fix translation
180
            } else {
181
                $this->addFlash('danger', 'An error occurred during capture of the payment: '.$res->getErrorMessage()); // @todo fix translation
182
            }
183
        }
184
185
        $redirect = $request->headers->get('referer') ? $request->headers->get('referer') : $this->generateUrl('loevgaard_dandomain_altapay_payment_index');
186
187
        return $this->redirect($redirect);
0 ignored issues
show
Bug introduced by
It seems like $redirect defined by $request->headers->get('...altapay_payment_index') on line 185 can also be of type array; however, Symfony\Bundle\Framework...rollerTrait::redirect() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
188
    }
189
190
    /**
191
     * @Method({"POST", "GET"})
192
     * @Route("/{paymentId}/refund", name="loevgaard_dandomain_altapay_payment_refund")
193
     *
194
     * @param int     $paymentId
195
     * @param Request $request
196
     *
197
     * @return RedirectResponse
198
     */
199
    public function refundAction(int $paymentId, Request $request)
200
    {
201
        $payment = $this->getPaymentFromId($paymentId);
202
203
        if ($payment) {
204
            $paymentHandler = $this->getPaymentHandler();
205
206
            // @todo this is ugly. Should be put somewhere else, maybe on the Payment entity. Maybe the createMoney*() methods on the payment entity should be public so it's easy to create Money objects based on the Payments currency
207
            $amount = (float) $request->query->get('amount');
208
            if ($amount) {
209
                $amount = new Money($amount * 100, new Currency($payment->getMerchantCurrencyAlpha()));
210
            }
211
212
            $res = $paymentHandler->refund($payment, null, $amount);
0 ignored issues
show
Bug introduced by
It seems like $amount defined by (double) $request->query->get('amount') on line 207 can also be of type double; however, Loevgaard\DandomainAltap...aymentHandler::refund() does only seem to accept null|object<Money\Money>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
213
214
            if ($res->isSuccessful()) {
215
                $this->addFlash('success', 'The payment for order '.$payment->getOrderId().' was refunded.'); // @todo fix translation
216
            } else {
217
                $this->addFlash('danger', 'An error occurred during refund of the payment: '.$res->getErrorMessage()); // @todo fix translation
218
            }
219
        }
220
221
        $redirect = $request->headers->get('referer') ?: $this->generateUrl('loevgaard_dandomain_altapay_payment_index');
222
223
        return $this->redirect($redirect);
0 ignored issues
show
Bug introduced by
It seems like $redirect defined by $request->headers->get('...altapay_payment_index') on line 221 can also be of type array; however, Symfony\Bundle\Framework...rollerTrait::redirect() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
224
    }
225
226
    /**
227
     * @Method("GET")
228
     * @Route("/{paymentId}/redirectToAltapay", name="loevgaard_dandomain_altapay_redirect_to_altapay_payment")
229
     *
230
     * @param int $paymentId
231
     *
232
     * @return RedirectResponse
233
     */
234
    public function redirectToAltapayPaymentAction(int $paymentId)
235
    {
236
        $payment = $this->getPaymentFromId($paymentId);
237
238
        $url = $this->getParameter('loevgaard_dandomain_altapay.altapay_url').'/merchant/transactions/paymentDetails/'.$payment->getAltapayId();
239
240
        return $this->redirect($url);
241
    }
242
243
    /**
244
     * @param int $paymentId
245
     *
246
     * @return Payment
247
     */
248 View Code Duplication
    private function getPaymentFromId(int $paymentId): Payment
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
249
    {
250
        $paymentRepository = $this->get('loevgaard_dandomain_altapay.payment_repository');
251
252
        /** @var Payment $payment */
253
        $payment = $paymentRepository->find($paymentId);
254
255
        if (!$payment) {
256
            throw $this->createNotFoundException('Payment with id `'.$paymentId.'` not found');
257
        }
258
259
        return $payment;
260
    }
261
262
    /**
263
     * @return PaymentHandler
264
     */
265
    private function getPaymentHandler(): PaymentHandler
266
    {
267
        return $this->get('loevgaard_dandomain_altapay.payment_handler');
268
    }
269
}
270