Alipay   A
last analyzed

Complexity

Total Complexity 37

Size/Duplication

Total Lines 415
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 17

Importance

Changes 0
Metric Value
wmc 37
lcom 1
cbo 17
dl 0
loc 415
rs 9.44
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 1
A __call() 0 8 2
A pay() 0 19 2
A verify() 0 22 5
A find() 0 18 3
A refund() 0 10 1
A cancel() 0 10 2
A close() 0 10 2
A download() 0 12 3
A success() 0 6 1
B extend() 0 25 6
A makePay() 0 12 3
B makeExtend() 0 28 6
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\InvalidArgumentException;
12
use Yansongda\Pay\Exceptions\InvalidConfigException;
13
use Yansongda\Pay\Exceptions\InvalidGatewayException;
14
use Yansongda\Pay\Exceptions\InvalidSignException;
15
use Yansongda\Pay\Gateways\Alipay\Support;
16
use Yansongda\Supports\Collection;
17
use Yansongda\Supports\Config;
18
use Yansongda\Supports\Str;
19
20
/**
21
 * @method Response app(array $config) APP 支付
22
 * @method Collection pos(array $config) 刷卡支付
23
 * @method Collection scan(array $config) 扫码支付
24
 * @method Collection transfer(array $config) 帐户转账
25
 * @method Response wap(array $config) 手机网站支付
26
 * @method Response web(array $config) 电脑支付
27
 * @method Collection mini(array $config) 小程序支付
28
 */
