Completed
Push — master ( 026505...b42306 )
by Songda
02:02
created

src/Gateways/Alipay.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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\InvalidConfigException;
12
use Yansongda\Pay\Exceptions\InvalidGatewayException;
13
use Yansongda\Pay\Exceptions\InvalidSignException;
14
use Yansongda\Pay\Gateways\Alipay\Support;
15
use Yansongda\Pay\Log;
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',
46
        self::MODE_DEV    => 'https://openapi.alipaydev.com/gateway.do',
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
     * Bootstrap.
65
     *
66
     * @author yansongda <[email protected]>
67
     *
68
     * @param Config $config
69
     */
70
    public function __construct(Config $config)
71
    {
72
        $this->gateway = Support::create($config)->getBaseUri();
73
        $this->payload = [
74
            'app_id'         => $config->get('app_id'),
75
            'method'         => '',
76
            'format'         => 'JSON',
77
            'charset'        => 'utf-8',
78
            'sign_type'      => 'RSA2',
79
            'version'        => '1.0',
80
            'return_url'     => $config->get('return_url'),
81
            'notify_url'     => $config->get('notify_url'),
82
            'timestamp'      => date('Y-m-d H:i:s'),
83
            'sign'           => '',
84
            'biz_content'    => '',
85
            'app_auth_token' => $config->get('app_auth_token'),
86
        ];
87
    }
88
89
    /**
90
     * Magic pay.
91
     *
92
     * @author yansongda <[email protected]>
93
     *
94
     * @param string $method
95
     * @param array  $params
96
     *
97
     * @throws InvalidGatewayException
98
     *
99
     * @return Response|Collection
100
     */
101
    public function __call($method, $params)
102
    {
103
        return $this->pay($method, ...$params);
104
    }
105
106
    /**
107
     * Pay an order.
108
     *
109
     * @author yansongda <[email protected]>
110
     *
111
     * @param string $gateway
112
     * @param array  $params
113
     *
114
     * @throws InvalidGatewayException
115
     *
116
     * @return Response|Collection
117
     */
118
    public function pay($gateway, $params = [])
119
    {
120
        Events::dispatch(Events::PAY_STARTING, new Events\PayStarting('Alipay', $gateway, $params));
121
122
        $this->payload['return_url'] = $params['return_url'] ?? $this->payload['return_url'];
123
        $this->payload['notify_url'] = $params['notify_url'] ?? $this->payload['notify_url'];
124
125
        unset($params['return_url'], $params['notify_url']);
126
127
        $this->payload['biz_content'] = json_encode($params);
128
129
        $gateway = get_class($this).'\\'.Str::studly($gateway).'Gateway';
130
131
        if (class_exists($gateway)) {
132
            return $this->makePay($gateway);
133
        }
134
135
        throw new InvalidGatewayException("Pay Gateway [{$gateway}] not exists");
136
    }
137
138
    /**
139
     * Verify sign.
140
     *
141
     * @author yansongda <[email protected]>
142
     *
143
     * @param null|array $data
144
     * @param bool       $refund
145
     *
146
     * @throws InvalidSignException
147
     * @throws InvalidConfigException
148
     *
149
     * @return Collection
150
     */
151
    public function verify($data = null, $refund = false): Collection
152
    {
153
        if (is_null($data)) {
154
            $request = Request::createFromGlobals();
155
156
            $data = $request->request->count() > 0 ? $request->request->all() : $request->query->all();
157
            $data = Support::encoding($data, 'utf-8', $data['charset'] ?? 'gb2312');
158
        }
159
160
        if (isset($data['fund_bill_list'])) {
161
            $data['fund_bill_list'] = htmlspecialchars_decode($data['fund_bill_list']);
162
        }
163
164
        Events::dispatch(Events::REQUEST_RECEIVED, new Events\RequestReceived('Alipay', '', $data));
165
166
        if (Support::verifySign($data)) {
167
            return new Collection($data);
168
        }
169
170
        Events::dispatch(Events::SIGN_FAILED, new Events\SignFailed('Alipay', '', $data));
171
172
        throw new InvalidSignException('Alipay Sign Verify FAILED', $data);
173
    }
174
175
    /**
176
     * Query an order.
177
     *
178
     * @author yansongda <[email protected]>
179
     *
180
     * @param string|array $order
181
     * @param string       $type
182
     * @param bool         $transfer @deprecated since v2.7.3
183
     *
184
     * @throws GatewayException
185
     * @throws InvalidConfigException
186
     * @throws InvalidSignException
187
     *
188
     * @return Collection
189
     */
190
    public function find($order, $type = 'wap', $transfer = false): Collection
191
    {
192
        if ($type === true || $transfer) {
193
            Log::warning('DEPRECATED: In Alipay->find(), the REFUND/TRANSFER param is deprecated since v2.7.3, use TYPE param instead!');
194
            @trigger_error('In yansongda/pay Alipay->find(), the REFUND/TRANSFER param is deprecated since v2.7.3, use TYPE param instead!', E_USER_DEPRECATED);
195
196
            $type = $type === true ? 'refund' : 'transfer';
197
        }
198
199
        if ($type === false) {
200
            Log::warning('DEPRECATED: In Alipay->find(), the REFUND/TRANSFER param is deprecated since v2.7.3, use TYPE param instead!');
201
            @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
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...
202
203
            $type = 'wap';
204
        }
205
206
        $gateway = get_class($this).'\\'.Str::studly($type).'Gateway';
207
208
        if (!class_exists($gateway) || !is_callable([new $gateway(), 'find'])) {
209
            throw new GatewayException("{$gateway} Done Not Exist Or Done Not Has FIND Method");
210
        }
211
212
        $config = call_user_func([new $gateway(), 'find'], $order);
213
214
        $this->payload['method'] = $config['method'];
215
        $this->payload['biz_content'] = $config['biz_content'];
216
        $this->payload['sign'] = Support::generateSign($this->payload);
217
218
        Events::dispatch(Events::METHOD_CALLED, new Events\MethodCalled('Alipay', 'Find', $this->gateway, $this->payload));
219
220
        return Support::requestApi($this->payload);
221
    }
