• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

AJenbo / agcms / 20972494104

13 Jan 2026 09:02PM UTC coverage: 53.72% (+0.2%) from 53.541%
20972494104

push

github

AJenbo
Upgrade codebase to support PHP 8.5 compatibility

247 of 340 new or added lines in 40 files covered. (72.65%)

6 existing lines in 5 files now uncovered.

2780 of 5175 relevant lines covered (53.72%)

13.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.68
/application/inc/Http/Controllers/Payment.php
1
<?php
2

3
namespace App\Http\Controllers;
4

5
use App\Countries;
6
use App\Enums\InvoiceStatus;
7
use App\Exceptions\Exception;
8
use App\Exceptions\Handler as ExceptionHandler;
9
use App\Exceptions\InvalidInput;
10
use App\Http\Request;
11
use App\Models\CustomPage;
12
use App\Models\Email;
13
use App\Models\Invoice;
14
use App\Models\VolatilePage;
15
use App\Services\ConfigService;
16
use App\Services\EmailService;
17
use App\Services\EpaymentService;
18
use App\Services\InvoiceService;
19
use App\Services\OrmService;
20
use App\Services\RenderService;
21
use Symfony\Component\HttpFoundation\RedirectResponse;
22
use Symfony\Component\HttpFoundation\Response;
23
use Throwable;
24

