Completed
Push — master ( bf6a7e...87baad )
by Songda
01:52
created

Wechat::getResult()   D

Complexity

Conditions 9
Paths 24

Size

Total Lines 28
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 28
c 0
b 0
f 0
rs 4.909
cc 9
eloc 17
nc 24
nop 2
1
<?php
2
3
namespace Yansongda\Pay\Gateways\Wechat;
4
5
use Yansongda\Pay\Support\Config;
6
use Yansongda\Pay\Traits\HasHttpRequest;
7
use Yansongda\Pay\Contracts\GatewayInterface;
8
use Yansongda\Pay\Exceptions\GatewayException;
9
use Yansongda\Pay\Exceptions\InvalidArgumentException;
10
11
/**
12
 * abstract class Wechat.
13
 */
14
abstract class Wechat implements GatewayInterface
15
{
16
    use HasHttpRequest;
17
18
    /**
19
     * 支付订单.
20
     *
21
     * @var string
22
     */
23
    protected $gateway = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
24
25
    /**
26
     * 查询订单.
27
     * 
28
     * @var string
29
     */
30
    protected $gateway_query = 'https://api.mch.weixin.qq.com/pay/orderquery';
31
32
    /**
33
     * 关闭订单.
34
     * 
35
     * @var string
36
     */
37
    protected $gateway_close = 'https://api.mch.weixin.qq.com/pay/closeorder';
38
39
    /**
40
     * 退款.
41
     * 
42
     * @var string
43
     */
44
    protected $gateway_refund = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
45
46
    /**
47
     * 配置项.
48
     *
49
     * @var array
50
     */
51
    protected $config;
52
53
    /**
54
     * 用户传参配置项.
55
     *
56
     * @var Yansongda\Pay\Support\Config
57
     */
58
    protected $user_config;
59
60
    /**
61
     * [__construct description].
62
     *
63
     * @author yansongda <[email protected]>
64
     *
65
     * @version 2017-08-14
66
     *
67
     * @param   array      $config [description]
68
     */
69
    public function __construct(array $config)
70
    {
71
        $this->user_config = new Config($config);
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Yansongda\Pay\Support\Config($config) of type object<Yansongda\Pay\Support\Config> is incompatible with the declared type object<Yansongda\Pay\Gat...gda\Pay\Support\Config> of property $user_config.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
72
73
        $this->config = [
74
            'appid' => $this->user_config->get('app_id', ''),
75
            'mch_id' => $this->user_config->get('mch_id', ''),
76
            'nonce_str' => $this->createNonceStr(),
77
            'sign_type' => 'MD5',
78
            'notify_url' => $this->user_config->get('notify_url', ''),
79
            'trade_type' => $this->getTradeType(),
80
        ];
81
    }
82
83
    /**
84
     * 对外支付接口.
85
     *
86
     * @author yansongda <[email protected]>
87
     *
88
     * @version 2017-08-15
89
     *
90
     * @param   array      $config_biz [description]
91
     *
92
     * @return  mixed                  [description]
93
     */
94
    abstract public function pay(array $config_biz = []);
95
96
    /**
97
     * 对外接口 - 退款.
98
     *
99
     * @author yansongda <[email protected]>
100
     *
101
     * @version 2017-08-15
102
     *
103
     * @return  [type]     [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
104
     */
105
    public function refund(array $config_biz = [])
106
    {
107
        $this->config = array_merge($this->config, $config_biz);
108
        $this->config['total_fee'] = intval($this->config['total_fee'] * 100);
109
        $this->config['refund_fee'] = intval($this->config['refund_fee'] * 100);
110
        $this->config['op_user_id'] = isset($this->config['op_user_id']) ?: $this->user_config->get('mch_id', '');
111
112
        return $this->getResult($this->gateway_refund, true);
113
    }
114
115
    /**
116
     * 对外接口 - 关闭订单.
117
     *
118
     * @author yansongda <[email protected]>
119
     *
120
     * @version 2017-08-15
121
     *
122
     * @return  array|boolean     [description]
123
     */
124 View Code Duplication
    public function close($out_trade_no = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
125
    {
126
        unset($this->config['notify_url']);
127
        unset($this->config['trade_type']);
128
        $this->config['out_trade_no'] = $out_trade_no;
129
130
        return $this->getResult($this->gateway_close);
131
    }
132
133
    /**
134
     * 对外接口 - 订单查询
135
     * @author yansongda <[email protected]>
136
     * 
137
     * @version 2017-08-19
138
     * 
139
     * @param   string     $out_trade_no 商家订单号
140
     * 
141
     * @return  array|boolean            [description]
142
     */
143 View Code Duplication
    public function find($out_trade_no = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
144
    {
145
        unset($this->config['notify_url']);
146
        unset($this->config['trade_type']);
147
        
148
        $this->config['out_trade_no'] = $out_trade_no;
149
150
        return $this->getResult($this->gateway_query);
151
    }
152
153
    /**
154
     * 验证签名.
155
     *
156
     * @author yansongda <[email protected]>
157
     *
158
     * @version 2017-08-15
159
     *
160
     * @param   string     $data 待验证 xml 数据
161
     * @param   string     $sign 服务器返回的签名
162
     * @param   bool       $sync is sync sign
163
     *
164
     * @return  array|boolean    是否相符
165
     */
166
    public function verify($data, $sign = null, $sync = false)
167
    {
168
        $data = $this->fromXml($data);
169
170
        $sign = is_null($sign) ? $data['sign'] : $sign;
171
172
        return $this->getSign($data) === $sign ? $data : false;
173
    }
174
175
    /**
176
     * 获取交易类型.
177
     * @author yansongda <[email protected]>
178
     * 
179
     * @version 2017-08-17
180
     * 
181
     * @return  string     [description]
182
     */
183
    abstract protected function getTradeType();
184
185
    /**
186
     * 预下单.
187
     *
188
     * @author yansongda <[email protected]>
189
     *
190
     * @version 2017-08-15
191
     *
192
     * @param   array  $config_biz  业务参数
193
     *
194
     * @return  array               服务器返回结果数组
195
     */
196
    protected function preOrder($config_biz = [])
197
    {
198
        $this->config = array_merge($this->config, $config_biz);
199
        $this->config['total_fee'] = intval($this->config['total_fee'] * 100);
200
201
        return $this->getResult($this->gateway);
202
    }
203
204
    /**
205
     * 获取 API 结果.
206
     * 
207
     * @author yansongda <[email protected]>
208
     * 
209
     * @version 2017-08-19
210
     * 
211
     * @param   string     $end_point [description]
212
     * @param   bool       $cert      是否使用证书
213
     * 
214
     * @return  [type]                [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
215
     */
216
    protected function getResult($end_point, $cert = false)
217
    {
218
        $this->config['sign'] = $this->getSign($this->config);
219
220
        if ($cert) {
221
            $data = $this->fromXml($this->post($end_point, $this->toXml($this->config), ['cert' => $this->user_config->get('cert_client', ''), 'ssl_key' => $this->user_config->get('cert_key', '')]));
0 ignored issues
show
Documentation introduced by
$this->post($end_point, ...->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...
222
        } else {
223
            $data = $this->fromXml($this->post($end_point, $this->toXml($this->config)));
0 ignored issues
show
Documentation introduced by
$this->post($end_point, ...->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...
224
        }
225
226
        if (!isset($data['return_code']) || $data['return_code'] !== 'SUCCESS' || $data['result_code'] !== 'SUCCESS') {
227
            $error = 'getResult error:' . $data['return_msg'];
228
            $error .= isset($data['err_code_des']) ? ' - ' . $data['err_code_des'] : '';
229
        }
230
231
        if (!isset($error) && $this->getSign($data) !== $data['sign']) {
232
            $error = 'getResult error: return data sign error';
233
        }
234
235
        if (isset($error)) {
236
            throw new GatewayException(
237
                $error,
238
                20000,
239
                $data);
240
        }
241
242
        return $data;
243
    }
244
245
    /**
246
     * 签名.
247
     *
248
     * @author yansongda <[email protected]>
249
     * 
250
     * @version 2017-08-15
251
     *
252
     * @param   array      $data 带签名数组
253
     *
254
     * @return  string           [description]
255
     */
256
    protected function getSign($data)
257
    {
258
        if (is_null($this->user_config->get('key'))) {
259
            throw new InvalidArgumentException("Missing Config -- [key]");
260
        }
261
262
        ksort($data);
263
264
        $string = md5($this->getSignContent($data) . "&key=" . $this->user_config->get('key'));
265
266
        return strtoupper($string);
267
    }
268
269
    /**
270
     * 将数组转换成 URL 格式.
271
     *
272
     * @author yansongda <[email protected]>
273
     *
274
     * @version 2017-08-15
275
     *
276
     * @param   array      $data [description]
277
     *
278
     * @return  string           [description]
279
     */
280
    protected function getSignContent($data)
281
    {
282
        $buff = "";
283
284
        foreach ($data as $k => $v)
285
        {
286
            if ($k != "sign" && $v != "" && !is_array($v)) {
287
                $buff .= $k . "=" . $v . "&";
288
            }
289
        }
290
        
291
        return trim($buff, "&");
292
    }
293
294
    /**
295
     * 生成随机字符串.
296
     *
297
     * @author yansongda <[email protected]>
298
     *
299
     * @version 2017-08-14
300
     *
301
     * @param   integer    $length [description]
302
     *
303
     * @return  string             [description]
304
     */
305
    protected function createNonceStr($length = 16) {
306
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
307
308
        $str = "";
309
        for ($i = 0; $i < $length; $i++) {
310
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
311
        }
312
313
        return $str;
314
    }
315
316
    /**
317
     * 转化为 xml.
318
     *
319
     * @author yansongda <[email protected]>
320
     *
321
     * @version 2017-08-14
322
     *
323
     * @param   array      $data 带转化数组
324
     *
325
     * @return  string           转化后的xml字符串
326
     */
327
    protected function toXml($data)
328
    {
329
        if (!is_array($data) || count($data) <= 0) {
330
            throw new InvalidArgumentException("convert to xml error!invalid array!");
331
        }
332
        
333
        $xml = "<xml>";
334
        foreach ($data as $key => $val) {
335
            if (is_numeric($val)) {
336
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
337
            } else {
338
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
339
            }
340
        }
341
        $xml .= "</xml>";
342
343
        return $xml; 
344
    }
345
346
    /**
347
     * xml 转化为 array.
348
     *
349
     * @author yansongda <[email protected]>
350
     *
351
     * @version 2017-08-14
352
     *
353
     * @param   string     $xml xml字符串
354
     *
355
     * @return  array           转化后的数组
356
     */
357
    protected function fromXml($xml)
358
    {   
359
        if (!$xml) {
360
            throw new InvalidArgumentException("convert to array error !invalid xml");
361
        }
362
363
        libxml_disable_entity_loader(true);
364
365
        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);        
366
    }
367
}
368