29
class Alipay implements GatewayApplicationInterface
30
{
31
    /**
32
     * Const mode_normal.
33
     */
34
    const MODE_NORMAL = 'normal';
35
36
    /**
37
     * Const mode_dev.
38
     */
39
    const MODE_DEV = 'dev';
40
41
    /**
42
     * Const url.
43
     */
44
    const URL = [
45
        self::MODE_NORMAL => 'https://openapi.alipay.com/gateway.do?charset=utf-8',
46
        self::MODE_DEV    => 'https://openapi.alipaydev.com/gateway.do?charset=utf-8',
47
    ];
48
49
    /**
50
     * Alipay payload.
51
     *
52
     * @var array
53
     */
54
    protected $payload;
55
56
    /**
57
     * Alipay gateway.
58
     *
59
     * @var string
60
     */
61
    protected $gateway;
62
63
    /**
64
     * extends.
65
     *
66
     * @var array
67
     */
68
    protected $extends;
69
70
    /**
71
     * Bootstrap.
72
     *
73
     * @author yansongda <[email protected]>
74
     *
75
     * @param Config $config
76
     */
77
    public function __construct(Config $config)
78
    {
79
        $this->gateway = Support::create($config)->getBaseUri();
80
        $this->payload = [
81
            'app_id'         => $config->get('app_id'),
82
            'method'         => '',
83
            'format'         => 'JSON',
84
            'charset'        => 'utf-8',
85
            'sign_type'      => 'RSA2',
86
            'version'        => '1.0',
87
            'return_url'     => $config->get('return_url'),
88
            'notify_url'     => $config->get('notify_url'),
89
            'timestamp'      => date('Y-m-d H:i:s'),
90
            'sign'           => '',
91
            'biz_content'    => '',
92
            'app_auth_token' => $config->get('app_auth_token'),
93
        ];
94
    }
95
96
    /**
97
     * Magic pay.
98
     *
99
     * @author yansongda <[email protected]>
100
     *
101
     * @param string $method
102
     * @param array  $params
103
     *
104
     * @throws GatewayException
105
     * @throws InvalidArgumentException
106
     * @throws InvalidConfigException
107
     * @throws InvalidGatewayException
108
     * @throws InvalidSignException
109
     *
110
     * @return Response|Collection
111
     */
112
    public function __call($method, $params)
113
    {
114
        if (isset($this->extends[$method])) {
115
            return $this->makeExtend($method, ...$params);
116
        }
117
118
        return $this->pay($method, ...$params);
119
    }
120
121
    /**
122
     * Pay an order.
123
     *
124
     * @author yansongda <[email protected]>
125
     *
126
     * @param string $gateway
127
     * @param array  $params
128
     *
129
     * @throws InvalidGatewayException
130
     *
131
     * @return Response|Collection
132
     */
133
    public function pay($gateway, $params = [])
134
    {
135
        Events::dispatch(new Events\PayStarting('Alipay', $gateway, $params));
136
137
        $this->payload['return_url'] = $params['return_url'] ?? $this->payload['return_url'];
138
        $this->payload['notify_url'] = $params['notify_url'] ?? $this->payload['notify_url'];
139
140
        unset($params['return_url'], $params['notify_url']);
141
142
        $this->payload['biz_content'] = json_encode($params);
143
144
        $gateway = get_class($this).'\\'.Str::studly($gateway).'Gateway';
145
146
        if (class_exists($gateway)) {
147
            return $this->makePay($gateway);
148
        }
149
150
        throw new InvalidGatewayException("Pay Gateway [{$gateway}] not exists");
151
    }
152
153
    /**
154
     * Verify sign.
155
     *
156
     * @author yansongda <[email protected]>
157
     *
158
     * @param null|array $data
159
     * @param bool       $refund
160
     *
161
     * @throws InvalidSignException
162
     * @throws InvalidConfigException
163
     *
164
     * @return Collection
165
     */
166
    public function verify($data = null, bool $refund = false): Collection
167
    {
168
        if (is_null($data)) {
169
            $request = Request::createFromGlobals();
170
171
            $data = $request->request->count() > 0 ? $request->request->all() : $request->query->all();
172
        }
173
174
        if (isset($data['fund_bill_list'])) {
175
            $data['fund_bill_list'] = htmlspecialchars_decode($data['fund_bill_list']);
176
        }
177
178
        Events::dispatch(new Events\RequestReceived('Alipay', '', $data));
179
180
        if (Support::verifySign($data)) {
181
            return new Collection($data);
182
        }
183
184
        Events::dispatch(new Events\SignFailed('Alipay', '', $data));
185
186
        throw new InvalidSignException('Alipay Sign Verify FAILED', $data);
187
    }
188
189
    /**
190
     * Query an order.
191
     *
192
     * @author yansongda <[email protected]>
193
     *
194
     * @param string|array $order
195
     * @param string       $type
196
     *
197
     * @throws GatewayException
198
     * @throws InvalidConfigException
199
     * @throws InvalidSignException
200
     *
201
     * @return Collection
202
     */
203
    public function find($order, string $type = 'wap'): Collection
204
    {
205
        $gateway = get_class($this).'\\'.Str::studly($type).'Gateway';
206
207
        if (!class_exists($gateway) || !is_callable([new $gateway(), 'find'])) {
208
            throw new GatewayException("{$gateway} Done Not Exist Or Done Not Has FIND Method");
209
        }
210
211
        $config = call_user_func([new $gateway(), 'find'], $order);
212
213
        $this->payload['method'] = $config['method'];
214
        $this->payload['biz_content'] = $config['biz_content'];
215
        $this->payload['sign'] = Support::generateSign($this->payload);
216
217
        Events::dispatch(new Events\MethodCalled('Alipay', 'Find', $this->gateway, $this->payload));
218
219
        return Support::requestApi($this->payload);
220
    }
221
222
    /**
223
     * Refund an order.
224
     *
225
     * @author yansongda <[email protected]>
226
     *
227
     * @param array $order
228
     *
229
     * @throws GatewayException
230
     * @throws InvalidConfigException
231
     * @throws InvalidSignException
232
     *
233
     * @return Collection
234
     */
235
    public function refund(array $order): Collection
236
    {
237
        $this->payload['method'] = 'alipay.trade.refund';
238
        $this->payload['biz_content'] = json_encode($order);
239
        $this->payload['sign'] = Support::generateSign($this->payload);
240
241
        Events::dispatch(new Events\MethodCalled('Alipay', 'Refund', $this->gateway, $this->payload));
242
243
        return Support::requestApi($this->payload);
244
    }
245
246
    /**
247
     * Cancel an order.
248
     *
249
     * @author yansongda <[email protected]>
250
     *
251
     * @param array|string $order
252
     *
253
     * @throws GatewayException
254
     * @throws InvalidConfigException
255
     * @throws InvalidSignException
256
     *
257
     * @return Collection
258
     */
259
    public function cancel($order): Collection
260
    {
261
        $this->payload['method'] = 'alipay.trade.cancel';
262
        $this->payload['biz_content'] = json_encode(is_array($order) ? $order : ['out_trade_no' => $order]);
263
        $this->payload['sign'] = Support::generateSign($this->payload);
264
265
        Events::dispatch(new Events\MethodCalled('Alipay', 'Cancel', $this->gateway, $this->payload));
266
267
        return Support::requestApi($this->payload);
268
    }
269
270
    /**
271
     * Close an order.
272
     *
273
     * @param string|array $order
274
     *
275
     * @author yansongda <[email protected]>
276
     *
277
     * @throws GatewayException
278
     * @throws InvalidConfigException
279
     * @throws InvalidSignException
280
     *
281
     * @return Collection
282
     */
283
    public function close($order): Collection
284
    {
285
        $this->payload['method'] = 'alipay.trade.close';
286
        $this->payload['biz_content'] = json_encode(is_array($order) ? $order : ['out_trade_no' => $order]);
287
        $this->payload['sign'] = Support::generateSign($this->payload);
288
289
        Events::dispatch(new Events\MethodCalled('Alipay', 'Close', $this->gateway, $this->payload));
290
291
        return Support::requestApi($this->payload);
292
    }
293
294
    /**
295
     * Download bill.
296
     *
297
     * @author yansongda <[email protected]>
298
     *
299
     * @param string|array $bill
300
     *
301
     * @throws GatewayException
302
     * @throws InvalidConfigException
303
     * @throws InvalidSignException
304
     *
305
     * @return string
306
     */
307
    public function download($bill): string
308
    {
309
        $this->payload['method'] = 'alipay.data.dataservice.bill.downloadurl.query';
310
        $this->payload['biz_content'] = json_encode(is_array($bill) ? $bill : ['bill_type' => 'trade', 'bill_date' => $bill]);
311
        $this->payload['sign'] = Support::generateSign($this->payload);
312
313
        Events::dispatch(new Events\MethodCalled('Alipay', 'Download', $this->gateway, $this->payload));
314
315
        $result = Support::requestApi($this->payload);
316
317
        return ($result instanceof Collection) ? $result->get('bill_download_url') : '';
318
    }
319
320
    /**
321
     * Reply success to alipay.
322
     *
323
     * @author yansongda <[email protected]>
324
     *
325
     * @return Response
326
     */
327
    public function success(): Response
328
    {
329
        Events::dispatch(new Events\MethodCalled('Alipay', 'Success', $this->gateway));
330
331
        return Response::create('success');
332
    }
333
334
    /**
335
     * extend.
336
     *
337
     * @author yansongda <[email protected]>
338
     *
339
     * @param string   $method
340
     * @param callable $function
341
     * @param bool     $now
342
     *
343
     * @throws GatewayException
344
     * @throws InvalidConfigException
345
     * @throws InvalidSignException
346
     * @throws InvalidArgumentException
347
     *
348
     * @return Collection|null
349
     */
350
    public function extend(string $method, callable $function, bool $now = true): ?Collection
351
    {
352
        if (!$now && !method_exists($this, $method)) {
353
            $this->extends[$method] = $function;
354
355
            return null;
356
        }
357
358
        $customize = $function($this->payload);
359
360
        if (!is_array($customize) && !($customize instanceof Collection)) {
361
            throw new InvalidArgumentException('Return Type Must Be Array Or Collection');
362
        }
363
364
        Events::dispatch(new Events\MethodCalled('Alipay', 'extend', $this->gateway, $customize));
365
366
        if (is_array($customize)) {
367
            $this->payload = $customize;
368
            $this->payload['sign'] = Support::generateSign($this->payload);
369
370
            return Support::requestApi($this->payload);
371
        }
372
373
        return $customize;
374
    }
375
376
    /**
377
     * Make pay gateway.
378
     *
379
     * @author yansongda <[email protected]>
380
     *
381
     * @param string $gateway
382
     *
383
     * @throws InvalidGatewayException
384
     *
385
     * @return Response|Collection
386
     */
387
    protected function makePay(string $gateway)
388
    {
389
        $app = new $gateway();
390
391
        if ($app instanceof GatewayInterface) {
392
            return $app->pay($this->gateway, array_filter($this->payload, function ($value) {
393
                return $value !== '' && !is_null($value);
394
            }));
395
        }
396
397
        throw new InvalidGatewayException("Pay Gateway [{$gateway}] Must Be An Instance Of GatewayInterface");
398
    }
399
400
    /**
401
     * makeExtend.
402
     *
403
     * @author yansongda <[email protected]>
404
     *
405
     * @param string $method
406
     * @param array  $params
407
     *
408
     * @throws GatewayException
409
     * @throws InvalidArgumentException
410
     * @throws InvalidConfigException
411
     * @throws InvalidSignException
412
     *
413
     * @return Collection
414
     */
415
    protected function makeExtend(string $method, array ...$params): Collection
416
    {
417
        $params = count($params) >= 1 ? $params[0] : $params;
418
419
        $function = $this->extends[$method];
420
421
        $customize = $function($this->payload, $params);
422
423
        if (!is_array($customize) && !($customize instanceof Collection)) {
424
            throw new InvalidArgumentException('Return Type Must Be Array Or Collection');
425
        }
426
427
        Events::dispatch(new Events\MethodCalled(
428
            'Alipay',
429
            'extend - '.$method,
430
            $this->gateway,
431
            is_array($customize) ? $customize : $customize->toArray()
432
        ));
433
434
        if (is_array($customize)) {
435
            $this->payload = $customize;
436
            $this->payload['sign'] = Support::generateSign($this->payload);
437
438
            return Support::requestApi($this->payload);
439
        }
440
441
        return $customize;
442
    }
443
}
444