Passed
Push — master ( 48cc5f...cb74be )
by Darko
05:49
created

BtcPaymentController   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 109
Duplicated Lines 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
wmc 13
eloc 60
c 3
b 1
f 0
dl 0
loc 109
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A callback() 0 18 4
A show() 0 44 4
A btcPayCallback() 0 23 4
A verify_webhook() 0 6 1
1
<?php
2
3
namespace App\Http\Controllers;
4
5
use App\Models\User;
6
use Blacklight\libraries\Geary;
7
use Illuminate\Http\Request;
8
use Illuminate\Http\Response;
9
use Spatie\Permission\Models\Role;
10
11
class BtcPaymentController extends BasePageController
12
{
13
    /**
14
     * @return \Illuminate\Http\RedirectResponse|void
15
     *
16
     * @throws \Exception
17
     */
18
    public function show(Request $request)
19
    {
20
        $this->setPreferences();
21
        $gateway_id = config('settings.mycelium_gateway_id');
22
        $gateway_secret = config('settings.mycelium_gateway_secret');
23
24
        $action = $request->input('action') ?? 'view';
25
        $donation = Role::query()->where('donation', '>', 0)->get(['id', 'name', 'donation', 'addyears']);
26
        $this->smarty->assign('donation', $donation);
27
28
        switch ($action) {
29
            case 'submit':
30
                $price = $request->input('price');
31
                $role = $request->input('role');
32
                $roleName = $request->input('rolename');
33
                $addYears = $request->input('addyears');
34
                $data = ['user_id' => $this->userdata->id, 'username' => $this->userdata->username, 'price' => $price, 'role' => $role, 'rolename' => $roleName, 'addyears' => $addYears];
35
                $keychain_id = random_int(0, 19);
36
                $callback_data = json_encode($data);
37
38
                $geary = new Geary($gateway_id, $gateway_secret);
39
                $order = $geary->create_order($price, $keychain_id, $callback_data);
40
41
                if ($order->payment_id) {
42
                    // Redirect to a payment gateway
43
                    $url = 'https://gateway.gear.mycelium.com/pay/'.$order->payment_id;
44
45
                    return redirect()->to($url);
46
                }
47
                break;
48
            case 'view':
49
            default:
50
                $userId = $this->userdata->id;
0 ignored issues
show
Unused Code introduced by
The assignment to $userId is dead and can be removed.
Loading history...
51
                break;
52
        }
53
54
        $title = 'Become a supporter';
55
        $meta_title = 'Become a supporter';
56
        $meta_description = 'Become a supporter';
57
58
        $content = $this->smarty->fetch('btc_payment.tpl');
59
60
        $this->smarty->assign(compact('content', 'meta_title', 'title', 'meta_description'));
61
        $this->pagerender();
62
    }
63
64
    /**
65
     * Callback data from Mycelium Gear.
66
     */
67
    public function callback(): void
68
    {
69
        $gateway_id = config('settings.mycelium_gateway_id');
70
        $gateway_secret = config('settings.mycelium_gateway_secret');
71
72
        $geary = new Geary($gateway_id, $gateway_secret);
73
        $order = $geary->check_order_callback();
74
75
        // Order status was received
76
        if ($order !== false) {
77
            $callback_data = json_decode($order['callback_data'], true);
78
            $newRole = $callback_data['role'];
79
            $amount = $callback_data['price'];
0 ignored issues
show
Unused Code introduced by
The assignment to $amount is dead and can be removed.
Loading history...
80
            $addYear = $callback_data['addyears'];
81
            // If order was paid in full (2) or overpaid (4)
82
            if ((int) $order['status'] === 2 || (int) $order['status'] === 4) {
83
                User::updateUserRole($callback_data['user_id'], $newRole);
84
                User::updateUserRoleChangeDate($callback_data['user_id'], null, $addYear);
85
            }
86
        }
87
    }
88
89
    public function btcPayCallback(Request $request): Response
90
    {
91
        $payload = json_decode($request->getContent(), true);
92
        if (! empty($payload)) {
93
            // We have received a payment for an invoice and user should be upgraded to a paid plan based on order
94
            if ($payload['type'] === 'InvoiceReceivedPayment') {
95
                preg_match('/(?P<role>\w+(\+\+)?)[ ](?P<addYears>\d+)/i', $payload['metadata']['itemDesc'], $matches);
96
                $user = User::query()->where('email', '=', $payload['metadata']['buyerEmail'])->first();
97
                if ($user) {
98
                    User::updateUserRole($user->id, $matches['role']);
99
                    User::updateUserRoleChangeDate($user->id, null, $matches['addYears']);
100
                    Log::info('User upgraded to '.$matches['role'].' for BTCPay webhook: '.$payload['metadata']['buyerEmail']);
0 ignored issues
show
Bug introduced by
The type App\Http\Controllers\Log was not found. Did you mean Log? If so, make sure to prefix the type with \.
Loading history...
101
                } else {
102
                    Log::error('User not found for BTCPay webhook: '.$payload['metadata']['buyerEmail']);
103
104
                    return response('Not Found', 404);
105
                }
106
            }
107
108
            return response('OK', 200);
109
        }
110
111
        return response('OK', 200);
112
    }
113
114
    public static function verify_webhook($data, $hmac_header): bool
115
    {
116
        // Calculate HMAC
117
        $calculated_hmac = base64_encode(hash_hmac('sha256', $data, config('nntmux.btcpay_webhook_secret'), true));
118
119
        return hash_equals($hmac_header, $calculated_hmac);
120
    }
121
}
122