StartPlugin::getCertSn()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 3
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yansongda\Pay\Plugin\Alipay\V2;
6
7
use Closure;
8
use Yansongda\Artful\Contract\ConfigInterface;
9
use Yansongda\Artful\Contract\PluginInterface;
10
use Yansongda\Artful\Exception\ContainerException;
11
use Yansongda\Artful\Exception\InvalidConfigException;
12
use Yansongda\Artful\Exception\ServiceNotFoundException;
13
use Yansongda\Artful\Logger;
14
use Yansongda\Artful\Rocket;
15
use Yansongda\Pay\Exception\Exception;
16
use Yansongda\Pay\Pay;
17
18
use function Yansongda\Pay\get_alipay_config;
19
use function Yansongda\Pay\get_public_cert;
20
use function Yansongda\Pay\get_tenant;
21
22
class StartPlugin implements PluginInterface
23
{
24
    /**
25
     * @throws ContainerException
26
     * @throws ServiceNotFoundException
27
     * @throws InvalidConfigException
28
     */
29
    public function assembly(Rocket $rocket, Closure $next): Rocket
30
    {
31
        Logger::debug('[Alipay][StartPlugin] 插件开始装载', ['rocket' => $rocket]);
32
33
        $rocket->mergePayload($this->getPayload($rocket->getParams()));
34
35
        Logger::info('[Alipay][StartPlugin] 插件装载完毕', ['rocket' => $rocket]);
36
37
        return $next($rocket);
38
    }
39
40
    /**
41
     * @throws ContainerException
42
     * @throws ServiceNotFoundException
43
     * @throws InvalidConfigException
44
     */
45
    protected function getPayload(array $params): array
46
    {
47
        $tenant = get_tenant($params);
48
        $config = get_alipay_config($params);
49
50
        return [
51
            'app_id' => $config['app_id'] ?? '',
52
            'method' => '',
53
            'format' => 'JSON',
54
            'return_url' => $this->getReturnUrl($params, $config),
55
            'charset' => 'utf-8',
56
            'sign_type' => 'RSA2',
57
            'sign' => '',
58
            'timestamp' => date('Y-m-d H:i:s'),
59
            'version' => '1.0',
60
            'notify_url' => $this->getNotifyUrl($params, $config),
61
            'app_auth_token' => $this->getAppAuthToken($params, $config),
62
            'app_cert_sn' => $this->getAppCertSn($tenant, $config),
63
            'alipay_root_cert_sn' => $this->getAlipayRootCertSn($tenant, $config),
64
            'biz_content' => [],
65
        ];
66
    }
67
68
    protected function getReturnUrl(array $params, array $config): string
69
    {
70
        if (!empty($params['_return_url'])) {
71
            return $params['_return_url'];
72
        }
73
74
        return $config['return_url'] ?? '';
75
    }
76
77
    protected function getNotifyUrl(array $params, array $config): string
78
    {
79
        if (!empty($params['_notify_url'])) {
80
            return $params['_notify_url'];
81
        }
82
83
        return $config['notify_url'] ?? '';
84
    }
85
86
    protected function getAppAuthToken(array $params, array $config): string
87
    {
88
        if (!empty($params['_app_auth_token'])) {
89
            return $params['_app_auth_token'];
90
        }
91
92
        return $config['app_auth_token'] ?? '';
93
    }
94
95
    /**
96
     * @throws ContainerException
97
     * @throws InvalidConfigException
98
     * @throws ServiceNotFoundException
99
     */
100
    protected function getAppCertSn(string $tenant, array $config): string
101
    {
102
        if (!empty($config['app_public_cert_sn'])) {
103
            return $config['app_public_cert_sn'];
104
        }
105
106
        $path = $config['app_public_cert_path'] ?? null;
107
108
        if (is_null($path)) {
109
            throw new InvalidConfigException(Exception::CONFIG_ALIPAY_INVALID, '配置异常: 缺少支付宝配置 -- [app_public_cert_path]');
110
        }
111
112
        $ssl = openssl_x509_parse(get_public_cert($path));
113
114
        if (false === $ssl) {
115
            throw new InvalidConfigException(Exception::CONFIG_ALIPAY_INVALID, '配置异常: 解析 `app_public_cert_path` 失败');
116
        }
117
118
        $result = $this->getCertSn($ssl['issuer'] ?? [], $ssl['serialNumber'] ?? '');
119
120
        Pay::get(ConfigInterface::class)->set('alipay.'.$tenant.'.app_public_cert_sn', $result);
121
122
        return $result;
123
    }
124
125
    /**
126
     * @throws ContainerException
127
     * @throws InvalidConfigException
128
     * @throws ServiceNotFoundException
129
     */
130
    protected function getAlipayRootCertSn(string $tenant, array $config): string
131
    {
132
        if (!empty($config['alipay_root_cert_sn'])) {
133
            return $config['alipay_root_cert_sn'];
134
        }
135
136
        $path = $config['alipay_root_cert_path'] ?? null;
137
138
        if (is_null($path)) {
139
            throw new InvalidConfigException(Exception::CONFIG_ALIPAY_INVALID, '配置异常: 缺少支付宝配置 -- [alipay_root_cert_path]');
140
        }
141
142
        $sn = '';
143
        $exploded = explode('-----END CERTIFICATE-----', get_public_cert($path));
144
145
        foreach ($exploded as $cert) {
146
            if (empty(trim($cert))) {
147
                continue;
148
            }
149
150
            $ssl = openssl_x509_parse($cert.'-----END CERTIFICATE-----');
151
152
            if (false === $ssl) {
153
                throw new InvalidConfigException(Exception::CONFIG_ALIPAY_INVALID, '配置异常: 解析 `alipay_root_cert` 失败');
154
            }
155
156
            $detail = $this->formatCert($ssl);
157
158
            if ('sha1WithRSAEncryption' == $detail['signatureTypeLN'] || 'sha256WithRSAEncryption' == $detail['signatureTypeLN']) {
159
                $sn .= $this->getCertSn($detail['issuer'], $detail['serialNumber']).'_';
160
            }
161
        }
162
163
        $result = substr($sn, 0, -1);
164
165
        Pay::get(ConfigInterface::class)->set('alipay.'.$tenant.'.alipay_root_cert_sn', $result);
166
167
        return $result;
168
    }
169
170
    protected function getCertSn(array $issuer, string $serialNumber): string
171
    {
172
        return md5($this->array2string(array_reverse($issuer)).$serialNumber);
173
    }
174
175
    protected function array2string(array $array): string
176
    {
177
        $string = [];
178
179
        foreach ($array as $key => $value) {
180
            $string[] = $key.'='.$value;
181
        }
182
183
        return implode(',', $string);
184
    }
185
186
    protected function formatCert(array $ssl): array
187
    {
188
        if (str_starts_with($ssl['serialNumber'] ?? '', '0x')) {
189
            $ssl['serialNumber'] = $this->hex2dec($ssl['serialNumberHex'] ?? '');
190
        }
191
192
        return $ssl;
193
    }
194
195
    protected function hex2dec(string $hex): string
196
    {
197
        $dec = '0';
198
        $len = strlen($hex);
199
200
        for ($i = 1; $i <= $len; ++$i) {
201
            $dec = bcadd(
202
                $dec,
203
                bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i), 0), 0),
204
                0
205
            );
206
        }
207
208
        return $dec;
209
    }
210
}
211