Completed
Pull Request — master (#30)
by Ryan
01:39
created

Wechat::getResult()   D

Complexity

Conditions 9
Paths 24

Size

Total Lines 35
Code Lines 21

Duplication

Lines 4
Ratio 11.43 %

Importance

Changes 0
Metric Value
dl 4
loc 35
rs 4.909
c 0
b 0
f 0
cc 9
eloc 21
nc 24
nop 2
1
<?php
2
3
namespace Yansongda\Pay\Gateways\Wechat;
4
5
use Yansongda\Pay\Contracts\GatewayInterface;
6
use Yansongda\Pay\Exceptions\GatewayException;
7
use Yansongda\Pay\Exceptions\InvalidArgumentException;
8
use Yansongda\Pay\Support\Config;
9
use Yansongda\Pay\Traits\HasHttpRequest;
10
11
abstract class Wechat implements GatewayInterface
12
{
13
    use HasHttpRequest;
14
15
    /**
16
     * @var string
17
     */
18
    protected $endpoint = 'https://api.mch.weixin.qq.com/';
19
20
    /**
21
     * @var string
22
     */
23
    protected $gateway_order = 'pay/unifiedorder';
24
25
    /**
26
     * @var string
27
     */
28
    protected $gateway_query = 'pay/orderquery';
29
30
    /**
31
     * @var string
32
     */
33
    protected $gateway_close = 'pay/closeorder';
34
35
    /**
36
     * @var string
37
     */
38
    protected $gateway_refund = 'secapi/pay/refund';
39
40
    /**
41
     * @var array
42
     */
43
    protected $config;
44
45
    /**
46
     * @var \Yansongda\Pay\Support\Config
47
     */
48
    protected $user_config;
49
50
    /**
51
     * [__construct description].
52
     *
53
     * @author yansongda <[email protected]>
54
     *
55
     * @param array $config
56
     */
57
    public function __construct(array $config)
58
    {
59
        $this->user_config = new Config($config);
60
61
        $this->config = [
62
            'appid'      => $this->user_config->get('app_id', ''),
63
            'mch_id'     => $this->user_config->get('mch_id', ''),
64
            'nonce_str'  => $this->createNonceStr(),
65
            'sign_type'  => 'MD5',
66
            'notify_url' => $this->user_config->get('notify_url', ''),
67
            'trade_type' => $this->getTradeType(),
68
        ];
69
70
        if ($endpoint = $this->user_config->get('endpoint_url')) {
71
            $this->endpoint = $endpoint;
72
        }
73
    }
74
75
    /**
76
     * pay a order.
77
     *
78
     * @author yansongda <[email protected]>
79
     *
80
     * @param array $config_biz
81
     *
82
     * @return mixed
83
     */
84
    abstract public function pay(array $config_biz = []);
85
86
    /**
87
     * refund.
88
     *
89
     * @author yansongda <[email protected]>
90
     *
91
     * @return string|bool
92
     */
93
    public function refund($config_biz = [])
94
    {
95
        $this->config = array_merge($this->config, $config_biz);
96
        $this->config['op_user_id'] = isset($this->config['op_user_id']) ?: $this->user_config->get('mch_id', '');
97
98
        $this->unsetTradeTypeAndNotifyUrl();
99
100
        return $this->getResult($this->gateway_refund, true);
101
    }
102
103
    /**
104
     * close a order.
105
     *
106
     * @author yansongda <[email protected]>
107
     *
108
     * @return array|bool
109
     */
110
    public function close($out_trade_no = '')
111
    {
112
        $this->config['out_trade_no'] = $out_trade_no;
113
114
        $this->unsetTradeTypeAndNotifyUrl();
115
116
        return $this->getResult($this->gateway_close);
117
    }
118
119
    /**
120
     * find a order.
121
     *
122
     * @author yansongda <[email protected]>
123
     *
124
     * @param string $out_trade_no
125
     *
126
     * @return array|bool
127
     */
128
    public function find($out_trade_no = '')
129
    {
130
        $this->config['out_trade_no'] = $out_trade_no;
131
132
        $this->unsetTradeTypeAndNotifyUrl();
133
134
        return $this->getResult($this->gateway_query);
135
    }
136
137
    /**
138
     * verify the notify.
139
     *
140
     * @author yansongda <[email protected]>
141
     *
142
     * @param string $data
143
     * @param string $sign
144
     * @param bool   $sync
145
     *
146
     * @return array|bool
147
     */
148
    public function verify($data, $sign = null, $sync = false)
149
    {
150
        $data = $this->fromXml($data);
151
152
        $sign = is_null($sign) ? $data['sign'] : $sign;
153
154
        return $this->getSign($data) === $sign ? $data : false;
155
    }
156
157
    /**
158
     * get trade type config.
159
     *
160
     * @author yansongda <[email protected]>
161
     *
162
     * @return string
163
     */
164
    abstract protected function getTradeType();
165
166
    /**
167
     * pre order.
168
     *
169
     * @author yansongda <[email protected]>
170
     *
171
     * @param array $config_biz
172
     *
173
     * @return array
174
     */
175
    protected function preOrder($config_biz = [])
176
    {
177
        $this->config = array_merge($this->config, $config_biz);
178
179
        return $this->getResult($this->gateway_order);
180
    }
181
182
    /**
183
     * get api result.
184
     *
185
     * @author yansongda <[email protected]>
186
     *
187
     * @param string $path
188
     * @param bool   $cert
189
     *
190
     * @return array
191
     */
192
    protected function getResult($path, $cert = false)
193
    {
194
        $this->config['sign'] = $this->getSign($this->config);
195
196
        if ($cert) {
197
            $data = $this->fromXml($this->post(
0 ignored issues
show
Documentation introduced by
$this->post($this->endpo...->get('cert_key', ''))) is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
198
                $this->endpoint . $path,
199
                $this->toXml($this->config),
200
                [
201
                    'cert'    => $this->user_config->get('cert_client', ''),
202
                    'ssl_key' => $this->user_config->get('cert_key', ''),
203
                ]
204
            ));
205
        } else {
206
            $data = $this->fromXml($this->post($this->endpoint . $path, $this->toXml($this->config)));
0 ignored issues
show
Documentation introduced by
$this->post($this->endpo...->toXml($this->config)) is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
207
        }
208
209 View Code Duplication
        if (!isset($data['return_code']) || $data['return_code'] !== 'SUCCESS' || $data['result_code'] !== 'SUCCESS') {
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...
210
            $error = 'getResult error:'.$data['return_msg'];
211
            $error .= isset($data['err_code_des']) ? ' - '.$data['err_code_des'] : '';
212
        }
213
214
        if (!isset($error) && $this->getSign($data) !== $data['sign']) {
215
            $error = 'getResult error: return data sign error';
216
        }
217
218
        if (isset($error)) {
219
            throw new GatewayException(
220
                $error,
221
                20000,
222
                $data);
223
        }
224
225
        return $data;
226
    }
227
228
    /**
229
     * sign.
230
     *
231
     * @author yansongda <[email protected]>
232
     *
233
     * @param array $data
234
     *
235
     * @return string
236
     */
237
    protected function getSign($data)
238
    {
239
        if (is_null($this->user_config->get('key'))) {
240
            throw new InvalidArgumentException('Missing Config -- [key]');
241
        }
242
243
        ksort($data);
244
245
        $string = md5($this->getSignContent($data).'&key='.$this->user_config->get('key'));
246
247
        return strtoupper($string);
248
    }
249
250
    /**
251
     * get sign content.
252
     *
253
     * @author yansongda <[email protected]>
254
     *
255
     * @param array $data
256
     *
257
     * @return string
258
     */
259
    protected function getSignContent($data)
260
    {
261
        $buff = '';
262
263
        foreach ($data as $k => $v) {
264
            $buff .= ($k != 'sign' && $v != '' && !is_array($v)) ? $k.'='.$v.'&' : '';
265
        }
266
267
        return trim($buff, '&');
268
    }
269
270
    /**
271
     * create random string.
272
     *
273
     * @author yansongda <[email protected]>
274
     *
275
     * @param int $length
276
     *
277
     * @return string
278
     */
279
    protected function createNonceStr($length = 16)
280
    {
281
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
282
283
        $str = '';
284
        for ($i = 0; $i < $length; $i++) {
285
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
286
        }
287
288
        return $str;
289
    }
290
291
    /**
292
     * convert to xml.
293
     *
294
     * @author yansongda <[email protected]>
295
     *
296
     * @param array $data
297
     *
298
     * @return string
299
     */
300
    protected function toXml($data)
301
    {
302
        if (!is_array($data) || count($data) <= 0) {
303
            throw new InvalidArgumentException('convert to xml error!invalid array!');
304
        }
305
306
        $xml = '<xml>';
307
        foreach ($data as $key => $val) {
308
            $xml .= is_numeric($val) ? '<'.$key.'>'.$val.'</'.$key.'>' :
309
                                       '<'.$key.'><![CDATA['.$val.']]></'.$key.'>';
310
        }
311
        $xml .= '</xml>';
312
313
        return $xml;
314
    }
315
316
    /**
317
     * convert to array.
318
     *
319
     * @author yansongda <[email protected]>
320
     *
321
     * @param string $xml
322
     *
323
     * @return array
324
     */
325
    protected function fromXml($xml)
326
    {
327
        if (!$xml) {
328
            throw new InvalidArgumentException('convert to array error !invalid xml');
329
        }
330
331
        libxml_disable_entity_loader(true);
332
333
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);
334
    }
335
336
    /**
337
     * delete trade_type and notify_url.
338
     *
339
     * @author yansongda <[email protected]>
340
     *
341
     * @return bool
342
     */
343
    protected function unsetTradeTypeAndNotifyUrl()
344
    {
345
        unset($this->config['notify_url']);
346
        unset($this->config['trade_type']);
347
348
        return true;
349
    }
350
}
351