Completed
Push — master ( 742527...9833f0 )
by Carlos
05:46 queued 02:57
created

API::parseResponse()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.2559

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 8
ccs 3
cts 5
cp 0.6
rs 9.4285
cc 2
eloc 4
nc 2
nop 1
crap 2.2559
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
 * @link      https://github.com/overtrue
19
 * @link      http://overtrue.me
20
 */
21
namespace EasyWeChat\Payment;
22
23
use EasyWeChat\Core\AbstractAPI;
24
use EasyWeChat\Support\Collection;
25
use EasyWeChat\Support\XML;
26
use Psr\Http\Message\ResponseInterface;
27
28
/**
29
 * Class API.
30
 */
31
class API extends AbstractAPI
32
{
33
    /**
34
     * Merchant instance.
35
     *
36
     * @var Merchant
37
     */
38
    protected $merchant;
39
40
    // api
41
    const API_PAY_ORDER = 'https://api.mch.weixin.qq.com/pay/micropay';
42
    const API_PREPARE_ORDER = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
43
    const API_QUERY = 'https://api.mch.weixin.qq.com/pay/orderquery';
44
    const API_CLOSE = 'https://api.mch.weixin.qq.com/pay/closeorder';
45
    const API_REVERSE = 'https://api.mch.weixin.qq.com/secapi/pay/reverse';
46
    const API_REFUND = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
47
    const API_QUERY_REFUND = 'https://api.mch.weixin.qq.com/pay/refundquery';
48
    const API_DOWNLOAD_BILL = 'https://api.mch.weixin.qq.com/pay/downloadbill';
49
    const API_REPORT = 'https://api.mch.weixin.qq.com/payitil/report';
50
    const API_URL_SHORTEN = 'https://api.mch.weixin.qq.com/tools/shorturl';
51
    const API_AUTH_CODE_TO_OPENID = 'https://api.mch.weixin.qq.com/tools/authcodetoopenid';
52
53
    // order id types.
54
    const TRANSACTION_ID = 'transaction_id';
55
    const OUT_TRADE_NO = 'out_trade_no';
56
    const OUT_REFUND_NO = 'out_refund_no';
57
    const REFUND_ID = 'refund_id';
58
59
    // bill types.
60
    const BILL_TYPE_ALL = 'ALL';
61
    const BILL_TYPE_SUCCESS = 'SUCCESS';
62
    const BILL_TYPE_REFUND = 'REFUND';
63
    const BILL_TYPE_REVOKED = 'REVOKED';
64
65
    /**
66
     * API constructor.
67
     *
68
     * @param \EasyWeChat\Payment\Merchant $merchant
69
     */
70 12
    public function __construct(Merchant $merchant)
71
    {
72 12
        $this->merchant = $merchant;
73 12
    }
74
75
    /**
76
     * Pay the order.
77
     *
78
     * @param Order $order
79
     *
80
     * @return \EasyWeChat\Support\Collection
81
     */
82 1
    public function pay(Order $order)
83
    {
84 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\Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write 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.");
        }
    }

}

Since the property has write access only, you can use the @property-write 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...
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...
85
86 1
        return $this->request(self::API_PAY_ORDER, $order->all());
87
    }
88
89
    /**
90
     * Prepare order to pay.
91
     *
92
     * @param Order $order
93
     *
94
     * @return \EasyWeChat\Support\Collection
95
     */
96 1
    public function prepare(Order $order)
97
    {
98 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\Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write 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.");
        }
    }

}

Since the property has write access only, you can use the @property-write 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...
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...
99
100 1
        return $this->request(self::API_PREPARE_ORDER, $order->all());
101
    }
102
103
    /**
104
     * Query order.
105
     *
106
     * @param string $orderNo
107
     * @param string $type
108
     *
109
     * @return \EasyWeChat\Support\Collection
110
     */
111 1
    public function query($orderNo, $type = self::OUT_TRADE_NO)
112
    {
113
        $params = [
114 1
            $type => $orderNo,
115 1
        ];
116
117 1
        return $this->request(self::API_QUERY, $params);
118
    }
119
120
    /**
121
     * Query order by transaction_id.
122
     *
123
     * @param string $transactionId
124
     *
125
     * @return \EasyWeChat\Support\Collection
126
     */
127 1
    public function queryByTransactionId($transactionId)
128
    {
129 1
        return $this->query($transactionId, self::TRANSACTION_ID);
130
    }
131
132
    /**
133
     * Close order by out_trade_no.
134
     *
135
     * @param $tradeNo
136
     *
137
     * @return \EasyWeChat\Support\Collection
138
     */
