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

RequestTrait::request()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 10
nc 6
nop 3
dl 0
loc 18
ccs 0
cts 11
cp 0
crap 20
rs 9.2
c 0
b 0
f 0
1
<?php
2
3
namespace Nilnice\Payment\Wechat\Traits;
4
5
use GuzzleHttp\Client;
6
use Illuminate\Support\Arr;
7
use Illuminate\Support\Collection;
8
use Nilnice\Payment\Constant;
9
use Nilnice\Payment\Exception\GatewayException;
10
use Nilnice\Payment\Exception\InvalidSignException;
11
use Psr\Http\Message\ResponseInterface;
12
13
trait RequestTrait
14
{
15
    /**
16
     * Send a Wechat interface request.
17
     *
18
     * @param string $gateway
19
     * @param array  $array
20
     * @param string $key
21
     *
22
     * @return \Illuminate\Support\Collection
23
     * @throws \Nilnice\Payment\Exception\GatewayException
24
     * @throws \InvalidArgumentException
25
     * @throws \Nilnice\Payment\Exception\InvalidKeyException
26
     * @throws \Nilnice\Payment\Exception\InvalidSignException
27
     * @throws \RuntimeException
28
     */
29
    public function send(
30
        string $gateway,
31
        array $array,
32
        string $key
33
    ) : Collection {
34
        $result = $this->post($gateway, self::toXml($array), []);
35
        $result = \is_array($result) ? $result : self::fromXml($result);
36
        $code = Arr::get($result, 'result_code');
37
38
        if ($code !== 'SUCCESS') {
39
            throw new GatewayException(
40
                'Wechat API Error: ' . $result['return_msg'],
41
                20000
42
            );
43
        }
44
45
        if (self::generateSign($result, $key) === $result['sign']) {
46
            return new Collection($result);
47
        }
48
49
        throw new InvalidSignException(
50
            'Invalid Alipay [signature] verify.',
51
            3
52
        );
53
    }
54
55
    /**
56
     * Send a post request.
57
     *
58
     * @param string $gateway
59
     * @param mixed  $parameter
60
     * @param array  ...$options
61
     *
62
     * @return mixed
63
     * @throws \RuntimeException
64
     */
65
    public function post(
66
        string $gateway,
67
        $parameter = null,
68
        ...$options
69
    ) {
70
        $options = $options[0] ?? [];
71
        if (\is_array($parameter)) {
72
            $options['form_params'] = $parameter;
73
        } else {
74
            $options['body'] = $parameter;
75
        }
76
77
        return $this->request('post', $gateway, $options);
78
    }
79
80
    /**
81
     * Send a request.
82
     *
83
     * @param string $method
84
     * @param string $gateway
85
     * @param array  $options
86
     *
87
     * @return mixed
88
     * @throws \RuntimeException
89
     */
90
    public function request(
91
        string $method,
92
        string $gateway,
93
        array $options = []
94
    ) {
95
        if (property_exists($this, 'config')) {
96
            $baseuri = $this->config->get('env') === 'dev'
97
                ? Constant::WX_PAY_DEV_URI
98
                : Constant::WX_PAY_PRO_URI;
99
        }
100
        $baseuri = $baseuri ?? '';
101
        $timeout = property_exists($this, 'timeout') ? $this->timeout : 5.0;
102
        $config = ['base_uri' => $baseuri, 'timeout' => $timeout];
103
104
        $client = new Client($config);
105
        $response = $client->{$method}($gateway, $options);
106
107
        return $this->jsonResponse($response);
108
    }
109
110
    /**
111
     * Convert array to xml.
112
     *
113
     * @param array $array
114
     *
115
     * @return string
116
     * @throws \InvalidArgumentException
117
     */
118
    public static function toXml(array $array) : string
119
    {
120
        if (! $array) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $array of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
introduced by
The condition ! $array can never be false.
Loading history...
121
            throw new \InvalidArgumentException('Invalid [array] argument.', 2);
122
        }
123
124
        $xml = '<xml>';
125
        foreach ($array as $key => $val) {
126
            $xml .= is_numeric($val)
127
                ? "<{$key}>{$val}</{$key}>"
128
                : "<{$key}><![CDATA[{$val}]]></{$key}>";
129
        }
130
        $xml .= '</xml>';
131
132
        return $xml;
133
    }
134
135
    /**
136
     * Convert xml to array.
137
     *
138
     * @param string $xml
139
     *
140
     * @return array
141
     * @throws \InvalidArgumentException
142
     */
143
    public static function fromXml(string $xml) : array
144
    {
145
        if (! $xml) {
146
            throw new \InvalidArgumentException('Invalid [xml] argument.', 3);
147
        }
148
        libxml_disable_entity_loader(true);
149
        $array = simplexml_load_string(
150
            $xml,
151
            'SimpleXMLElement',
152
            LIBXML_NOCDATA
153
        );
154
        $array = json_decode(
155
            json_encode($array, JSON_UNESCAPED_UNICODE),
156
            true
157
        );
158
159
        return $array;
160
    }
161
162
    /**
163
     * Decodes a json/javascript/xml response contents.
164
     *
165
     * @param \Psr\Http\Message\ResponseInterface $response
166
     *
167
     * @return mixed
168
     * @throws \RuntimeException
169
     */
170
    protected function jsonResponse(ResponseInterface $response)
171
    {
172
        $type = $response->getHeaderLine('Content-Type');
173
        $content = $response->getBody()->getContents();
174
175
        if (false !== self::contains($type, 'json')) {
0 ignored issues
show
introduced by
The condition false !== self::contains($type, 'json') can never be false.
Loading history...
176
            $content = json_decode($content, true);
177
        }
178
179
        return $content;
180
    }
181
182
    /**
183
     * Find the position of the first occurrence of a substring in a string.
184
     *
185
     * @param string $needle
186
     * @param string $haystack
187
     * @param bool   $isStrict
188
     *
189
     * @return bool|int
190
     */
191
    private static function contains(
192
        string $needle,
193
        string $haystack,
194
        bool $isStrict = false
195
    ) {
196
        return $isStrict
197
            ? strpos($haystack, $needle)
198
            : stripos($haystack, $needle);
199
    }
200
}
201