Completed
Push — master ( 2fad31...6b685a )
by Songda
01:59
created

Alipay::extend()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
namespace Yansongda\Pay\Gateways;
4
5
use Symfony\Component\HttpFoundation\Request;
6
use Symfony\Component\HttpFoundation\Response;
7
use Yansongda\Pay\Contracts\GatewayApplicationInterface;
8
use Yansongda\Pay\Contracts\GatewayInterface;
9
use Yansongda\Pay\Events;
10
use Yansongda\Pay\Exceptions\GatewayException;
11
use Yansongda\Pay\Exceptions\InvalidGatewayException;
12
use Yansongda\Pay\Exceptions\InvalidSignException;
13
use Yansongda\Pay\Gateways\Alipay\Support;
14
use Yansongda\Pay\Log;
15
use Yansongda\Supports\Collection;
16
use Yansongda\Supports\Config;
17
use Yansongda\Supports\Str;
18
19
/**
20
 * @method Response app(array $config) APP 支付
21
 * @method Collection pos(array $config) 刷卡支付
22
 * @method Collection scan(array $config) 扫码支付
23
 * @method Collection transfer(array $config) 帐户转账
24
 * @method Response wap(array $config) 手机网站支付
25
 * @method Response web(array $config) 电脑支付
26
 * @method Collection mini(array $config) 小程序支付
27
 */