139 1
    public function close($tradeNo)
140
    {
141
        $params = [
142 1
            'out_trade_no' => $tradeNo,
143 1
        ];
144
145 1
        return $this->request(self::API_CLOSE, $params);
146
    }
147
148
    /**
149
     * Reverse order.
150
     *
151
     * @param string $orderNo
152
     * @param string $type
153
     *
154
     * @return \EasyWeChat\Support\Collection
155
     */
156 1
    public function reverse($orderNo, $type = self::OUT_TRADE_NO)
157
    {
158
        $params = [
159 1
            $type => $orderNo,
160 1
        ];
161
162 1
        return $this->safeRequest(self::API_REVERSE, $params);
163
    }
164
165
    /**
166
     * Reverse order by transaction_id.
167
     *
168
     * @param int $transactionId
169
     *
170
     * @return \EasyWeChat\Support\Collection
171
     */
172
    public function reverseByTransactionId($transactionId)
173
    {
174
        return $this->reverse($transactionId, self::TRANSACTION_ID);
175
    }
176
177
    /**
178
     * Make a refund request.
179
     *
180
     * @param string $orderNo
181
     * @param float  $totalFee
182
     * @param float  $refundFee
183
     * @param string $opUserId
184
     * @param string $type
185
     *
186
     * @return \EasyWeChat\Support\Collection
187
     */
188 1
    public function refund(
189
        $orderNo,
190
        $refundNo,
191
        $totalFee,
192
        $refundFee = null,
193
        $opUserId = null,
194
        $type = self::OUT_TRADE_NO
195
        ) {
196
        $params = [
197 1
            $type => $orderNo,
198 1
            'out_refund_no' => $refundNo,
199 1
            'total_fee' => $totalFee,
200 1
            'refund_fee' => $refundFee ?: $totalFee,
201 1
            'refund_fee_type' => $this->merchant->fee_type,
202 1
            'op_user_id' => $opUserId ?: $this->merchant->merchant_id,
203 1
        ];
204
205 1
        return $this->safeRequest(self::API_REFUND, $params);
206
    }
207
208
    /**
209
     * Refund by transaction id.
210
     *
211
     * @param string $orderNo
212
     * @param float  $totalFee
213
     * @param float  $refundFee
214
     * @param string $opUserId
215
     *
216
     * @return \EasyWeChat\Support\Collection
217
     */
218
    public function refundByTransactionId(
219
        $orderNo,
220
        $refundNo,
221
        $totalFee,
222
        $refundFee = null,
223
        $opUserId = null
224
        ) {
225
        return $this->refund($orderNo, $refundNo, $totalFee, $refundFee, $opUserId, self::TRANSCATION_ID);
226
    }
227
228
    /**
229
     * Query refund status.
230
     *
231
     * @param string $orderNo
232
     * @param string $type
233
     *
234
     * @return \EasyWeChat\Support\Collection
235
     */
236 1
    public function queryRefund($orderNo, $type = self::OUT_TRADE_NO)
237
    {
238
        $params = [
239 1
            $type => $orderNo,
240 1
        ];
241
242 1
        return $this->request(self::API_QUERY_REFUND, $params);
243
    }
244
245
    /**
246
     * Query refund status by out_refund_no.
247
     *
248
     * @param string $refundNo
249
     *
250
     * @return \EasyWeChat\Support\Collection
251
     */
252
    public function queryRefundByRefundNo($refundNo)
253
    {
254
        return $this->queryRefund($refundNo, self::OUT_REFUND_NO);
255
    }
256
257
    /**
258
     * Query refund status by transaction_id.
259
     *
260
     * @param string $transactionId
261
     *
262
     * @return \EasyWeChat\Support\Collection
263
     */
264
    public function queryRefundByTransactionId($transactionId)
265
    {
266
        return $this->queryRefund($transactionId, self::TRANSACTION_ID);
267
    }
268
269
    /**
270
     * Query refund status by refund_id.
271
     *
272
     * @param string $refundId
273
     *
274
     * @return \EasyWeChat\Support\Collection
275
     */
276
    public function queryRefundByRefundId($refundId)
277
    {
278
        return $this->queryRefund($refundId, self::REFUND_ID);
279
    }
280
281
    /**
282
     * Download bill history as a table file.
283
     *
284
     * @param string $date
285
     * @param string $type
286
     *
287
     * @return \EasyWeChat\Support\Collection
288
     */
