Completed
Pull Request — master (#660)
by mingyoung
28:40 queued 09:53
created

API::safeRequest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 3
dl 0
loc 9
ccs 5
cts 5
cp 1
crap 1
rs 9.6666
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of the overtrue/wechat.
5
 *
6
 * (c) overtrue <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
/**
13
 * API.php.
14
 *
15
 * @author    overtrue <[email protected]>
16
 * @copyright 2015 overtrue <[email protected]>
17
 *
18
 * @see      https://github.com/overtrue
19
 * @see      http://overtrue.me
20
 */
21
22
namespace EasyWeChat\Payment;
23
24
use EasyWeChat\Core\AbstractAPI;
25
use EasyWeChat\Support\Collection;
26
use EasyWeChat\Support\XML;
27
use Psr\Http\Message\ResponseInterface;
28
29
/**
30
 * Class API.
31
 */
32
class API extends AbstractAPI
33
{
34
    /**
35
     * Merchant instance.
36
     *
37
     * @var Merchant
38
     */
39
    protected $merchant;
40
41
    /**
42
     * Sandbox box mode.
43
     *
44
     * @var bool
45
     */
46
    protected $sandboxEnabled = false;
47
48
    const API_HOST = 'https://api.mch.weixin.qq.com';
49
50
    // api
51
    const API_PAY_ORDER = '/pay/micropay';
52
    const API_PREPARE_ORDER = '/pay/unifiedorder';
53
    const API_QUERY = '/pay/orderquery';
54
    const API_CLOSE = '/pay/closeorder';
55
    const API_REVERSE = '/secapi/pay/reverse';
56
    const API_REFUND = '/secapi/pay/refund';
57
    const API_QUERY_REFUND = '/pay/refundquery';
58
    const API_DOWNLOAD_BILL = '/pay/downloadbill';
59
    const API_REPORT = '/payitil/report';
60
61
    const API_URL_SHORTEN = 'https://api.mch.weixin.qq.com/tools/shorturl';
62
    const API_AUTH_CODE_TO_OPENID = 'https://api.mch.weixin.qq.com/tools/authcodetoopenid';
63
64
    // order id types.
65
    const TRANSACTION_ID = 'transaction_id';
66
    const OUT_TRADE_NO = 'out_trade_no';
67
    const OUT_REFUND_NO = 'out_refund_no';
68
    const REFUND_ID = 'refund_id';
69
70
    // bill types.
71
    const BILL_TYPE_ALL = 'ALL';
72
    const BILL_TYPE_SUCCESS = 'SUCCESS';
73
    const BILL_TYPE_REFUND = 'REFUND';
74
    const BILL_TYPE_REVOKED = 'REVOKED';
75
76
    /**
77
     * API constructor.
78
     *
79
     * @param \EasyWeChat\Payment\Merchant $merchant
80
     */
81 12
    public function __construct(Merchant $merchant)
82
    {
83 12
        $this->merchant = $merchant;
84 12
    }
85
86
    /**
87
     * Pay the order.
88
     *
89
     * @param Order $order
90
     *
91
     * @return \EasyWeChat\Support\Collection
92
     */
93 1
    public function pay(Order $order)
94
    {
95 1
        return $this->request($this->wrapApi(self::API_PAY_ORDER), $order->all());
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request($this->wr...ORDER), $order->all()); of type EasyWeChat\Support\Colle...ssage\ResponseInterface adds the type Psr\Http\Message\ResponseInterface to the return on line 95 which is incompatible with the return type documented by EasyWeChat\Payment\API::pay of type EasyWeChat\Support\Collection.
Loading history...
96
    }
97
98
    /**
99
     * Prepare order to pay.
100
     *
101
     * @param Order $order
102
     *
103
     * @return \EasyWeChat\Support\Collection
104
     */
105 1
    public function prepare(Order $order)
106
    {
107 1
        $order->notify_url = $order->get('notify_url', $this->merchant->notify_url);
0 ignored issues
show
Documentation introduced by
The property notify_url does not exist on object<EasyWeChat\Payment\Merchant>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
108 1
        if (is_null($order->spbill_create_ip)) {
109 1
            $order->spbill_create_ip = ($order->trade_type === Order::NATIVE) ? get_server_ip() : get_client_ip();
110 1
        }
111
112 1
        return $this->request($this->wrapApi(self::API_PREPARE_ORDER), $order->all());
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request($this->wr...ORDER), $order->all()); of type EasyWeChat\Support\Colle...ssage\ResponseInterface adds the type Psr\Http\Message\ResponseInterface to the return on line 112 which is incompatible with the return type documented by EasyWeChat\Payment\API::prepare of type EasyWeChat\Support\Collection.
Loading history...
113
    }
