Passed
Pull Request — master (#678)
by Songda
02:10
created

RadarSignPlugin::getWechatAuthorization()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 15
c 1
b 0
f 0
dl 0
loc 25
rs 9.7666
cc 3
nc 3
nop 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yansongda\Pay\Plugin\Wechat;
6
7
use Closure;
8
use GuzzleHttp\Psr7\Utils;
9
use Yansongda\Pay\Contract\PluginInterface;
10
use Yansongda\Pay\Exception\Exception;
11
use Yansongda\Pay\Exception\InvalidConfigException;
12
use Yansongda\Pay\Exception\InvalidParamsException;
13
14
use function Yansongda\Pay\get_public_cert;
15
use function Yansongda\Pay\get_wechat_config;
16
use function Yansongda\Pay\get_wechat_sign;
17
18
use Yansongda\Pay\Logger;
19
use Yansongda\Pay\Rocket;
20
use Yansongda\Supports\Collection;
21
use Yansongda\Supports\Str;
22
23
class RadarSignPlugin implements PluginInterface
24
{
25
    /**
26
     * @throws \Yansongda\Pay\Exception\ContainerException
27
     * @throws \Yansongda\Pay\Exception\InvalidConfigException
28
     * @throws \Yansongda\Pay\Exception\InvalidParamsException
29
     * @throws \Yansongda\Pay\Exception\ServiceNotFoundException
30
     * @throws \Exception
31
     */
32
    public function assembly(Rocket $rocket, Closure $next): Rocket
33
    {
34
        Logger::info('[wechat][SignPlugin] 插件开始装载', ['rocket' => $rocket]);
35
36
        $timestamp = time();
37
        $random = Str::random(32);
38
        $body = $this->payloadToString($rocket->getPayload());
39
        $contents = $this->getContents($rocket, $timestamp, $random);
40
        $authorization = $this->getWechatAuthorization($rocket->getParams(), $timestamp, $random, $contents);
41
        $radar = $rocket->getRadar()->withHeader('Authorization', $authorization);
42
43
        if (!empty($rocket->getParams()['_serial_no'])) {
44
            $radar = $radar->withHeader('Wechatpay-Serial', $rocket->getParams()['_serial_no']);
45
        }
46
47
        if (!empty($body)) {
48
            $radar = $radar->withBody(Utils::streamFor($body));
49
        }
50
51
        $rocket->setRadar($radar);
52
53
        Logger::info('[wechat][SignPlugin] 插件装载完毕', ['rocket' => $rocket]);
54
55
        return $next($rocket);
56
    }
57
58
    /**
59
     * @throws \Yansongda\Pay\Exception\ContainerException
60
     * @throws \Yansongda\Pay\Exception\InvalidConfigException
61
     * @throws \Yansongda\Pay\Exception\ServiceNotFoundException
62
     */
63
    protected function getWechatAuthorization(array $params, int $timestamp, string $random, string $contents): string
64
    {
65
        $config = get_wechat_config($params);
66
        $mchPublicCertPath = $config['mch_public_cert_path'] ?? null;
67
68
        if (empty($mchPublicCertPath)) {
69
            throw new InvalidConfigException(Exception::WECHAT_CONFIG_ERROR, 'Missing Wechat Config -- [mch_public_cert_path]');
70
        }
71
72
        $ssl = openssl_x509_parse(get_public_cert($mchPublicCertPath));
73
74
        if (empty($ssl['serialNumberHex'])) {
75
            throw new InvalidConfigException(Exception::WECHAT_CONFIG_ERROR, 'Parse [mch_public_cert_path] Serial Number Error');
76
        }
77
78
        $auth = sprintf(
79
            'mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
80
            $config['mch_id'] ?? '',
81
            $random,
82
            $timestamp,
83
            $ssl['serialNumberHex'],
84
            get_wechat_sign($params, $contents),
85
        );
86
87
        return 'WECHATPAY2-SHA256-RSA2048 '.$auth;
88
    }
89
90
    /**
91
     * @throws \Yansongda\Pay\Exception\InvalidParamsException
92
     */
93
    protected function getContents(Rocket $rocket, int $timestamp, string $random): string
94
    {
95
        $request = $rocket->getRadar();
96
97
        if (is_null($request)) {
98
            throw new InvalidParamsException(Exception::REQUEST_NULL_ERROR);
99
        }
100
101
        $uri = $request->getUri();
102
103
        return $request->getMethod()."\n".
104
            $uri->getPath().(empty($uri->getQuery()) ? '' : '?'.$uri->getQuery())."\n".
105
            $timestamp."\n".
106
            $random."\n".
107
            $this->payloadToString($rocket->getPayload())."\n";
108
    }
109
110
    protected function payloadToString(?Collection $payload): string
111
    {
112
        return (is_null($payload) || 0 === $payload->count()) ? '' : $payload->toJson();
113
    }
114
}
115