289 1
    public function downloadBill($date, $type = self::BILL_TYPE_ALL)
290
    {
291
        $params = [
292 1
            'bill_date' => $date,
293 1
            'bill_type' => $type,
294 1
        ];
295
296 1
        return $this->request(self::API_DOWNLOAD_BILL, $params);
297
    }
298
299
    /**
300
     * Convert long url to short url.
301
     *
302
     * @param string $url
303
     *
304
     * @return \EasyWeChat\Support\Collection
305
     */
306 1
    public function urlShorten($url)
307
    {
308 1
        return $this->request(self::API_URL_SHORTEN, ['long_url' => $url]);
309
    }
310
311
    /**
312
     * Report API status to WeChat.
313
     *
314
     * @param string $api
315
     * @param int    $timeConsuming
316
     * @param string $resultCode
317
     * @param string $returnCode
318
     * @param array  $other         ex: err_code,err_code_des,out_trade_no,user_ip...
319
     *
320
     * @return \EasyWeChat\Support\Collection
321
     */
322
    public function report($api, $timeConsuming, $resultCode, $returnCode, array $other = [])
0 ignored issues
show
Coding Style introduced by
report uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
323
    {
324
        $params = array_merge([
325
            'interface_url' => $api,
326
            'execute_time_' => $timeConsuming,
327
            'return_code' => $returnCode,
328
            'return_msg' => null,
329
            'result_code' => $resultCode,
330
            'user_ip' => $_SERVER['SERVER_ADDR'],
331
            'time' => time(),
332
        ], $other);
333
334
        return $this->request(self::API_REPORT, $params);
335
    }
336
337
    /**
338
     * Get openid by auth code.
339
     *
340
     * @param string $authCode
341
     *
342
     * @return \EasyWeChat\Support\Collection
343
     */
344 1
    public function authCodeToOpenId($authCode)
345
    {
346 1
        return $this->request(self::API_AUTH_CODE_TO_OPENID, ['auth_code' => $authCode]);
347
    }
348
349
    /**
350
     * Merchant setter.
351
     *
352
     * @param Merchant $merchant
353
     *
354
     * @return $this
355
     */
356 1
    public function setMerchant(Merchant $merchant)
357
    {
358 1
        $this->merchant = $merchant;
359 1
    }
360
361
    /**
362
     * Merchant getter.
363
     *
364
     * @return Merchant
365
     */
366 1
    public function getMerchant()
367
    {
368 1
        return $this->merchant;
369
    }
370
371
    /**
372
     * Make a API request.
373
     *
374
     * @param string $api
375
     * @param array  $params
376
     * @param string $method
377
     * @param array  $options
378
     *
379
     * @return \EasyWeChat\Support\Collection
380
     */
381 10
    protected function request($api, array $params, $method = 'post', array $options = [])
382
    {
383 10
        $params['appid'] = $this->merchant->app_id;
384 10
        $params['mch_id'] = $this->merchant->merchant_id;
385 10
        $params['device_info'] = $this->merchant->device_info;
386 10
        $params['nonce_str'] = uniqid();
387 10
        $params['sign'] = generate_sign($params, $this->merchant->key, 'md5');
388
389 10
        $options = array_merge([
390 10
            'body' => XML::build($params),
391 10
        ], $options);
392
393 10
        return $this->parseResponse($this->getHttp()->request($api, $method, $options));
0 ignored issues
show
Documentation introduced by
$this->getHttp()->reques...api, $method, $options) is of type object<Psr\Http\Message\ResponseInterface>, 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...
394
    }
395
396
    /**
397
     * Request with SSL.
398
     *
399
     * @param string $api
400
     * @param array  $params
401
     * @param string $method
402
     *
403
     * @return \EasyWeChat\Support\Collection
404
     */
405 2
    protected function safeRequest($api, array $params, $method = 'post')
406
    {
407
        $options = [
408 2
            'cert' => $this->merchant->get('cert_path'),
409 2
            'ssl_key' => $this->merchant->get('key_path'),
410 2
        ];
411
412 2
        return $this->request($api, $params, $method, $options);
413
    }
414
415
    /**
416
     * Parse Response XML to array.
417
     *
418
     * @param string $response
419
     *
420
     * @return \EasyWeChat\Support\Collection
421
     */
422 10
    protected function parseResponse($response)
423
    {
424 10
        if ($response instanceof ResponseInterface) {
425
            $response = $response->getBody();
426
        }
427
428 10
        return new Collection((array) XML::parse($response));
429
    }
430
}
431