Passed
Pull Request — master (#666)
by
unknown
03:01
created

API::refundByTransactionId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 6
dl 0
loc 10
ccs 0
cts 2
cp 0
crap 2
rs 9.4285
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 Doctrine\Common\Cache\FilesystemCache;
25
use EasyWeChat\Core\AbstractAPI;
26
use EasyWeChat\Core\Exception;
27
use EasyWeChat\Support\Collection;
28
use EasyWeChat\Support\XML;
29
use Psr\Http\Message\ResponseInterface;
30
31
/**
32
 * Class API.
33
 */
34
class API extends AbstractAPI
35
{
36
    /**
37
     * Merchant instance.
38
     *
39
     * @var Merchant
40
     */
41
    protected $merchant;
42
43
    /**
44
     * Sandbox box mode.
45
     *
46
     * @var bool
47
     */
48
    protected $sandboxEnabled = false;
49
50
    /**
51
     * Sandbox sign key.
52
     *
53
     * @var string
54
     */
55
    protected $sandboxSignKey;
56
57
    const API_HOST = 'https://api.mch.weixin.qq.com';
58
59
    // api
60
    const API_PAY_ORDER = '/pay/micropay';
61
    const API_PREPARE_ORDER = '/pay/unifiedorder';
62
    const API_QUERY = '/pay/orderquery';
63
    const API_CLOSE = '/pay/closeorder';
64
    const API_REVERSE = '/secapi/pay/reverse';
65
    const API_REFUND = '/secapi/pay/refund';
66
    const API_QUERY_REFUND = '/pay/refundquery';
67
    const API_DOWNLOAD_BILL = '/pay/downloadbill';
68
    const API_REPORT = '/payitil/report';
69
70
    const API_URL_SHORTEN = 'https://api.mch.weixin.qq.com/tools/shorturl';
71
    const API_AUTH_CODE_TO_OPENID = 'https://api.mch.weixin.qq.com/tools/authcodetoopenid';
72
    const API_SANDBOX_SIGN_KEY = 'https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey';
73
74
    // order id types.
75
    const TRANSACTION_ID = 'transaction_id';
76
    const OUT_TRADE_NO = 'out_trade_no';
77
    const OUT_REFUND_NO = 'out_refund_no';
78
    const REFUND_ID = 'refund_id';
79
80
    // bill types.
81
    const BILL_TYPE_ALL = 'ALL';
82
    const BILL_TYPE_SUCCESS = 'SUCCESS';
83
    const BILL_TYPE_REFUND = 'REFUND';
84
    const BILL_TYPE_REVOKED = 'REVOKED';
85
86
    /**
87
     * API constructor.
88
     *
89
     * @param \EasyWeChat\Payment\Merchant $merchant
90
     */
91 12
    public function __construct(Merchant $merchant)
92
    {
93 12
        $this->merchant = $merchant;
94 12
    }
95
96
    /**
97
     * Pay the order.
98
     *
99
     * @param Order $order
100
     *
101
     * @return \EasyWeChat\Support\Collection
102
     */
103 1
    public function pay(Order $order)
104
    {
105 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 105 which is incompatible with the return type documented by EasyWeChat\Payment\API::pay of type EasyWeChat\Support\Collection.
Loading history...
106
    }
107
108
    /**
109
     * Prepare order to pay.
110
     *
111
     * @param Order $order
112
     *
113
     * @return \EasyWeChat\Support\Collection
114
     */
115 1
    public function prepare(Order $order)
116
    {
117 1
        $order->notify_url = $order->get('notify_url', $this->merchant->notify_url);
118 1
        if (is_null($order->spbill_create_ip)) {
119 1
            $order->spbill_create_ip = ($order->trade_type === Order::NATIVE) ? get_server_ip() : get_client_ip();
120 1
        }
121
122 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 122 which is incompatible with the return type documented by EasyWeChat\Payment\API::prepare of type EasyWeChat\Support\Collection.
Loading history...
123
    }
124
125
    /**
126
     * Query order.
127
     *
128
     * @param string $orderNo
129
     * @param string $type
130
     *
131
     * @return \EasyWeChat\Support\Collection
132
     */
133 1 View Code Duplication
    public function query($orderNo, $type = self::OUT_TRADE_NO)
134
    {
135
        $params = [
136 1
            $type => $orderNo,
137 1
        ];
138
139 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 139 which is incompatible with the return type documented by EasyWeChat\Payment\API::query of type EasyWeChat\Support\Collection.
Loading history...
140
    }
141
142
    /**
143
     * Query order by transaction_id.
144
     *
145
     * @param string $transactionId
146
     *
147
     * @return \EasyWeChat\Support\Collection
148
     */
149 1
    public function queryByTransactionId($transactionId)
150
    {
151 1
        return $this->query($transactionId, self::TRANSACTION_ID);
152
    }
153
154
    /**
155
     * Close order by out_trade_no.
156
     *
157
     * @param $tradeNo
158
     *
159
     * @return \EasyWeChat\Support\Collection
160
     */
161 1
    public function close($tradeNo)
162
    {
163
        $params = [
164 1
            'out_trade_no' => $tradeNo,
165 1
        ];
166
167 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 167 which is incompatible with the return type documented by EasyWeChat\Payment\API::close of type EasyWeChat\Support\Collection.
Loading history...
168
    }
169
170
    /**
171
     * Reverse order.
172
     *
173
     * @param string $orderNo
174
     * @param string $type
175
     *
176
     * @return \EasyWeChat\Support\Collection
177
     */
178 1
    public function reverse($orderNo, $type = self::OUT_TRADE_NO)
179
    {
180
        $params = [
181 1
            $type => $orderNo,
182 1
        ];
183
184 1
        return $this->safeRequest($this->wrapApi(self::API_REVERSE), $params);
185
    }
186
187
    /**
188
     * Reverse order by transaction_id.
189
     *
190
     * @param int $transactionId
191
     *
192
     * @return \EasyWeChat\Support\Collection
193
     */
194
    public function reverseByTransactionId($transactionId)
195
    {
196
        return $this->reverse($transactionId, self::TRANSACTION_ID);
197
    }
198
199
    /**
200
     * Make a refund request.
201
     *
202
     * @param string $orderNo
203
     * @param float  $totalFee
204
     * @param float  $refundFee
205
     * @param string $opUserId
206
     * @param string $type
207
     * @param string $refundAccount
208
     *
209
     * @return \EasyWeChat\Support\Collection
210
     */
211 1
    public function refund(
212
        $orderNo,
213
        $refundNo,
214
        $totalFee,
215
        $refundFee = null,
216
        $opUserId = null,
217
        $type = self::OUT_TRADE_NO,
218
        $refundAccount = 'REFUND_SOURCE_UNSETTLED_FUNDS'
219
        ) {
220
        $params = [
221 1
            $type => $orderNo,
222 1
            'out_refund_no' => $refundNo,
223 1
            'total_fee' => $totalFee,
224 1
            'refund_fee' => $refundFee ?: $totalFee,
225 1
            'refund_fee_type' => $this->merchant->fee_type,
226 1
            'refund_account' => $refundAccount,
227 1
            'op_user_id' => $opUserId ?: $this->merchant->merchant_id,
228 1
        ];
229
230 1
        return $this->safeRequest($this->wrapApi(self::API_REFUND), $params);
231
    }
232
233
    /**
234
     * Refund by transaction id.
235
     *
236
     * @param string $orderNo
237
     * @param float  $totalFee
238
     * @param float  $refundFee
239
     * @param string $opUserId
240
     * @param string $refundAccount
241
     *
242
     * @return \EasyWeChat\Support\Collection
243
     */
244
    public function refundByTransactionId(
245
        $orderNo,
246
        $refundNo,
247
        $totalFee,
248
        $refundFee = null,
249
        $opUserId = null,
250
        $refundAccount = 'REFUND_SOURCE_UNSETTLED_FUNDS'
251
        ) {
252
        return $this->refund($orderNo, $refundNo, $totalFee, $refundFee, $opUserId, self::TRANSACTION_ID, $refundAccount);
253
    }
254
255
    /**
256
     * Query refund status.
257
     *
258
     * @param string $orderNo
259
     * @param string $type
260
     *
261
     * @return \EasyWeChat\Support\Collection
262
     */
263 1 View Code Duplication
    public function queryRefund($orderNo, $type = self::OUT_TRADE_NO)
264
    {
265
        $params = [
266 1
            $type => $orderNo,
267 1
        ];
268
269 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 269 which is incompatible with the return type documented by EasyWeChat\Payment\API::queryRefund of type EasyWeChat\Support\Collection.
Loading history...
270
    }
271
272
    /**
273
     * Query refund status by out_refund_no.
274
     *
275
     * @param string $refundNo
276
     *
277
     * @return \EasyWeChat\Support\Collection
278
     */
279
    public function queryRefundByRefundNo($refundNo)
280
    {
281
        return $this->queryRefund($refundNo, self::OUT_REFUND_NO);
282
    }
283
284
    /**
285
     * Query refund status by transaction_id.
286
     *
287
     * @param string $transactionId
288
     *
289
     * @return \EasyWeChat\Support\Collection
290
     */
291
    public function queryRefundByTransactionId($transactionId)
292
    {
293
        return $this->queryRefund($transactionId, self::TRANSACTION_ID);
294
    }
295
296
    /**
297
     * Query refund status by refund_id.
298
     *
299
     * @param string $refundId
300
     *
301
     * @return \EasyWeChat\Support\Collection
302
     */
303
    public function queryRefundByRefundId($refundId)
304
    {
305
        return $this->queryRefund($refundId, self::REFUND_ID);
306
    }
307
308
    /**
309
     * Download bill history as a table file.
310
     *
311
     * @param string $date
312
     * @param string $type
313
     *
314
     * @return \Psr\Http\Message\ResponseInterface
315
     */
316 1
    public function downloadBill($date, $type = self::BILL_TYPE_ALL)
317
    {
318
        $params = [
319 1
            'bill_date' => $date,
320 1
            'bill_type' => $type,
321 1
        ];
322
323 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...
324
    }
325
326
    /**
327
     * Convert long url to short url.
328
     *
329
     * @param string $url
330
     *
331
     * @return \EasyWeChat\Support\Collection
332
     */
333 1
    public function urlShorten($url)
334
    {
335 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 335 which is incompatible with the return type documented by EasyWeChat\Payment\API::urlShorten of type EasyWeChat\Support\Collection.
Loading history...
336
    }
337
338
    /**
339
     * Report API status to WeChat.
340
     *
341
     * @param string $api
342
     * @param int    $timeConsuming
343
     * @param string $resultCode
344
     * @param string $returnCode
345
     * @param array  $other         ex: err_code,err_code_des,out_trade_no,user_ip...
346
     *
347
     * @return \EasyWeChat\Support\Collection
348
     */
349
    public function report($api, $timeConsuming, $resultCode, $returnCode, array $other = [])
350
    {
351
        $params = array_merge([
352
            'interface_url' => $api,
353
            'execute_time_' => $timeConsuming,
354
            'return_code' => $returnCode,
355
            'return_msg' => null,
356
            'result_code' => $resultCode,
357
            'user_ip' => get_client_ip(),
358
            'time' => time(),
359
        ], $other);
360
361
        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 361 which is incompatible with the return type documented by EasyWeChat\Payment\API::report of type EasyWeChat\Support\Collection.
Loading history...
362
    }
363
364
    /**
365
     * Get openid by auth code.
366
     *
367
     * @param string $authCode
368
     *
369
     * @return \EasyWeChat\Support\Collection
370
     */
371 1
    public function authCodeToOpenId($authCode)
372
    {
373 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 373 which is incompatible with the return type documented by EasyWeChat\Payment\API::authCodeToOpenId of type EasyWeChat\Support\Collection.
Loading history...
374
    }
375
376
    /**
377
     * Merchant setter.
378
     *
379
     * @param Merchant $merchant
380
     *
381
     * @return $this
382
     */
383 1
    public function setMerchant(Merchant $merchant)
384
    {
385 1
        $this->merchant = $merchant;
386 1
    }
387
388
    /**
389
     * Merchant getter.
390
     *
391
     * @return Merchant
392
     */
393 1
    public function getMerchant()
394
    {
395 1
        return $this->merchant;
396
    }
397
398
    /**
399
     * Set sandbox mode.
400
     *
401
     * @param bool $enabled
402
     *
403
     * @return $this
404
     */
405 10
    public function sandboxMode($enabled = false)
406
    {
407 10
        $this->sandboxEnabled = $enabled;
408
409 10
        return $this;
410
    }
411
412
    /**
413
     * Make a API request.
414
     *
415
     * @param string $api
416
     * @param array  $params
417
     * @param string $method
418
     * @param array  $options
419
     * @param bool   $returnResponse
420
     *
421
     * @return \EasyWeChat\Support\Collection|\Psr\Http\Message\ResponseInterface
422
     */
423 10
    protected function request($api, array $params, $method = 'post', array $options = [], $returnResponse = false)
424
    {
425 10
        $params = array_merge($params, $this->merchant->only(['sub_appid', 'sub_mch_id']));
426
427 10
        $params['appid'] = $this->merchant->app_id;
428 10
        $params['mch_id'] = $this->merchant->merchant_id;
429 10
        $params['device_info'] = $this->merchant->device_info;
430 10
        $params['nonce_str'] = uniqid();
431 10
        $params = array_filter($params);
432
433 10
        if ($this->sandboxEnabled && $this->sandboxSignKey) {
434
            $params['sign'] = generate_sign($params, $this->sandboxSignKey, 'md5');
435
        } else {
436 10
            $params['sign'] = generate_sign($params, $this->merchant->key, 'md5');
437
        }
438
439 10
        $options = array_merge([
440 10
            'body' => XML::build($params),
441 10
        ], $options);
442
443 10
        $response = $this->getHttp()->request($api, $method, $options);
444
445 10
        return $returnResponse ? $response : $this->parseResponse($response);
446
    }
447
448
    /**
449
     * Request with SSL.
450
     *
451
     * @param string $api
452
     * @param array  $params
453
     * @param string $method
454
     *
455
     * @return \EasyWeChat\Support\Collection
456
     */
457 2
    protected function safeRequest($api, array $params, $method = 'post')
458
    {
459
        $options = [
460 2
            'cert' => $this->merchant->get('cert_path'),
461 2
            'ssl_key' => $this->merchant->get('key_path'),
462 2
        ];
463
464 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 464 which is incompatible with the return type documented by EasyWeChat\Payment\API::safeRequest of type EasyWeChat\Support\Collection.
Loading history...
465
    }
466
467
    /**
468
     * Parse Response XML to array.
469
     *
470
     * @param ResponseInterface $response
471
     *
472
     * @return \EasyWeChat\Support\Collection
473
     */
474 9
    protected function parseResponse($response)
475
    {
476 9
        if ($response instanceof ResponseInterface) {
477
            $response = $response->getBody();
478
        }
479
480 9
        return new Collection((array) XML::parse($response));
481
    }
482
483
    /**
484
     * Wrap API.
485
     *
486
     * @param string $resource
487
     *
488
     * @return string
489
     */
490 8
    protected function wrapApi($resource)
491
    {
492 8
        return self::API_HOST.($this->sandboxEnabled ? '/sandboxnew' : '').$resource;
493
    }
494
495
    /**
496
     * Get sandbox sign key.
497
     */
498
    protected function getSandboxSignKey()
499
    {
500
        // Try to get sandbox_signkey from cache
501
        $cacheKey = 'sandbox_signkey'.$this->merchant->merchant_id.$this->merchant->sub_merchant_id;
502
        /** @var \Doctrine\Common\Cache\Cache $cache */
503
        $cache = new FilesystemCache(sys_get_temp_dir());
504
        $this->sandboxSignKey = $cache->fetch($cacheKey);
505
506
        if (!$this->sandboxSignKey) {
507
            // Try to acquire a new sandbox_signkey from WeChat
508
            try {
509
                $result = $this->request(self::API_SANDBOX_SIGN_KEY, []);
510
                if ($result->return_code === 'SUCCESS') {
511
                    $cache->save($cacheKey, $result->sandbox_signkey);
512
                    $this->sandboxSignKey = $result->sandbox_signkey;
513
                } else {
514
                    throw new Exception($result->return_msg);
515
                }
516
            } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
517
518
            }
519
        }
520
    }
521
}
522