Passed
Push — main ( 4aef09...4747c4 )
by Miaad
01:45
created

crypto::isIPNRequestValid()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 11
c 1
b 0
f 0
nc 4
nop 0
dl 0
loc 15
rs 9.9
1
<?php
2
3
namespace BPT\pay;
4
5
use BPT\pay\crypto\invoicePaymentInterface;
6
use BPT\pay\crypto\estimatePriceInterface;
7
use BPT\pay\crypto\estimateUpdateInterface;
8
use BPT\pay\crypto\invoiceResponseInterface;
9
use BPT\pay\crypto\ipnDataInterface;
10
use BPT\pay\crypto\paymentInterface;
11
use BPT\settings;
12
use BPT\tools;
13
use CurlHandle;
14
15
class crypto {
16
    private static string $api_key = '';
17
18
    private static string $ipn_secret = '';
19
20
    const API_BASE = 'https://api.nowpayments.io/v1/';
21
22
    private static CurlHandle $session;
23
24
    public static function init (): void {
25
        self::$api_key = settings::$pay['crypto']['api_key'] ?? '';
26
        self::$ipn_secret = settings::$pay['crypto']['ipn_secret'] ?? '';
27
        self::$session = curl_init();
28
        curl_setopt(self::$session, CURLOPT_RETURNTRANSFER, true);
29
        curl_setopt(self::$session, CURLOPT_SSL_VERIFYPEER, 1);
30
        curl_setopt(self::$session, CURLOPT_SSL_VERIFYHOST, 2);
31
    }
32
33
    private static function execute (string $method, string $endpoint, string|array $data = '') {
34
        if (is_array($data)) {
35
            foreach ($data as $key => $value) {
36
                if (empty($value)) {
37
                    unset($data[$key]);
38
                }
39
            }
40
        }
41
42
        $session = self::$session;
43
        curl_setopt($session, CURLOPT_HTTPHEADER, [
44
            'X-API-KEY: ' . self::$api_key,
45
            'Content-Type: application/json'
46
        ]);
47
        switch ($method) {
48
            case 'GET':
49
                curl_setopt($session, CURLOPT_URL, self::API_BASE . $endpoint . !empty($data) && is_array($data) ? ('?' . http_build_query($data)) : '');
50
                break;
51
            case 'POST':
52
                curl_setopt($session, CURLOPT_POST, true);
53
                curl_setopt($session, CURLOPT_POSTFIELDS, json_encode($data));
54
                curl_setopt($session, CURLOPT_URL, self::API_BASE . $endpoint);
55
                break;
56
            default:
57
                return false;
58
        }
59
        return json_decode(curl_exec($session));
0 ignored issues
show
Bug introduced by
It seems like curl_exec($session) can also be of type true; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

59
        return json_decode(/** @scrutinizer ignore-type */ curl_exec($session));
Loading history...
60
    }
61
62
    public static function status (): bool {
63
        return self::execute('GET', 'status')->message === 'OK';
64
    }
65
66
    /**
67
     * @return estimatePriceInterface|mixed
68
     */
69
    public static function getEstimatePrice (int|float $amount, string $currency_from, string $currency_to) {
70
        return self::execute('GET', 'estimate', [
71
            'amount'        => $amount,
72
            'currency_from' => $currency_from,
73
            'currency_to'   => $currency_to
74
        ]);
75
    }
76
77
    /**
78
     * @return invoicePaymentInterface|mixed
79
     */
80
    public static function createPayment (int|float $price_amount, string $price_currency, string $pay_currency, int|float $pay_amount = null, string $ipn_callback_url = null, string $order_id = null, string $order_description = null, string $purchase_id = null, string $payout_address = null, string $payout_currency = null, string $payout_extra_id = null, bool $fixed_rate = null) {
81
        return self::execute('POST', 'payment', [
82
            'price_amount'      => $price_amount,
83
            'price_currency'    => $price_currency,
84
            'pay_currency'      => $pay_currency,
85
            'pay_amount'        => $pay_amount,
86
            'ipn_callback_url'  => $ipn_callback_url,
87
            'order_id'          => $order_id,
88
            'order_description' => $order_description,
89
            'purchase_id'       => $purchase_id,
90
            'payout_address'    => $payout_address,
91
            'payout_currency'   => $payout_currency,
92
            'payout_extra_id'   => $payout_extra_id,
93
            'fixed_rate'        => $fixed_rate
94
        ]);
95
    }
96
97
    /**
98
     * @return invoicePaymentInterface|mixed
99
     */
100
    public static function createInvoicePayment (string $iid, string $pay_currency, string $purchase_id = null, string $order_description = null, string $customer_email = null, string $payout_address = null, string $payout_extra_id = null, string $payout_currency = null) {
101
        return self::execute('POST', 'invoice', [
102
            'iid'               => $iid,
103
            'pay_currency'      => $pay_currency,
104
            'purchase_id'       => $purchase_id,
105
            'order_description' => $order_description,
106
            'customer_email'    => $customer_email,
107
            'payout_address'    => $payout_address,
108
            'payout_extra_id'   => $payout_extra_id,
109
            'payout_currency'   => $payout_currency
110
        ]);
111
    }
112
113
    /**
114
     * @return estimateUpdateInterface|mixed
115
     */
116
    public static function updateEstimatePrice (int $paymentID) {
117
        return self::execute('POST', 'payment/' . $paymentID . '/update-merchant-estimate');
118
    }
119
120
    /**
121
     * @return paymentInterface|mixed
122
     */
123
    public static function getPaymentStatus (int $paymentID) {
124
        return self::execute('GET', 'payment/' . $paymentID);
125
    }
126
127
    public static function getMinimumPaymentAmount (string $currency_from, string $currency_to): float {
128
        return self::execute('GET', 'min-amount', [
129
            'currency_from' => $currency_from,
130
            'currency_to'   => $currency_to
131
        ])->min_amount;
132
    }
133
134
    /**
135
     * @return invoiceResponseInterface|mixed
136
     */
137
    public static function createInvoice (int|float $price_amount, string $price_currency, string $pay_currency, int|float $pay_amount = null, string $ipn_callback_url = null, string $order_id = null, string $order_description = null, string $success_url = null, string $cancel_url = null) {
138
        return self::execute('POST', 'invoice', [
139
            'price_amount'      => $price_amount,
140
            'price_currency'    => $price_currency,
141
            'pay_currency'      => $pay_currency,
142
            'pay_amount'        => $pay_amount,
143
            'ipn_callback_url'  => $ipn_callback_url,
144
            'order_id'          => $order_id,
145
            'order_description' => $order_description,
146
            'success_url'       => $success_url,
147
            'cancel_url'        => $cancel_url
148
        ]);
149
    }
150
151
    public static function getCurrencies (): array {
152
        return self::execute('GET', 'currencies')->currencies;
153
    }
154
155
    public static function isNowPayments(): bool {
156
        $ip = $_SERVER['REMOTE_ADDR'];
157
        if (settings::$cloudflare_verify) {
158
            if (isset($_SERVER['HTTP_CF_CONNECTING_IP']) && tools::isCloudFlare($ip)) {
159
                $ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
160
            }
161
        }
162
        elseif (settings::$arvancloud_verify && isset($_SERVER['HTTP_AR_REAL_IP']) && tools::isArvanCloud($ip)) {
163
            $ip = $_SERVER['HTTP_AR_REAL_IP'];
164
        }
165
166
        return $ip === '144.76.201.30';
167
    }
168
169
    public static function isIPNRequestValid (): bool {
170
        if (empty($_SERVER['HTTP_X_NOWPAYMENTS_SIG'])) {
171
            return false;
172
        }
173
        if (!self::isNowPayments()) {
174
            return false;
175
        }
176
        $request_json = file_get_contents('php://input');
177
        if (empty($request_json)) {
178
            return false;
179
        }
180
        $request_data = json_decode($request_json, true);
181
        ksort($request_data);
182
        $hmac = hash_hmac("sha512", json_encode($request_data, JSON_UNESCAPED_SLASHES), trim(self::$ipn_secret));
183
        return $hmac == $_SERVER['HTTP_X_NOWPAYMENTS_SIG'];
184
    }
185
186
    /**
187
     * @return ipnDataInterface|mixed
188
     */
189
    public static function getIPN () {
190
        if (!self::isIPNRequestValid()) {
191
            return false;
192
        }
193
        return json_decode(file_get_contents('php://input'));
194
    }
195
}