Completed
Branch master (3a051a)
by Songda
01:40
created

Support::verifySign()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 24
Code Lines 15

Duplication

Lines 7
Ratio 29.17 %

Importance

Changes 0
Metric Value
dl 7
loc 24
rs 8.5125
c 0
b 0
f 0
cc 6
eloc 15
nc 5
nop 4
1
<?php
2
3
namespace Yansongda\Pay\Gateways\Alipay;
4
5
use Yansongda\Pay\Exceptions\GatewayException;
6
use Yansongda\Pay\Exceptions\InvalidConfigException;
7
use Yansongda\Pay\Exceptions\InvalidSignException;
8
use Yansongda\Pay\Log;
9
use Yansongda\Supports\Arr;
10
use Yansongda\Supports\Collection;
11
use Yansongda\Supports\Str;
12
use Yansongda\Supports\Traits\HasHttpRequest;
13
14
class Support
15
{
16
    use HasHttpRequest;
17
18
    /**
19
     * Instance.
20
     *
21
     * @var Support
22
     */
23
    private static $instance;
24
25
    /**
26
     * Alipay gateway.
27
     *
28
     * @var string
29
     */
30
    protected $baseUri = 'https://openapi.alipay.com/gateway.do';
31
32
    /**
33
     * Get instance.
34
     *
35
     * @author yansongda <[email protected]>
36
     *
37
     * @return Support
38
     */
39
    public static function getInstance()
40
    {
41
        if (!(self::$instance instanceof self)) {
42
            self::$instance = new self();
43
        }
44
45
        return self::$instance;
46
    }
47
48
    /**
49
     * Get Alipay API result.
50
     *
51
     * @author yansongda <[email protected]>
52
     *
53
     * @param array  $data
54
     * @param string $publicKey
55
     *
56
     * @return Collection
57
     */
58
    public static function requestApi($data, $publicKey): Collection
59
    {
60
        Log::debug('Request To Alipay Api', [self::baseUri(), $data]);
61
62
        $method = str_replace('.', '_', $data['method']).'_response';
63
64
        $data = mb_convert_encoding(self::getInstance()->post('', $data), 'utf-8', 'gb2312');
65
        $data = json_decode($data, true);
66
67
        if (!self::verifySign($data[$method], $publicKey, true, $data['sign'])) {
68
            Log::warning('Alipay Sign Verify FAILED', $data);
69
70
            throw new InvalidSignException('Alipay Sign Verify FAILED', 3, $data);
71
        }
72
73
        if (isset($data[$method]['code']) && $data[$method]['code'] == '10000') {
74
            return new Collection($data[$method]);
75
        }
76
77
        throw new GatewayException(
78
            'Get Alipay API Error:'.$data[$method]['msg'].' - '.$data[$method]['sub_code'],
79
            $data[$method]['code'],
80
            $data
81
        );
82
    }
83
84
    /**
85
     * Generate sign.
86
     *
87
     * @author yansongda <[email protected]>
88
     *
89
     * @return string
90
     */
91
    public static function generateSign($parmas, $privateKey = null): string
92
    {
93
        if (is_null($privateKey)) {
94
            throw new InvalidConfigException('Missing Alipay Config -- [private_key]', 1);
95
        }
96
97 View Code Duplication
        if (Str::endsWith($privateKey, '.pem')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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
            $privateKey = openssl_pkey_get_private($privateKey);
99
        } else {
100
            $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n".
101
                wordwrap($privateKey, 64, "\n", true).
102
                "\n-----END RSA PRIVATE KEY-----";
103
        }
104
105
        openssl_sign(self::getSignContent($parmas), $sign, $privateKey, OPENSSL_ALGO_SHA256);
106
107
        return base64_encode($sign);
108
    }
109
110
    /**
111
     * Verfiy sign.
112
     *
113
     * @author yansongda <[email protected]>
114
     *
115
     * @param array       $data
116
     * @param string      $publicKey
117
     * @param bool        $sync
118
     * @param string|null $sign
119
     *
120
     * @return bool
121
     */
122
    public static function verifySign($data, $publicKey = null, $sync = false, $sign = null): bool
123
    {
124
        if (is_null($publicKey)) {
125
            throw new InvalidConfigException('Missing Alipay Config -- [ali_public_key]', 2);
126
        }
127
128 View Code Duplication
        if (Str::endsWith($publicKey, '.pem')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
129
            $publicKey = openssl_pkey_get_public($publicKey);
130
        } else {
131
            $publicKey = "-----BEGIN PUBLIC KEY-----\n".
132
                wordwrap($publicKey, 64, "\n", true).
133
                "\n-----END PUBLIC KEY-----";
134
        }
135
136
        $sign = $sign ?? $data['sign'];
137
138
        $toVerify = $sync ? mb_convert_encoding(json_encode($data, JSON_UNESCAPED_UNICODE), 'gb2312', 'utf-8') :
139
                            self::getSignContent(
140
                                (!isset($data['charset']) || $data['charset'] != 'utf-8') ? Arr::encoding($data, 'gb2312', 'utf-8') : $data,
0 ignored issues
show
Bug introduced by
The method encoding() does not seem to exist on object<Yansongda\Supports\Arr>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
141
                                true
142
                            );
143
144
        return openssl_verify($toVerify, base64_decode($sign), $publicKey, OPENSSL_ALGO_SHA256) === 1;
145
    }
146
147
    /**
148
     * Get signContent that is to be signed.
149
     *
150
     * @author yansongda <[email protected]>
151
     *
152
     * @param array $toBeSigned
153
     * @param bool  $verify
154
     *
155
     * @return string
156
     */
157
    public static function getSignContent(array $toBeSigned, $verify = false): string
158
    {
159
        ksort($toBeSigned);
160
161
        $stringToBeSigned = '';
162
        foreach ($toBeSigned as $k => $v) {
163
            if ($verify && $k != 'sign' && $k != 'sign_type') {
164
                $stringToBeSigned .= $k.'='.$v.'&';
165
            }
166
            if (!$verify && $v !== '' && !is_null($v) && $k != 'sign' && '@' != substr($v, 0, 1)) {
167
                $stringToBeSigned .= $k.'='.$v.'&';
168
            }
169
        }
170
        $stringToBeSigned = substr($stringToBeSigned, 0, -1);
171
        unset($k, $v);
172
173
        return $stringToBeSigned;
174
    }
175
176
    /**
177
     * Alipay gateway.
178
     *
179
     * @author yansongda <[email protected]>
180
     *
181
     * @param string $mode
182
     *
183
     * @return string
184
     */
185
    public static function baseUri($mode = null): string
186
    {
187
        switch ($mode) {
188
            case 'dev':
189
                self::getInstance()->baseUri = 'https://openapi.alipaydev.com/gateway.do';
190
                break;
191
192
            default:
193
                break;
194
        }
195
196
        return self::getInstance()->baseUri;
197
    }
198
}
199