114
115
    /**
116
     * Query order.
117
     *
118
     * @param string $orderNo
119
     * @param string $type
120
     *
121
     * @return \EasyWeChat\Support\Collection
122
     */
123 1 View Code Duplication
    public function query($orderNo, $type = self::OUT_TRADE_NO)
124
    {
125
        $params = [
126 1
            $type => $orderNo,
127 1
        ];
128
129 1
        return $this->request($this->wrapApi(self::API_QUERY), $params);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request($this->wr...::API_QUERY), $params); of type EasyWeChat\Support\Colle...ssage\ResponseInterface adds the type Psr\Http\Message\ResponseInterface to the return on line 129 which is incompatible with the return type documented by EasyWeChat\Payment\API::query of type EasyWeChat\Support\Collection.
Loading history...
130
    }
131
132
    /**
133
     * Query order by transaction_id.
134
     *
135
     * @param string $transactionId
136
     *
137
     * @return \EasyWeChat\Support\Collection
138
     */
139 1
    public function queryByTransactionId($transactionId)
140
    {
141 1
        return $this->query($transactionId, self::TRANSACTION_ID);
142
    }
143
144
    /**
145
     * Close order by out_trade_no.
146
     *
147
     * @param $tradeNo
148
     *
149
     * @return \EasyWeChat\Support\Collection
150
     */
151 1
    public function close($tradeNo)
152
    {
153
        $params = [
154 1
            'out_trade_no' => $tradeNo,
155 1
        ];
156
157 1
        return $this->request($this->wrapApi(self::API_CLOSE), $params);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request($this->wr...::API_CLOSE), $params); of type EasyWeChat\Support\Colle...ssage\ResponseInterface adds the type Psr\Http\Message\ResponseInterface to the return on line 157 which is incompatible with the return type documented by EasyWeChat\Payment\API::close of type EasyWeChat\Support\Collection.
Loading history...
158
    }
159
160
    /**
161
     * Reverse order.
162
     *
163
     * @param string $orderNo
164
     * @param string $type
165
     *
166
     * @return \EasyWeChat\Support\Collection
167
     */
168 1
    public function reverse($orderNo, $type = self::OUT_TRADE_NO)
169
    {
170
        $params = [
171 1
            $type => $orderNo,
172 1
        ];
173
174 1
        return $this->safeRequest($this->wrapApi(self::API_REVERSE), $params);
175
    }
176
177
    /**
178
     * Reverse order by transaction_id.
179
     *
180
     * @param int $transactionId
181
     *
182
     * @return \EasyWeChat\Support\Collection
183
     */
184
    public function reverseByTransactionId($transactionId)
185
    {
186
        return $this->reverse($transactionId, self::TRANSACTION_ID);
187
    }
188
189
    /**
190
     * Make a refund request.
191
     *
192
     * @param string $orderNo
193
     * @param float  $totalFee
194
     * @param float  $refundFee
195
     * @param string $opUserId
196
     * @param string $type
197
     * @param string $refundAccount
198
     *
199
     * @return \EasyWeChat\Support\Collection
200
     */
201 1
    public function refund(
202
        $orderNo,
203
        $refundNo,
204
        $totalFee,
205
        $refundFee = null,
206
        $opUserId = null,
207
        $type = self::OUT_TRADE_NO,
208
        $refundAccount = 'REFUND_SOURCE_UNSETTLED_FUNDS'
209
        ) {
210
        $params = [
211 1
            $type => $orderNo,
212 1
            'out_refund_no' => $refundNo,
213 1
            'total_fee' => $totalFee,
214 1
            'refund_fee' => $refundFee ?: $totalFee,
215 1
            'refund_fee_type' => $this->merchant->fee_type,
216 1
            'refund_account' => $refundAccount,
217 1
            'op_user_id' => $opUserId ?: $this->merchant->merchant_id,
218 1
        ];
219
220 1
        return $this->safeRequest($this->wrapApi(self::API_REFUND), $params);
221
    }
222
223
    /**
224
     * Refund by transaction id.
225
     *
226
     * @param string $orderNo
227
     * @param float  $totalFee
228
     * @param float  $refundFee
229
     * @param string $opUserId
230
     * @param string $refundAccount
231
     *
232
     * @return \EasyWeChat\Support\Collection
233
     */
