Completed
Push — master ( bf6fd0...fe0998 )
by i
04:47
created

SecurityTrait::toEncoding()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 3
dl 0
loc 15
ccs 0
cts 7
cp 0
crap 12
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Nilnice\Payment\Wechat\Traits;
4
5
use Illuminate\Support\Arr;
6
use Illuminate\Support\Str;
7
use Nilnice\Payment\Constant;
8
use Nilnice\Payment\Exception\InvalidKeyException;
9
10
trait SecurityTrait
11
{
12
    /**
13
     * Generate signature.
14
     *
15
     * @param array $array
16
     * @param null  $key
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $key is correct as it would always require null to be passed?
Loading history...
17
     *
18
     * @return string
19
     * @throws \Nilnice\Payment\Exception\InvalidKeyException
20
     */
21 3
    public static function generateSign(array $array, $key = null) : string
22
    {
23 3
        if ($key === null) {
24 3
            throw new InvalidKeyException(
25 3
                'Invalid Wechat [key] configuration.',
26 3
                Constant::WX_PAY_KEY_INVALID
27
            );
28
        }
29
30
        ksort($array);
31
        $string = md5(self::getSignContent($array) . '&key=' . $key);
32
        $string = strtoupper($string);
33
34
        return $string;
35
    }
36
37
    /**
38
     * Get signature content.
39
     *
40
     * @param array $array
41
     *
42
     * @return string
43
     */
44
    public static function getSignContent(array $array) : string
45
    {
46
        $string = '';
47
        foreach ($array as $key => $val) {
48
            if ($key !== 'sign' && $val !== '' && ! \is_array($val)) {
49
                $string .= $key . '=' . $val;
50
            }
51
        }
52
53
        return rtrim($string, '&');
54
    }
55
56
    /**
57
     * Verify signature.
58
     *
59
     * @param array       $array
60
     * @param string|null $key
61
     * @param bool        $isSync
62
     * @param string|null $sign
63
     *
64
     * @return bool
65
     * @throws \Nilnice\Payment\Exception\InvalidKeyException
66
     */
67
    public static function verifySign(
68
        array $array,
69
        $key = null,
70
        $isSync = false,
71
        $sign = null
72
    ) : bool {
73
        if ($key === null) {
74
            throw new InvalidKeyException(
75
                'Invalid Alipay [public key] configuration.',
76
                Constant::ALI_PAY_PUBLIC_KEY_INVALID
77
            );
78
        }
79
80
        if (Str::endsWith($key, '.pem')) {
81
            $key = openssl_pkey_get_public($key);
82
        } else {
83
            $key = self::getKey($key);
84
        }
85
86
        $sign = $sign ?? Arr::get($array, 'sign');
87
        $data = $isSync
88
            ? mb_convert_encoding(
89
                json_encode($array, JSON_UNESCAPED_UNICODE),
90
                'GB2312',
91
                'UTF-8'
92
            )
93
            : self::getSignContent($array, true);
0 ignored issues
show
Unused Code introduced by
The call to Nilnice\Payment\Wechat\T...Trait::getSignContent() has too many arguments starting with true. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

93
            : self::/** @scrutinizer ignore-call */ getSignContent($array, true);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
94
        $sign = base64_decode($sign);
95
        $result = openssl_verify($data, $sign, $key, OPENSSL_ALGO_SHA256) === 1;
96
97
        return $result === 1;
98
    }
99
100
    /**
101
     * To encoding.
102
     *
103
     * @param array  $array
104
     * @param string $to
105
     * @param string $from
106
     *
107
     * @return array
108
     */
109
    public static function toEncoding(
110
        array $array,
111
        $to = 'GB2312',
112
        $from = 'UTF-8'
113
    ) : array {
114
        $data = [];
115
        foreach ($array as $key => $val) {
116
            if (\is_array($val)) {
117
                $data[$key] = self::toEncoding((array)$val, $to, $from);
118
            } else {
119
                $data[$key] = mb_convert_encoding($val, $to, $from);
120
            }
121
        }
122
123
        return $data;
124
    }
125
126
    /**
127
     * Get key.
128
     *
129
     * @param string $key
130
     * @param bool   $isPublicKey
131
     *
132
     * @return string
133
     */
134
    public static function getKey(
135
        string $key,
136
        bool $isPublicKey = true
137
    ) : string {
138
        $char = $isPublicKey ? 'PUBLIC' : 'RSA PRIVATE';
139
        $format = "-----BEGIN %s KEY-----\n%s\n-----END %s KEY-----";
140
        $key = wordwrap($key, 64, PHP_EOL, true);
141
        $string = sprintf($format, $char, $key, $char);
142
143
        return $string;
144
    }
145
}
146