Completed
Push — master ( 7aa62b...6d63cb )
by Kirill
37:27
created

Client::getSecretData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
ccs 0
cts 4
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace Personnage\Tinkoff\SDK\E2Card;
4
5
use Closure;
6
use GuzzleHttp\Psr7\Request;
7
use Personnage\Tinkoff\SDK\Exception\HttpException;
8
use Personnage\Tinkoff\SDK\Exception\ResponseException;
9
use Personnage\Tinkoff\SDK\HasSender;
10
use Personnage\Tinkoff\SDK\Response\Init;
11
use Personnage\Tinkoff\SDK\Response\Payment;
12
use Personnage\Tinkoff\SDK\Response\State;
13
use Personnage\Tinkoff\SDK\SecretDataContainer;
14
use Personnage\Tinkoff\SDK\Sender;
15
use Psr\Http\Message\RequestInterface;
16
17
final class Client
18
{
19
    use HasSignature, HasSender;
20
21
    private $baseUri;
22
    private $terminalKey;
23
    private $certNumber;
24
    private $signer;
25
26
    /**
27
     * @var array
28
     */
29
    private static $requestHeaders = [
30
        'Accept' => 'application/json',
31
        'Content-Type' => 'application/x-www-form-urlencoded',
32
    ];
33
34
    /**
35
     * Init a new instance.
36
     *
37
     * @param string  $uri
38
     * @param string  $terminalKey
39
     * @param string  $certNumber
40
     * @param Closure $signer
41
     * @param Sender  $sender
42
     */
43
    public function __construct(string $uri, string $terminalKey, string $certNumber, Closure $signer, Sender $sender)
44
    {
45
        $this->baseUri     = rtrim($uri, '/');
46
        $this->terminalKey = $terminalKey;
47
        $this->certNumber  = $certNumber;
48
        $this->signer      = $signer;
49
        $this->setSender($sender);
50
    }
51
52
    /**
53
     * Create a new instance.
54
     *
55
     * @param string   $uri
56
     * @param string   $terminalKey
57
     * @param string   $pemFile
58
     * @param Sender   $sender
59
     * @return self
60
     */
61
    public static function make(string $uri, string $terminalKey, string $pemFile, Sender $sender): self
62
    {
63
        $signer = function (string $raw) use ($pemFile): SecretDataContainer {
64
            $cert = realpath($pemFile);
65
            $digest = self::digest($raw);
66
67
            return new SecretDataContainer(self::getSerialNumber($cert), $digest, self::sign($digest, $cert));
68
        };
69
70
        return new self($uri, $terminalKey, self::getSerialNumber($pemFile), $signer, $sender);
71
    }
72
73
    /**
74
     * Get serial number of X509.
75
     *
76
     * @param  string  $pemFile
77
     * @return string
78
     */
79
    private static function getSerialNumber(string $pemFile): string
80
    {
81
        $text = openssl_x509_parse('file://'.$pemFile, true);
82
83
        return $text['serialNumber'];
84
    }
85
86
    /**
87
     * Init a new payment session.
88
     *
89
     * @param string $orderId Номер заказа в системе Продавца.
90
     * @param string $cardId  Идентификатор карты пополнения.
91
     * @param int    $amount  Сумма в копейках.
92
     * @param array  $extra   Массив дополнительных полей, которые могут быть переданы в этом запросе.
93
     *
94
     * @return Init
95
     * @throws HttpException
96
     */
97 View Code Duplication
    public function init(string $orderId, string $cardId, 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...
98
    {
99
        $extra['Amount'] = $amount;
100
        $extra['CardId'] = $cardId;
101
        $extra['OrderId'] = $orderId;
102
103
        return new Init($this->send($this->makeRequest('Init', $extra)));
104
    }
105
106
    /**
107
     * Make payout to card.
108
     *
109
     * @param mixed $paymentId
110
     *
111
     * @return Payment
112
     * @throws HttpException
113
     */
114
    public function payment($paymentId): Payment
115
    {
116
        return new Payment($this->send($this->makeRequest('Payment', ['PaymentId' => $paymentId])));
117
    }
118
119
    /**
120
     * Get op state.
121
     *
122
     * @param mixed $paymentId
123
     *
124
     * @return State
125
     * @throws HttpException
126
     */
127
    public function getState($paymentId): State
128
    {
129
        return new State($this->send($this->makeRequest('GetState', ['PaymentId' => $paymentId])));
130
    }
131
132
    /**
133
     * Init a new payment session and make payout to card.
134
     *
135
     * @param string $orderId Номер заказа в системе Продавца.
136
     * @param string $cardId  Идентификатор карты пополнения.
137
     * @param int    $amount  Сумма в копейках.
138
     * @param array  $extra   Массив дополнительных полей, которые могут быть переданы в этом запросе.
139
     *
140
     * @return Payment
141
     * @throws HttpException
142
     * @throws ResponseException
143
     */
144
    public function payout(string $orderId, string $cardId, int $amount, array $extra = []): Payment
145
    {
146
        $response = $this->init($orderId, $cardId, $amount, $extra);
147
        if ($response->hasError() || 'CHECKED' !== $response->getStatus()) {
148
            throw new ResponseException($response);
149
        }
150
151
        $response = $this->payment($response->getPaymentId());
152
        if ($response->hasError() || 'COMPLETED' !== $response->getStatus()) {
153
            throw new ResponseException($response);
154
        }
155
156
        return $response;
157
    }
158
159
    /**
160
     * Make a new http request.
161
     *
162
     * @param string $uri
163
     * @param array  $body
164
     *
165
     * @return RequestInterface
166
     */
167
    private function makeRequest(string $uri, array $body = []): RequestInterface
168
    {
169
        /**
170
         * Удалить дайджест, чтобы не повлиять на подпись
171
         */
172
        unset($body['DigestValue']);
173
174
        /** @var $data SecretDataContainer */
175
        $data = $this->getSecretData($body);
176
177
        $body['TerminalKey']      = $this->terminalKey;
178
        $body['DigestValue']      = base64_encode($data->getDigest());
179
        $body['SignatureValue']   = base64_encode($data->getSignature());
180
        $body['X509SerialNumber'] = $data->getSerial();
181
182
        return new Request('post', "$this->baseUri/$uri", self::$requestHeaders, http_build_query($body, '', '&'));
183
    }
184
185
    /**
186
     * Get digest and signature for request data.
187
     *
188
     * @param  array  $body
189
     * @return SecretDataContainer
190
     */
191
    private function getSecretData(array $body): SecretDataContainer
192
    {
193
        return \call_user_func($this->signer, self::getValues($body));
194
    }
195
}
196