234
    public function refundByTransactionId(
235
        $orderNo,
236
        $refundNo,
237
        $totalFee,
238
        $refundFee = null,
239
        $opUserId = null,
240
        $refundAccount = 'REFUND_SOURCE_UNSETTLED_FUNDS'
241
        ) {
242
        return $this->refund($orderNo, $refundNo, $totalFee, $refundFee, $opUserId, self::TRANSACTION_ID, $refundAccount);
243
    }
244
245
    /**
246
     * Query refund status.
247
     *
248
     * @param string $orderNo
249
     * @param string $type
250
     *
251
     * @return \EasyWeChat\Support\Collection
252
     */
253 1 View Code Duplication
    public function queryRefund($orderNo, $type = self::OUT_TRADE_NO)
254
    {
255
        $params = [
256 1
            $type => $orderNo,
257 1
        ];
258
259 1
        return $this->request($this->wrapApi(self::API_QUERY_REFUND), $params);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request($this->wr...UERY_REFUND), $params); of type EasyWeChat\Support\Colle...ssage\ResponseInterface adds the type Psr\Http\Message\ResponseInterface to the return on line 259 which is incompatible with the return type documented by EasyWeChat\Payment\API::queryRefund of type EasyWeChat\Support\Collection.
Loading history...
260
    }
261
262
    /**
263
     * Query refund status by out_refund_no.
264
     *
265
     * @param string $refundNo
266
     *
267
     * @return \EasyWeChat\Support\Collection
268
     */
269
    public function queryRefundByRefundNo($refundNo)
270
    {
271
        return $this->queryRefund($refundNo, self::OUT_REFUND_NO);
272
    }
273
274
    /**
275
     * Query refund status by transaction_id.
276
     *
277
     * @param string $transactionId
278
     *
279
     * @return \EasyWeChat\Support\Collection
280
     */
281
    public function queryRefundByTransactionId($transactionId)
282
    {
283
        return $this->queryRefund($transactionId, self::TRANSACTION_ID);
284
    }
285
286
    /**
287
     * Query refund status by refund_id.
288
     *
289
     * @param string $refundId
290
     *
291
     * @return \EasyWeChat\Support\Collection
292
     */
293
    public function queryRefundByRefundId($refundId)
294
    {
295
        return $this->queryRefund($refundId, self::REFUND_ID);
296
    }
297
298
    /**
299
     * Download bill history as a table file.
300
     *
301
     * @param string $date
302
     * @param string $type
303
     *
304
     * @return \Psr\Http\Message\ResponseInterface
305
     */
306 1
    public function downloadBill($date, $type = self::BILL_TYPE_ALL)
307
    {
308
        $params = [
309 1
            'bill_date' => $date,
310 1
            'bill_type' => $type,
311 1
        ];
312
313 1
        return $this->request($this->wrapApi(self::API_DOWNLOAD_BILL), $params, 'post', [\GuzzleHttp\RequestOptions::STREAM => true], true)->getBody();
0 ignored issues
show
Bug introduced by
The method getBody does only exist in Psr\Http\Message\ResponseInterface, but not in EasyWeChat\Support\Collection.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
314
    }
315
316
    /**
317
     * Convert long url to short url.
318
     *
319
     * @param string $url
320
     *
321
     * @return \EasyWeChat\Support\Collection
322
     */
323 1
    public function urlShorten($url)
324
    {
325 1
        return $this->request(self::API_URL_SHORTEN, ['long_url' => $url]);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request(self::API...y('long_url' => $url)); of type EasyWeChat\Support\Colle...ssage\ResponseInterface adds the type Psr\Http\Message\ResponseInterface to the return on line 325 which is incompatible with the return type documented by EasyWeChat\Payment\API::urlShorten of type EasyWeChat\Support\Collection.
Loading history...
326
    }
327
328
    /**
329
     * Report API status to WeChat.
330
     *
331
     * @param string $api
332
     * @param int    $timeConsuming
333
     * @param string $resultCode
334
     * @param string $returnCode
335
     * @param array  $other         ex: err_code,err_code_des,out_trade_no,user_ip...
336
     *
337
     * @return \EasyWeChat\Support\Collection
338
     */
339
    public function report($api, $timeConsuming, $resultCode, $returnCode, array $other = [])
340
    {
341
        $params = array_merge([
342
            'interface_url' => $api,
343
            'execute_time_' => $timeConsuming,
344
            'return_code' => $returnCode,
345
            'return_msg' => null,
346
            'result_code' => $resultCode,
347
            'user_ip' => get_client_ip(),
348
            'time' => time(),
349
        ], $other);
350
351
        return $this->request($this->wrapApi(self::API_REPORT), $params);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request($this->wr...:API_REPORT), $params); of type EasyWeChat\Support\Colle...ssage\ResponseInterface adds the type Psr\Http\Message\ResponseInterface to the return on line 351 which is incompatible with the return type documented by EasyWeChat\Payment\API::report of type EasyWeChat\Support\Collection.