25
class Payment extends Base
26
{
27
    private InvoiceService $invoiceService;
28

29
    /**
30
     * Initialize needed services.
31
     */
32
    public function __construct()
33
    {
34
        $this->invoiceService = new InvoiceService();
24✔
35
    }
36

37
    /**
38
     * Page for manually entering the id and checkid code.
39
     */
40
    public function index(Request $request): Response
41
    {
42
        $data = $this->basicPageData();
1✔
43
        $crumbs = $data['crumbs'] ?? null;
1✔
44
        if (!is_array($crumbs)) {
1✔
45
            $crumbs = [];
×
46
        }
47

48
        $renderable = new VolatilePage(_('Payment'), $request->getRequestUri());
1✔
49
        $crumbs[] = $renderable;
1✔
50
        $data['crumbs'] = $crumbs;
1✔
51
        $data['renderable'] = $renderable;
1✔
52
        $data['id'] = $request->get('id');
1✔
53
        $data['checkid'] = $request->get('checkid');
1✔
54
        $response = $this->render('payment-manual', $data);
1✔
55

56
        return $this->cachedResponse($response);
1✔
57
    }
58

59
    /**
60
     * Show the items in the shopping basket.
61
     */
62
    public function basket(Request $request, int $id, string $checkId): Response
63
    {
64
        $invoice = app(OrmService::class)->getOne(Invoice::class, $id);
3✔
65
        if ($redirect = $this->checkStatus($id, $checkId, $invoice)) {
3✔
66
            return $redirect;
2✔
67
        }
68
        if (!$invoice) {
1✔
69
            throw new InvalidInput('Invoice not found', Response::HTTP_NOT_FOUND);
×
70
        }
71

72
        $invoice->setStatus(InvoiceStatus::Locked)->save();
1✔
73

74
        $data = $this->basicPageData();
1✔
75
        $crumbs = $data['crumbs'] ?? null;
1✔
76
        if (!is_array($crumbs)) {
1✔
77
            $crumbs = [];
×
78
        }
79

80
        $renderable = new VolatilePage(_('Order #') . $id, $invoice->getLink());
1✔
81
        $crumbs[] = $renderable;
1✔
82
        $data['crumbs'] = $crumbs;
1✔
83
        $data['renderable'] = $renderable;
1✔
84
        $data['invoice'] = $invoice;
1✔
85

86
        $response = $this->render('payment-form0', $data);
1✔
87

88
        return $this->cachedResponse($response);
1✔
89
    }
90

91
    /**
92
     * Page for user to correct there contact info.
93
     */
94
    public function address(Request $request, int $id, string $checkId): Response
95
    {
96
        $invoice = app(OrmService::class)->getOne(Invoice::class, $id);
3✔
97
        if ($redirect = $this->checkStatus($id, $checkId, $invoice)) {
3✔
98
            return $redirect;
1✔
99
        }
100
        if (!$invoice) {
2✔
101
            throw new InvalidInput('Invoice not found', Response::HTTP_NOT_FOUND);
×
102
        }
103

104
        $data = $this->basicPageData();
2✔
105
        $crumbs = $data['crumbs'] ?? null;
2✔
106
        if (!is_array($crumbs)) {
2✔
107
            $crumbs = [];
×
108
        }
109

110
        $data['countries'] = Countries::getOrdered();
2✔
111
        $crumbs[] = new VolatilePage(_('Order #') . $id, $invoice->getLink());
2✔
112
        $renderable = new VolatilePage(_('Address'), $invoice->getLink() . 'address/');
2✔
113
        $crumbs[] = $renderable;
2✔
114
        $data['crumbs'] = $crumbs;
2✔
115
        $data['renderable'] = $renderable;
2✔
116
        $data['newsletter'] = $request->query->getBoolean('newsletter');
2✔
117
        $data['invoice'] = $invoice;
2✔
118
        $data['invalid'] = $invoice->getInvalid();
2✔
119
        $data['action'] = $invoice->getLink() . 'address/';
2✔
120
        $data['actionLable'] = _('Continue');
2✔
121

122
        $response = $this->render('order-form1', $data);
2✔
123

124
        return $this->cachedResponse($response);
2✔
125
    }
126

127
    /**
128
     * Update the contact infor and forwared the user in the payment process.
129
     */
130
    public function addressSave(Request $request, int $id, string $checkId): Response
131
    {
132
        $invoice = app(OrmService::class)->getOne(Invoice::class, $id);
7✔
133
        if ($redirect = $this->checkStatus($id, $checkId, $invoice)) {
7✔
134
            return $redirect;
1✔
135
        }
136
        if (!$invoice) {
6✔
137
            throw new InvalidInput('Invoice not found', Response::HTTP_NOT_FOUND);
×
138
        }
139

140
        $data = $request->request->all();
6✔
141
        $data = $this->invoiceService->cleanAddressData($data);
6✔
142

143
        $invoice->setStatus(InvoiceStatus::Locked)
6✔
144
            ->setName(valstring($data['name']))
6✔
145
            ->setAttn(valstring($data['attn']))
6✔
146
            ->setAddress(valstring($data['address']))
6✔
147
            ->setPostbox(valstring($data['postbox']))
6✔
148
            ->setPostcode(valstring($data['postcode']))
6✔
149
            ->setCity(valstring($data['city']))
6✔
150
            ->setCountry(valstring($data['country']))
6✔
151
            ->setEmail(valstring($data['email']))
6✔
152
            ->setPhone1(valstring($data['phone1']))
6✔
153
            ->setPhone2(valstring($data['phone2']))
6✔
154
            ->setHasShippingAddress(valbool($data['has_shipping_address']))
6✔
155
            ->setShippingPhone(valstring($data['shipping_phone']))
6✔
156
            ->setShippingName(valstring($data['shipping_name']))
6✔
157
            ->setShippingAttn(valstring($data['shipping_attn']))
6✔
158
            ->setShippingAddress(valstring($data['shipping_address']))
6✔
159
            ->setShippingAddress2(valstring($data['shipping_address2']))
6✔
160
            ->setShippingPostbox(valstring($data['shipping_postbox']))
6✔
161
            ->setShippingPostcode(valstring($data['shipping_postcode']))
6✔
162
            ->setShippingCity(valstring($data['shipping_city']))
6✔
163
            ->setShippingCountry(valstring($data['shipping_country']))
6✔
164
            ->save();
6✔
165

166
        if ($invoice->getInvalid()) {
6✔
167
            if ($request->request->getBoolean('newsletter')) {
2✔
168
                return redirect($invoice->getLink() . 'address/?newsletter=1', Response::HTTP_SEE_OTHER);
1✔
169
            }
170

171
            return redirect($invoice->getLink() . 'address/', Response::HTTP_SEE_OTHER);
1✔
172
        }
173

174
        if ($request->request->getBoolean('newsletter')) {
4✔
175
            $this->invoiceService->addToAddressBook($invoice, $request->getClientIp());
1✔
176
        }
177

178
        return redirect($invoice->getLink() . 'terms/', Response::HTTP_SEE_OTHER);
4✔
179
    }
180

181
    /**
182
     * Show the terms of condition.
183
     */
184
    public function terms(Request $request, int $id, string $checkId): Response
185
    {
186
        $invoice = app(OrmService::class)->getOne(Invoice::class, $id);
2✔
187
        if ($redirect = $this->checkStatus($id, $checkId, $invoice)) {
2✔
188
            return $redirect;
1✔
189
        }
190
        if (!$invoice) {
1✔
191
            throw new InvalidInput('Invoice not found', Response::HTTP_NOT_FOUND);
×
192
        }
193

194
        $invoice->setStatus(InvoiceStatus::Locked)->save();
1✔
195

196
        $data = $this->basicPageData();
1✔
197
        $crumbs = $data['crumbs'] ?? null;
1✔
198
        if (!is_array($crumbs)) {
1✔
199
            $crumbs = [];
×
200
        }
201

202
        $crumbs[] = new VolatilePage(_('Order #') . $id, $invoice->getLink());
1✔
203
        $crumbs[] = new VolatilePage(_('Address'), $invoice->getLink() . 'address/');
1✔
204
        $renderable = new VolatilePage(_('Trade Conditions'), $invoice->getLink() . 'terms/');
1✔
205
        $crumbs[] = $renderable;
1✔
206
        $data['crumbs'] = $crumbs;
1✔
207
        $data['renderable'] = $renderable;
1✔
208

209
        $inputs = [
1✔
210
            'group'          => ConfigService::getString('pbsfix'),
1✔
211
            'merchantnumber' => ConfigService::getString('pbsid'),
1✔
212
            'orderid'        => ConfigService::getString('pbsfix') . $invoice->getId(),
1✔
213
            'currency'       => 208,
214
            'amount'         => number_format($invoice->getAmount(), 2, '', ''),
1✔
215
            'ownreceipt'     => 1,
216
            'accepturl'      => $invoice->getLink() . 'status/',
1✔
217
            'cancelurl'      => $invoice->getLink() . 'terms/',
1✔
218
            'callbackurl'    => $invoice->getLink() . 'callback/',
1✔
219
            'windowstate'    => 3,
220
            'windowid'       => ConfigService::getInt('pbswindow'),
1✔
221
        ];
222
        $inputs['hash'] = md5(implode('', $inputs) . ConfigService::getString('pbspassword'));
1✔
223
        $data['inputs'] = $inputs;
1✔
224
        $data['html'] = $this->getTermsHtml();
1✔
225

226
        $response = $this->render('payment-form2', $data);
1✔
227

228
        return $this->cachedResponse($response);
1✔
229
    }
230

231
    /**
232
     * Get the terms and conditions text.
233
     */
234
    public function getTermsHtml(): string
235
    {
236
        $shoppingTerms = app(OrmService::class)->getOne(CustomPage::class, 3);
1✔
237
        if (!$shoppingTerms) {
1✔
238
            app(ExceptionHandler::class)->report(new Exception(_('Missing terms and conditions')));
×
239

240
            return '';
×
241
        }
242

243
        return $shoppingTerms->getHtml();
1✔
244
    }
245

246
    /**
247
     * Show the order status page.
248
     *
249
     * Also set the order payment status if txnid is provided
250
     */
251
    public function status(Request $request, int $id, string $checkId): Response
252
    {
253
        $invoice = app(OrmService::class)->getOne(Invoice::class, $id);
5✔
254
        if (!$invoice || $checkId !== $invoice->getCheckId()) {
5✔
255
            return redirect('/betaling/?id=' . $id . '&checkid=' . rawurlencode($checkId), Response::HTTP_SEE_OTHER);
1✔
256
        }
257

258
        if (!$invoice->isHandled() && InvoiceStatus::PbsOk !== $invoice->getStatus() && !$request->query->has('txnid')) {
4✔
259
            return redirect($invoice->getLink(), Response::HTTP_SEE_OTHER);
1✔
260
        }
261

262
        if ($request->query->has('txnid')) {
3✔
263
            if (!$this->isHashValid($request)) {
2✔
264
                return redirect($invoice->getLink(), Response::HTTP_SEE_OTHER);
1✔
265
            }
266

267
            $this->setPaymentStatus($request, $invoice);
1✔
268
        }
269

270
        $data = $this->basicPageData();
2✔
271
        $crumbs = $data['crumbs'] ?? null;
2✔
272
        if (!is_array($crumbs)) {
2✔
273
            $crumbs = [];
×
274
        }
275

276
        $crumbs[] = new VolatilePage(_('Order #') . $id, $invoice->getLink());
2✔
277
        $crumbs[] = new VolatilePage(_('Address'), $invoice->getLink() . 'address/');
2✔
278
        $crumbs[] = new VolatilePage(_('Trade Conditions'), $invoice->getLink() . 'terms/');
2✔
279
        $renderable = new VolatilePage(_('Receipt'), $invoice->getLink() . 'status/');
2✔
280
        $crumbs[] = $renderable;
2✔
281
        $data['crumbs'] = $crumbs;
2✔
282
        $data['renderable'] = $renderable;
2✔
283
        $data['newsletter'] = $request->query->getBoolean('newsletter');
2✔
284
        $data['invoice'] = $invoice;
2✔
285
        $data['invalid'] = $invoice->getInvalid();
2✔
286
        $data['action'] = $invoice->getLink() . 'address/';
2✔
287
        $data['statusMessage'] = $this->getStatusMessage($invoice);
2✔
288

289
        $response = $this->render('payment-status', $data);
2✔
290

291
        return $this->cachedResponse($response);
2✔
292
    }
293

294
    /**
295
     * Get the status message.
296
     *
297
     * @throws Exception
298
     */
299
    private function getStatusMessage(Invoice $invoice): string
300
    {
301
        switch ($invoice->getStatus()) {
2✔
302
            case InvoiceStatus::PbsOk:
2✔
303
                return _('Payment is now accepted. We will send your goods by mail as soon as possible.')
1✔
304
                    . '<br />' . _('A copy of your order has been sent to your email.');
1✔
305
            case InvoiceStatus::Canceled:
1✔
306
                return _('The transaction is canceled.');
1✔
307
            case InvoiceStatus::Giro:
×
308
                return _('The payment has already been received via giro.');
×
309
            case InvoiceStatus::Cash:
×
310
                return _('The payment has already been received in cash.');
×
311
            case InvoiceStatus::Accepted:
×
312
                return _('The payment was received and the package is sent.');
×
313
        }
314

315
        throw new Exception(_('Unknown status.'));
×
316
    }
317

318
    /**
319
     * Set the order payment status.
320
     */
321
    public function callback(Request $request, int $id, string $checkId): Response
322
    {
323
        $invoice = app(OrmService::class)->getOne(Invoice::class, $id);
3✔
324
        if (!$invoice
325
            || $checkId !== $invoice->getCheckId()
3✔
326
            || !$this->isHashValid($request)
3✔
327
        ) {
328
            throw new InvalidInput('', Response::HTTP_BAD_REQUEST);
1✔
329
        }
330

331
        $this->setPaymentStatus($request, $invoice);
2✔
332

333
        return new Response();
2✔
334
    }
335

336
    /**
337
     * Validate payment hash.
338
     */
339
    private function isHashValid(Request $request): bool
340
    {
341
        $params = $request->query->all();
5✔
342
        unset($params['hash']);
5✔
343

344
        $eKey = md5(implode('', $params) . ConfigService::getString('pbspassword'));
5✔
345

346
        return $eKey === $request->get('hash');
5✔
347
    }
348

349
    /**
350
     * Set the order payment status.
351
     */
352
    private function setPaymentStatus(Request $request, Invoice $invoice): void
353
    {
354
        if ($invoice->isHandled() || InvoiceStatus::PbsOk === $invoice->getStatus()) {
3✔
355
            return;
1✔
356
        }
357

358
        $cardType = EpaymentService::getPaymentName(valint($request->get('paymenttype')));
2✔
359
        $internalNote = $this->generateInternalPaymentNote($request);
2✔
360

361
        if (!$invoice->getDepartment() || !app(EmailService::class)->valideMail($invoice->getDepartment())) {
2✔
362
            $invoice->setDepartment(ConfigService::getDefaultEmail());
2✔
363
        }
364

365
        $invoice->setCardtype($cardType)
2✔
366
            ->setInternalNote(mb_trim($invoice->getInternalNote() . "\n" . $internalNote))
2✔
367
            ->setStatus(InvoiceStatus::PbsOk)
2✔
368
            ->setTimeStampPay(time())
2✔
369
            ->save();
2✔
370

371
        $this->sendCustomerEmail($invoice);
2✔
372
        $this->sendAdminEmail($invoice);
2✔
373
    }
374

375
    /**
376
     * Send recipt to the customer.
377
     */
378
    private function sendCustomerEmail(Invoice $invoice): void
379
    {
380
        $data = [
2✔
381
            'invoice'    => $invoice,
382
            'siteName'   => ConfigService::getString('site_name'),
2✔
383
            'address'    => ConfigService::getString('address'),
2✔
384
            'postcode'   => ConfigService::getString('postcode'),
2✔
385
            'city'       => ConfigService::getString('city'),
2✔
386
            'phone'      => ConfigService::getString('phone'),
2✔
387
        ];
388
        $email = new Email([
2✔
389
            'subject'          => sprintf(_('Order #%d - payment completed'), $invoice->getId()),
2✔
390
            'body'             => app(RenderService::class)->render('email/payment-confirmation', $data),
2✔
391
            'senderName'       => ConfigService::getString('site_name'),
2✔
392
            'senderAddress'    => $invoice->getDepartment(),
2✔
393
            'recipientName'    => $invoice->getName(),
2✔
394
            'recipientAddress' => $invoice->getEmail(),
2✔
395
        ]);
396

397
        try {
398
            app(EmailService::class)->send($email);
2✔
399
        } catch (Throwable $exception) {
2✔
400
            app(ExceptionHandler::class)->report($exception);
2✔
401
            $email->save();
2✔
402
        }
403
    }
404

405
    /**
406
     * Send status email to the admin.
407
     */
408
    private function sendAdminEmail(Invoice $invoice): void
409
    {
410
        $subject = sprintf(
2✔
411
            _('Attn.: %s - Payment received for invoice #%d'),
2✔
412
            $invoice->getClerk(),
2✔
413
            $invoice->getId()
2✔
414
        );
415

416
        $emailBody = app(RenderService::class)->render(
2✔
417
            'admin/email/payment-confirmation',
418
            ['invoice' => $invoice]
419
        );
420

421
        $email = new Email([
2✔
422
            'subject'          => $subject,
423
            'body'             => $emailBody,
424
            'senderName'       => ConfigService::getString('site_name'),
2✔
425
            'senderAddress'    => $invoice->getDepartment(),
2✔
426
            'recipientName'    => ConfigService::getString('site_name'),
2✔
427
            'recipientAddress' => $invoice->getDepartment(),
2✔
428
        ]);
429

430
        try {
431
            app(EmailService::class)->send($email);
2✔
432
        } catch (Throwable $exception) {
2✔
433
            app(ExceptionHandler::class)->report($exception);
2✔
434
            $email->save();
2✔
435
        }
436
    }
437

438
    /**
439
     * Generate message for the internal note about fraud status.
440
     */
441
    private function generateInternalPaymentNote(Request $request): string
442
    {
443
        $internalNote = '';
2✔
444
        if ($request->get('fraud')) {
2✔
445
            $internalNote .= _('Possible payment fraud.') . "\n";
×
446
        }
447
        $cardno = valstring($request->get('cardno'));
2✔
448
        if ($cardno) {
2✔
NEW
449
            $internalNote .= _('Credit card no.: ') . $cardno . "\n";
×
450
        }
451

452
        $countries = Countries::getOrdered();
2✔
453
        $issuercountry = valstring($request->get('issuercountry'));
2✔
454
        if ($issuercountry) {
2✔
NEW
455
            $internalNote .= _('Card is from: ') . $countries[$issuercountry] . "\n";
×
456
        }
457
        $payercountry = valstring($request->get('payercountry'));
2✔
458
        if ($payercountry) {
2✔
NEW
459
            $internalNote .= _('Payment was made from: ') . $countries[$payercountry] . "\n";
×
460
        }
461

462
        return mb_trim($internalNote);
2✔
463
    }
464

465
    /**
466
     * Check if request should be redirected to a different page in the process.
467
     */
468
    private function checkStatus(int $id, string $checkId, ?Invoice $invoice): ?RedirectResponse
469
    {
470
        if (!$invoice || $checkId !== $invoice->getCheckId()) {
15✔
471
            return redirect('/betaling/?id=' . $id . '&checkid=' . rawurlencode($checkId), Response::HTTP_SEE_OTHER);
4✔
472
        }
473
        if ($invoice->isHandled() || InvoiceStatus::PbsOk === $invoice->getStatus()) {
11✔
474
            return redirect($invoice->getLink() . 'status/', Response::HTTP_SEE_OTHER);
1✔
475
        }
476

477
        return null;
10✔
478
    }
479
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc