Completed
Push — master ( 4a9165...4bed01 )
by Kirill
25:22 queued 10:18
created

MerchantAPI::assertValidToken()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 2
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace Personnage\Tinkoff\SDK;
4
5
use GuzzleHttp\Psr7\Request;
6
use Personnage\Tinkoff\SDK\Event\FailureEvent;
7
use Personnage\Tinkoff\SDK\Event\StartedEvent;
8
use Personnage\Tinkoff\SDK\Event\SuccessEvent;
9
use Personnage\Tinkoff\SDK\Exception\HttpException;
10
use Personnage\Tinkoff\SDK\Exception\InvalidArgumentException;
11
use Personnage\Tinkoff\SDK\Response\Cancel;
12
use Personnage\Tinkoff\SDK\Response\Charge;
13
use Personnage\Tinkoff\SDK\Response\Confirm;
14
use Personnage\Tinkoff\SDK\Response\Init;
15
use Personnage\Tinkoff\SDK\Response\Response;
16
use Psr\Http\Message\RequestInterface;
17
use Psr\Http\Message\ResponseInterface;
18
19
final class MerchantAPI
20
{
21
    use HasEventEmitter;
22
23
    private $baseUri;
24
    private $terminalKey;
25
    private $secretKey;
26
    private $sender;
27
28
    /**
29
     * @var array
30
     */
31
    private static $requestHeaders = [
32
        'Accept' => 'application/json',
33
        'Content-Type' => 'application/x-www-form-urlencoded',
34
    ];
35
36
    /**
37
     * Init a new instance.
38
     *
39
     * @param string  $uri
40
     * @param string  $terminalKey
41
     * @param string  $secretKey
42
     * @param Sender  $sender
43
     */
44
    public function __construct($uri, $terminalKey, $secretKey, Sender $sender)
45
    {
46
        $this->baseUri = rtrim($uri, '/');
47
        $this->terminalKey = $terminalKey;
48
        $this->secretKey = $secretKey;
49
        $this->sender = $sender;
50
    }
51
52
    /**
53
     * Init a new payment session.
54
     *
55
     * @param string  $orderId Номер заказа в системе Продавца.
56
     * @param int     $amount  Сумма в копейках.
57
     * @param array   $extra   Массив дополнительных полей, которые могут быть переданы в этом запросе.
58
     *
59
     * @return Init
60
     * @throws Exception\HttpException
61
     */
62 View Code Duplication
    public function init(string $orderId, int $amount, array $extra = []): Init
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...
63
    {
64
        $extra['Amount'] = $amount;
65
        $extra['OrderId'] = $orderId;
66
67
        return new Init($this->send($this->makeRequest('Init', $extra)));
68
    }
69
70
    /**
71
     * Init a new payment session.
72
     *
73
     * @param string  $customerKey
74
     * @param string  $orderId
75
     * @param int     $amount
76
     * @param array   $extra
77
     *
78
     * @return Init
79
     * @throws Exception\HttpException
80
     */
81
    public function rInit(string $customerKey, string $orderId, int $amount, array $extra = []): Init
82
    {
83
        $extra['Recurrent'] = 'Y';
84
        $extra['CustomerKey'] = $customerKey;
85
86
        return $this->init($orderId, $amount, $extra);
87
    }
88
89
    /**
90
     * Call charge.
91
     *
92
     * @param int  $paymentId
93
     * @param int  $rebillId
94
     *
95
     * @return Charge
96
     *
97
     * @throws Exception\HttpException
98
     */
99
    public function charge(int $paymentId, int $rebillId): Charge
100
    {
101
        return new Charge($this->send($this->makeRequest('Charge', [
102
            'PaymentId' => $paymentId,
103
            'RebillId' => $rebillId,
104
        ])));
105
    }
106
107
    /**
108
     * Init a new payment session and call charge.
109
     *
110
     * @param string  $orderId Номер заказа в системе Продавца.
111
     * @param int     $amount  Сумма в копейках.
112
     * @param array   $extra   Массив дополнительных полей, которые могут быть переданы в этом запросе.
113
     *
114
     * @return Init|Charge
115
     * @throws Exception\HttpException
116
     */
117
    public function recurrent(int $rebillId, string $orderId, int $amount, array $extra = []): Response
118
    {
119
        $init = $this->init($orderId, $amount, $extra);
120
121
        if ('NEW' === $init->getStatus()) {
122
            return $this->charge($init->getPaymentId(), $rebillId);
123
        }
124
125
        return $init;
126
    }
127
128
    /**
129
     * Send confirm request.
130
     *
131
     * @param int       $paymentId
132
     * @param int|null  $amount
133
     * @param array     $extra
134
     *
135
     * @return Confirm
136
     * @throws Exception\HttpException
137
     */
138 View Code Duplication
    public function confirm(int $paymentId, int $amount = null, array $extra = []): Confirm
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...
139
    {
140
        $extra['PaymentId'] = $paymentId;
141
142
        if (null !== $amount) {
143
            $extra['Amount'] = $amount;
144
        }
145
146
        return new Confirm($this->send($this->makeRequest('Confirm', $extra)));
147
    }
148
149
    /**
150
     * Send cancel request.
151
     *
152
     * @param int       $paymentId
153
     * @param int|null  $amount
154
     * @param array     $extra
155
     *
156
     * @return Cancel
157
     * @throws Exception\HttpException
158
     */
159 View Code Duplication
    public function cancel(int $paymentId, int $amount = null, array $extra = []): Cancel
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...
160
    {
161
        $extra['PaymentId'] = $paymentId;
162
163
        if (null !== $amount) {
164
            $extra['Amount'] = $amount;
165
        }
166
167
        return new Cancel($this->send($this->makeRequest('Cancel', $extra)));
168
    }
169
170
    /**
171
     * Make a new http request.
172
     *
173
     * @param string  $uri
174
     * @param array   $body
175
     *
176
     * @return RequestInterface
177
     */
178
    private function makeRequest(string $uri, array $body = []): RequestInterface
179
    {
180
        $body['TerminalKey'] = $this->terminalKey;
181
        $body['Token'] = $this->genToken($body);
182
183
        return new Request('post', "$this->baseUri/$uri", self::$requestHeaders, http_build_query($body, '', '&'));
184
    }
185
186
    /**
187
     * Call http request and throw events.
188
     *
189
     * @param  RequestInterface $request
190
     *
191
     * @return ResponseInterface
192
     * @throws Exception\HttpException
193
     */
194 View Code Duplication
    private function send(RequestInterface $request): ResponseInterface
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...
195
    {
196
        $started = microtime(true);
197
198
        try {
199
            $this->fire(StartedEvent::class, [$request, $started]);
200
            $response = $this->sender->send($request);
201
            $this->fire(SuccessEvent::class, [$request, $response, microtime(true), $started]);
202
203
            return $response;
204
        } catch (HttpException $e) {
205
            $this->fire(FailureEvent::class, [$request, microtime(true), $started]);
206
207
            throw $e;
208
        }
209
    }
210
211
    /**
212
     * Get signature for arguments.
213
     *
214
     * @param array $args
215
     *
216
     * @return string
217
     */
218
    private function genToken(array $args): string
219
    {
220
        $args['Password'] = $this->secretKey;
221
222
        ksort($args);
223
224
        return hash('sha256', implode('', $args));
225
    }
226
227
    /**
228
     * Throws an exception if token is invalid.
229
     *
230
     * @param string $expected
231
     * @param array  $data
232
     *
233
     * @throws InvalidArgumentException
234
     */
235
    public function assertValidToken(string $expected, array $data)
236
    {
237
        unset($data['Token']);
238
        $actual = $this->genToken($data);
239
240
        if ($expected === $actual) {
241
            return ;
242
        }
243
244
        throw new InvalidArgumentException(
245
            "Invalid token. Expected: $expected, but actual: $actual"
246
        );
247
    }
248
}
249