Loading history...
352
    }
353
354
    /**
355
     * Get openid by auth code.
356
     *
357
     * @param string $authCode
358
     *
359
     * @return \EasyWeChat\Support\Collection
360
     */
361 1
    public function authCodeToOpenId($authCode)
362
    {
363 1
        return $this->request(self::API_AUTH_CODE_TO_OPENID, ['auth_code' => $authCode]);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request(self::API...h_code' => $authCode)); of type EasyWeChat\Support\Colle...ssage\ResponseInterface adds the type Psr\Http\Message\ResponseInterface to the return on line 363 which is incompatible with the return type documented by EasyWeChat\Payment\API::authCodeToOpenId of type EasyWeChat\Support\Collection.
Loading history...
364
    }
365
366
    /**
367
     * Merchant setter.
368
     *
369
     * @param Merchant $merchant
370
     *
371
     * @return $this
372
     */
373 1
    public function setMerchant(Merchant $merchant)
374
    {
375 1
        $this->merchant = $merchant;
376 1
    }
377
378
    /**
379
     * Merchant getter.
380
     *
381
     * @return Merchant
382
     */
383 1
    public function getMerchant()
384
    {
385 1
        return $this->merchant;
386
    }
387
388
    /**
389
     * Set sandbox mode.
390
     *
391
     * @param bool $enabled
392
     *
393
     * @return $this
394
     */
395 10
    public function sandboxMode($enabled = false)
396
    {
397 10
        $this->sandboxEnabled = $enabled;
398
399 10
        return $this;
400
    }
401
402
    /**
403
     * Make a API request.
404
     *
405
     * @param string $api
406
     * @param array  $params
407
     * @param string $method
408
     * @param array  $options
409
     * @param bool   $returnResponse
410
     *
411
     * @return \EasyWeChat\Support\Collection|\Psr\Http\Message\ResponseInterface
412
     */
413 10
    protected function request($api, array $params, $method = 'post', array $options = [], $returnResponse = false)
414
    {
415 10
        $params = array_merge($params, $this->merchant->only(['sub_appid', 'sub_mch_id']));
416
417 10
        $params['appid'] = $this->merchant->app_id;
418 10
        $params['mch_id'] = $this->merchant->merchant_id;
419 10
        $params['device_info'] = $this->merchant->device_info;
420 10
        $params['nonce_str'] = uniqid();
421 10
        $params = array_filter($params);
422 10
        $params['sign'] = generate_sign($params, $this->merchant->key, 'md5');
423
424 10
        $options = array_merge([
425 10
            'body' => XML::build($params),
426 10
        ], $options);
427
428 10
        $response = $this->getHttp()->request($api, $method, $options);
429
430 10
        return $returnResponse ? $response : $this->parseResponse($response);
431
    }
432
433
    /**
434
     * Request with SSL.
435
     *
436
     * @param string $api
437
     * @param array  $params
438
     * @param string $method
439
     *
440
     * @return \EasyWeChat\Support\Collection
441
     */
442 2
    protected function safeRequest($api, array $params, $method = 'post')
443
    {
444
        $options = [
445 2
            'cert' => $this->merchant->get('cert_path'),
446 2
            'ssl_key' => $this->merchant->get('key_path'),
447 2
        ];
448
449 2
        return $this->request($api, $params, $method, $options);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->request($api, $params, $method, $options); of type Psr\Http\Message\Respons...Chat\Support\Collection adds the type Psr\Http\Message\ResponseInterface to the return on line 449 which is incompatible with the return type documented by EasyWeChat\Payment\API::safeRequest of type EasyWeChat\Support\Collection.
Loading history...
450
    }
451
452
    /**
453
     * Parse Response XML to array.
454
     *
455
     * @param ResponseInterface $response
456
     *
457
     * @return \EasyWeChat\Support\Collection
458
     */
459 9
    protected function parseResponse($response)
460
    {
461 9
        if ($response instanceof ResponseInterface) {
462
            $response = $response->getBody();
463
        }
464
465 9
        return new Collection((array) XML::parse($response));
466
    }
467
468
    /**
469
     * Wrap API.
470
     *
471
     * @param string $resource
472
     *
473
     * @return string
474
     */
475 8
    protected function wrapApi($resource)
476
    {
477 8
        return self::API_HOST.($this->sandboxEnabled ? '/sandbox' : '').$resource;
478
    }
479
}
480