222
223
    /**
224
     * Refund an order.
225
     *
226
     * @author yansongda <[email protected]>
227
     *
228
     * @param array $order
229
     *
230
     * @throws GatewayException
231
     * @throws InvalidConfigException
232
     * @throws InvalidSignException
233
     *
234
     * @return Collection
235
     */
236
    public function refund($order): Collection
237
    {
238
        $this->payload['method'] = 'alipay.trade.refund';
239
        $this->payload['biz_content'] = json_encode($order);
240
        $this->payload['sign'] = Support::generateSign($this->payload);
241
242
        Events::dispatch(Events::METHOD_CALLED, new Events\MethodCalled('Alipay', 'Refund', $this->gateway, $this->payload));
243
244
        return Support::requestApi($this->payload);
245
    }
246
247
    /**
248
     * Cancel an order.
249
     *
250
     * @author yansongda <[email protected]>
251
     *
252
     * @param array $order
253
     *
254
     * @throws GatewayException
255
     * @throws InvalidConfigException
256
     * @throws InvalidSignException
257
     *
258
     * @return Collection
259
     */
260 View Code Duplication
    public function cancel($order): Collection
261
    {
262
        $this->payload['method'] = 'alipay.trade.cancel';
263
        $this->payload['biz_content'] = json_encode(is_array($order) ? $order : ['out_trade_no' => $order]);
264
        $this->payload['sign'] = Support::generateSign($this->payload);
265
266
        Events::dispatch(Events::METHOD_CALLED, new Events\MethodCalled('Alipay', 'Cancel', $this->gateway, $this->payload));
267
268
        return Support::requestApi($this->payload);
269
    }
270
271
    /**
272
     * Close an order.
273
     *
274
     * @param string|array $order
275
     *
276
     * @author yansongda <[email protected]>
277
     *
278
     * @throws GatewayException
279
     * @throws InvalidConfigException
280
     * @throws InvalidSignException
281
     *
282
     * @return Collection
283
     */
284 View Code Duplication
    public function close($order): Collection
285
    {
286
        $this->payload['method'] = 'alipay.trade.close';
287
        $this->payload['biz_content'] = json_encode(is_array($order) ? $order : ['out_trade_no' => $order]);
288
        $this->payload['sign'] = Support::generateSign($this->payload);
289
290
        Events::dispatch(Events::METHOD_CALLED, new Events\MethodCalled('Alipay', 'Close', $this->gateway, $this->payload));
291
292
        return Support::requestApi($this->payload);
293
    }
294
295
    /**
296
     * Download bill.
297
     *
298
     * @author yansongda <[email protected]>
299
     *
300
     * @param string|array $bill
301
     *
302
     * @throws GatewayException
303
     * @throws InvalidConfigException
304
     * @throws InvalidSignException
305
     *
306
     * @return string
307
     */
308
    public function download($bill): string
309
    {
310
        $this->payload['method'] = 'alipay.data.dataservice.bill.downloadurl.query';
311
        $this->payload['biz_content'] = json_encode(is_array($bill) ? $bill : ['bill_type' => 'trade', 'bill_date' => $bill]);
312
        $this->payload['sign'] = Support::generateSign($this->payload);
313
314
        Events::dispatch(Events::METHOD_CALLED, new Events\MethodCalled('Alipay', 'Download', $this->gateway, $this->payload));
315
316
        $result = Support::requestApi($this->payload);
317
318
        return ($result instanceof Collection) ? $result->get('bill_download_url') : '';
319
    }
320
321
    /**
322
     * Reply success to alipay.
323
     *
324
     * @author yansongda <[email protected]>
325
     *
326
     * @return Response
327
     */
328
    public function success(): Response
329
    {
330
        Events::dispatch(Events::METHOD_CALLED, new Events\MethodCalled('Alipay', 'Success', $this->gateway));
331
332
        return Response::create('success');
333
    }
334
335
    /**
336
     * extend.
337
     *
338
     * @author yansongda <[email protected]>
339
     *
340
     * @param string   $method
341
     * @param callable $function
342
     *
343
     * @return void
344
     */
345
    public function extend(string $method, callable $function)
346
    {
347
    }
348
349
    /**
350
     * Make pay gateway.
351
     *
352
     * @author yansongda <[email protected]>
353
     *
354
     * @param string $gateway
355
     *
356
     * @throws InvalidGatewayException
357
     *
358
     * @return Response|Collection
359
     */
360 View Code Duplication
    protected function makePay($gateway)
361
    {
362
        $app = new $gateway();
363
364
        if ($app instanceof GatewayInterface) {
365
            return $app->pay($this->gateway, array_filter($this->payload, function ($value) {
366
                return $value !== '' && !is_null($value);
367
            }));
368
        }
369
370
        throw new InvalidGatewayException("Pay Gateway [{$gateway}] Must Be An Instance Of GatewayInterface");
371
    }
372
}
373