28
class Alipay implements GatewayApplicationInterface
29
{
30
    /**
31
     * Const mode_normal.
32
     */
33
    const MODE_NORMAL = 'normal';
34
35
    /**
36
     * Const mode_dev.
37
     */
38
    const MODE_DEV = 'dev';
39
40
    /**
41
     * Const url.
42
     */
43
    const URL = [
44
        self::MODE_NORMAL => 'https://openapi.alipay.com/gateway.do',
45
        self::MODE_DEV    => 'https://openapi.alipaydev.com/gateway.do',
46
    ];
47
48
    /**
49
     * Alipay payload.
50
     *
51
     * @var array
52
     */
53
    protected $payload;
54
55
    /**
56
     * Alipay gateway.
57
     *
58
     * @var string
59
     */
60
    protected $gateway;
61
62
    /**
63
     * Bootstrap.
64
     *
65
     * @author yansongda <[email protected]>
66
     *
67
     * @param Config $config
68
     */
69
    public function __construct(Config $config)
70
    {
71
        $this->gateway = Support::create($config)->getBaseUri();
72
        $this->payload = [
73
            'app_id'         => $config->get('app_id'),
74
            'method'         => '',
75
            'format'         => 'JSON',
76
            'charset'        => 'utf-8',
77
            'sign_type'      => 'RSA2',
78
            'version'        => '1.0',
79
            'return_url'     => $config->get('return_url'),
80
            'notify_url'     => $config->get('notify_url'),
81
            'timestamp'      => date('Y-m-d H:i:s'),
82
            'sign'           => '',
83
            'biz_content'    => '',
84
            'app_auth_token' => $config->get('app_auth_token'),
85
        ];
86
    }
87
88
    /**
89
     * Magic pay.
90
     *
91
     * @author yansongda <[email protected]>
92
     *
93
     * @param string $method
94
     * @param array  $params
95
     *
96
     * @throws InvalidGatewayException
97
     *
98
     * @return Response|Collection
99
     */
100
    public function __call($method, $params)
101
    {
102
        return $this->pay($method, ...$params);
103
    }
104
105
    /**
106
     * Pay an order.
107
     *
108
     * @author yansongda <[email protected]>
109
     *
110
     * @param string $gateway
111
     * @param array  $params
112
     *
113
     * @throws InvalidGatewayException
114
     *
115
     * @return Response|Collection
116
     */
117
    public function pay($gateway, $params = [])
118
    {
119
        Events::dispatch(Events::PAY_STARTING, new Events\PayStarting('Alipay', $gateway, $params));
120
121
        $this->payload['return_url'] = $params['return_url'] ?? $this->payload['return_url'];
122
        $this->payload['notify_url'] = $params['notify_url'] ?? $this->payload['notify_url'];
123
124
        unset($params['return_url'], $params['notify_url']);
125
126
        $this->payload['biz_content'] = json_encode($params);
127
128
        $gateway = get_class($this).'\\'.Str::studly($gateway).'Gateway';
129
130
        if (class_exists($gateway)) {
131
            return $this->makePay($gateway);
132
        }
133
134
        throw new InvalidGatewayException("Pay Gateway [{$gateway}] not exists");
135
    }
136
137
    /**
138
     * Verify sign.
139
     *
140
     * @author yansongda <[email protected]>
141
     *
142
     * @param null|array $data
143
     * @param bool       $refund
144
     *
145
     * @throws InvalidSignException
146
     * @throws \Yansongda\Pay\Exceptions\InvalidConfigException
147
     *
148
     * @return Collection
149
     */
150
    public function verify($data = null, $refund = false): Collection
151
    {
152
        if (is_null($data)) {
153
            $request = Request::createFromGlobals();
154
155
            $data = $request->request->count() > 0 ? $request->request->all() : $request->query->all();
156
            $data = Support::encoding($data, 'utf-8', $data['charset'] ?? 'gb2312');
157
        }
158
159
        if (isset($data['fund_bill_list'])) {
160
            $data['fund_bill_list'] = htmlspecialchars_decode($data['fund_bill_list']);
161
        }
162
163
        Events::dispatch(Events::REQUEST_RECEIVED, new Events\RequestReceived('Alipay', '', $data));
164
165
        if (Support::verifySign($data)) {
166
            return new Collection($data);
167
        }
168
169
        Events::dispatch(Events::SIGN_FAILED, new Events\SignFailed('Alipay', '', $data));
170
171
        throw new InvalidSignException('Alipay Sign Verify FAILED', $data);
172
    }
173
174
    /**
175
     * Query an order.
176
     *
177
     * @author yansongda <[email protected]>
178
     *
179
     * @param string|array $order
180
     * @param string       $type
181
     * @param bool         $transfer @deprecated since v2.7.3
182
     *
183
     * @throws InvalidSignException
184
     * @throws \Yansongda\Pay\Exceptions\GatewayException
185
     * @throws \Yansongda\Pay\Exceptions\InvalidConfigException
186
     *
187
     * @return Collection
188
     */
189
    public function find($order, $type = 'wap', $transfer = false): Collection
190
    {
191
        if ($type === true || $transfer) {
192
            Log::warning('DEPRECATED: In Alipay->find(), the REFUND/TRANSFER param is deprecated since v2.7.3, use TYPE param instead!');
193
            @trigger_error('In yansongda/pay Alipay->find(), the REFUND/TRANSFER param is deprecated since v2.7.3, use TYPE param instead!', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by yansongda
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
194
195
            $type = $type === true ? 'refund' : 'transfer';
196
        }
197
198
        $gateway = get_class($this).'\\'.Str::studly($type).'Gateway';
199
200
        if (!class_exists($gateway) || !is_callable([$gateway, 'find'])) {
201
            throw new GatewayException("{$gateway} Done Not Exist Or Done Not Has FIND Method");
202
        }
203
204
        $config = call_user_func([$gateway, 'find'], $order);
205
206
        $this->payload['method'] = $config['method'];
207
        $this->payload['biz_content'] = $config['biz_content'];
208
        $this->payload['sign'] = Support::generateSign($this->payload);
209
210
        Events::dispatch(Events::METHOD_CALLED, new Events\MethodCalled('Alipay', 'Find', $this->gateway, $this->payload));
211
212
        return Support::requestApi($this->payload);
213
    }
214
215
    /**
216
     * Refund an order.
217
     *
218
     * @author yansongda <[email protected]>
219
     *
220
     * @param array $order
221
     *
222
     * @throws InvalidSignException
223
     * @throws \Yansongda\Pay\Exceptions\GatewayException
224
     * @throws \Yansongda\Pay\Exceptions\InvalidConfigException
225
     *
226
     * @return Collection
227
     */
228
    public function refund($order): Collection
229
    {
230
        $this->payload['method'] = 'alipay.trade.refund';
231
        $this->payload['biz_content'] = json_encode($order);
232
        $this->payload['sign'] = Support::generateSign($this->payload);
233
234
        Events::dispatch(Events::METHOD_CALLED, new Events\MethodCalled('Alipay', 'Refund', $this->gateway, $this->payload));
235
236
        return Support::requestApi($this->payload);
237
    }
238
239
    /**
240
     * Cancel an order.
241
     *
242
     * @author yansongda <[email protected]>
243
     *
244
     * @param string|array $order
245
     *
246
     * @throws InvalidSignException
247
     * @throws \Yansongda\Pay\Exceptions\GatewayException
248
     * @throws \Yansongda\Pay\Exceptions\InvalidConfigException
249
     *
250
     * @return Collection
251
     */
252 View Code Duplication
    public function cancel($order): Collection
0 ignored issues
show
Duplication introduced by yansongda
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...
253
    {
254
        $this->payload['method'] = 'alipay.trade.cancel';
255
        $this->payload['biz_content'] = json_encode(is_array($order) ? $order : ['out_trade_no' => $order]);
256
        $this->payload['sign'] = Support::generateSign($this->payload);
257
258
        Events::dispatch(Events::METHOD_CALLED, new Events\MethodCalled('Alipay', 'Cancel', $this->gateway, $this->payload));
259
260
        return Support::requestApi($this->payload);
261
    }
262
263
    /**
264
     * Close an order.
265
     *
266
     * @author yansongda <[email protected]songda.cn>
267
     *
268
     * @param string|array $order
269
     *
270
     * @throws InvalidSignException
271
     * @throws \Yansongda\Pay\Exceptions\GatewayException
272
     * @throws \Yansongda\Pay\Exceptions\InvalidConfigException
273
     *
274
     * @return Collection
275
     */
276 View Code Duplication
    public function close($order): Collection
0 ignored issues
show
Duplication introduced by yansongda
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...
277
    {
278
        $this->payload['method'] = 'alipay.trade.close';
279
        $this->payload['biz_content'] = json_encode(is_array($order) ? $order : ['out_trade_no' => $order]);
280
        $this->payload['sign'] = Support::generateSign($this->payload);
281
282
        Events::dispatch(Events::METHOD_CALLED, new Events\MethodCalled('Alipay', 'Close', $this->gateway, $this->payload));
283
284
        return Support::requestApi($this->payload);
285
    }
286
287
    /**
288
     * Download bill.
289
     *
290
     * @author yansongda <[email protected]>
291
     *
292
     * @param string|array $bill
293
     *
294
     * @throws InvalidSignException
295
     * @throws \Yansongda\Pay\Exceptions\GatewayException
296
     * @throws \Yansongda\Pay\Exceptions\InvalidConfigException
297
     *
298
     * @return string
299
     */
300
    public function download($bill): string
301
    {
302
        $this->payload['method'] = 'alipay.data.dataservice.bill.downloadurl.query';
303
        $this->payload['biz_content'] = json_encode(is_array($bill) ? $bill : ['bill_type' => 'trade', 'bill_date' => $bill]);
304
        $this->payload['sign'] = Support::generateSign($this->payload);
305
306
        Events::dispatch(Events::METHOD_CALLED, new Events\MethodCalled('Alipay', 'Download', $this->gateway, $this->payload));
307
308
        $result = Support::requestApi($this->payload);
309
310
        return ($result instanceof Collection) ? $result->get('bill_download_url') : '';
311
    }
312
313
    /**
314
     * Reply success to alipay.
315
     *
316
     * @author yansongda <[email protected]>
317
     *
318
     * @return Response
319
     */
320
    public function success(): Response
321
    {
322
        Events::dispatch(Events::METHOD_CALLED, new Events\MethodCalled('Alipay', 'Success', $this->gateway));
323
324
        return Response::create('success');
325
    }
326
327
    /**
328
     * extend.
329
     *
330
     * @author yansongda <[email protected]>
331
     *
332
     * @param string   $method
333
     * @param callable $function
334
     *
335
     * @return void
336
     */
337
    public function extend(string $method, callable $function)
0 ignored issues
show
Unused Code introduced by yansongda
The parameter $method is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by yansongda
The parameter $function is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
338
    {
339
    }
340
341
    /**
342
     * Make pay gateway.
343
     *
344
     * @author yansongda <[email protected]>
345
     *
346
     * @param string $gateway
347
     *
348
     * @throws InvalidGatewayException
349
     *
350
     * @return Response|Collection
351
     */
352 View Code Duplication
    protected function makePay($gateway)
0 ignored issues
show
Duplication introduced by yansongda
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...
353
    {
354
        $app = new $gateway();
355
356
        if ($app instanceof GatewayInterface) {
357
            return $app->pay($this->gateway, array_filter($this->payload, function ($value) {
358
                return $value !== '' && !is_null($value);
359
            }));
360
        }
361
362
        throw new InvalidGatewayException("Pay Gateway [{$gateway}] Must Be An Instance Of GatewayInterface");
363
    }
364
}
365