Completed
Push — master ( 5ea3e6...b5c521 )
by Songda
01:57
created

src/Gateways/Wechat.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\RedirectResponse;
6
use Symfony\Component\HttpFoundation\Request;
7
use Symfony\Component\HttpFoundation\Response;
8
use Yansongda\Pay\Contracts\GatewayApplicationInterface;
9
use Yansongda\Pay\Contracts\GatewayInterface;
10
use Yansongda\Pay\Events;
11
use Yansongda\Pay\Exceptions\GatewayException;
12
use Yansongda\Pay\Exceptions\InvalidGatewayException;
13
use Yansongda\Pay\Exceptions\InvalidSignException;
14
use Yansongda\Pay\Gateways\Wechat\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 groupRedpack(array $config) 分裂红包
23
 * @method Collection miniapp(array $config) 小程序支付
24
 * @method Collection mp(array $config) 公众号支付
25
 * @method Collection pos(array $config) 刷卡支付
26
 * @method Collection redpack(array $config) 普通红包
27
 * @method Collection scan(array $config) 扫码支付
28
 * @method Collection transfer(array $config) 企业付款
29
 * @method RedirectResponse wap(array $config) H5 支付
30
 */
31
class Wechat implements GatewayApplicationInterface
32
{
33
    /**
34
     * 普通模式.
35
     */
36
    const MODE_NORMAL = 'normal';
37
38
    /**
39
     * 沙箱模式.
40
     */
41
    const MODE_DEV = 'dev';
42
43
    /**
44
     * 香港钱包 API.
45
     */
46
    const MODE_HK = 'hk';
47
48
    /**
49
     * 境外 API.
50
     */
51
    const MODE_US = 'us';
52
53
    /**
54
     * 服务商模式.
55
     */
56
    const MODE_SERVICE = 'service';
57
58
    /**
59
     * Const url.
60
     */
61
    const URL = [
62
        self::MODE_NORMAL  => 'https://api.mch.weixin.qq.com/',
63
        self::MODE_DEV     => 'https://api.mch.weixin.qq.com/sandboxnew/',
64
        self::MODE_HK      => 'https://apihk.mch.weixin.qq.com/',
65
        self::MODE_SERVICE => 'https://api.mch.weixin.qq.com/',
66
        self::MODE_US      => 'https://apius.mch.weixin.qq.com/',
67
    ];
68
69
    /**
70
     * Wechat payload.
71
     *
72
     * @var array
73
     */
74
    protected $payload;
75
76
    /**
77
     * Wechat gateway.
78
     *
79
     * @var string
80
     */
81
    protected $gateway;
82
83
    /**
84
     * Bootstrap.
85
     *
86
     * @author yansongda <[email protected]>
87
     *
88
     * @param Config $config
89
     *
90
     * @throws \Exception
91
     */
92
    public function __construct(Config $config)
93
    {
94
        $this->gateway = Support::create($config)->getBaseUri();
95
        $this->payload = [
96
            'appid'            => $config->get('app_id', ''),
97
            'mch_id'           => $config->get('mch_id', ''),
98
            'nonce_str'        => Str::random(),
99
            'notify_url'       => $config->get('notify_url', ''),
100
            'sign'             => '',
101
            'trade_type'       => '',
102
            'spbill_create_ip' => Request::createFromGlobals()->getClientIp(),
103
        ];
104
105
        if ($config->get('mode', self::MODE_NORMAL) === static::MODE_SERVICE) {
106
            $this->payload = array_merge($this->payload, [
107
                'sub_mch_id' => $config->get('sub_mch_id'),
108
                'sub_appid'  => $config->get('sub_app_id', ''),
109
            ]);
110
        }
111
    }
112
113
    /**
114
     * Magic pay.
115
     *
116
     * @author yansongda <[email protected]>
117
     *
118
     * @param string $method
119
     * @param string $params
120
     *
121
     * @throws InvalidGatewayException
122
     *
123
     * @return Response|Collection
124
     */
125
    public function __call($method, $params)
126
    {
127
        return self::pay($method, ...$params);
128
    }
129
130
    /**
131
     * Pay an order.
132
     *
133
     * @author yansongda <[email protected]>
134
     *
135
     * @param string $gateway
136
     * @param array  $params
137
     *
138
     * @throws InvalidGatewayException
139
     *
140
     * @return Response|Collection
141
     */
142
    public function pay($gateway, $params = [])
143
    {
144
        Events::dispatch(Events::PAY_STARTING, new Events\PayStarting('Wechat', $gateway, $params));
145
146
        $this->payload = array_merge($this->payload, $params);
147
148
        $gateway = get_class($this).'\\'.Str::studly($gateway).'Gateway';
149
150
        if (class_exists($gateway)) {
151
            return $this->makePay($gateway);
152
        }
153
154
        throw new InvalidGatewayException("Pay Gateway [{$gateway}] Not Exists");
155
    }
156
157
    /**
158
     * Verify data.
159
     *
160
     * @author yansongda <[email protected]>
161
     *
162
     * @param string|null $content
163
     * @param bool        $refund
164
     *
165
     * @throws InvalidSignException
166
     * @throws \Yansongda\Pay\Exceptions\InvalidArgumentException
167
     *
168
     * @return Collection
169
     */
170
    public function verify($content = null, $refund = false): Collection
171
    {
172
        $content = $content ?? Request::createFromGlobals()->getContent();
173
174
        Events::dispatch(Events::REQUEST_RECEIVED, new Events\RequestReceived('Wechat', '', [$content]));
175
176
        $data = Support::fromXml($content);
177
        if ($refund) {
178
            $decrypt_data = Support::decryptRefundContents($data['req_info']);
179
            $data = array_merge(Support::fromXml($decrypt_data), $data);
180
        }
181
182
        Log::debug('Resolved The Received Wechat Request Data', $data);
183
184
        if ($refund || Support::generateSign($data) === $data['sign']) {
185
            return new Collection($data);
186
        }
187
188
        Events::dispatch(Events::SIGN_FAILED, new Events\SignFailed('Wechat', '', $data));
189
190
        throw new InvalidSignException('Wechat Sign Verify FAILED', $data);
191
    }
192
193
    /**
194
     * Query an order.
195
     *
196
     * @author yansongda <[email protected]>
197
     *
198
     * @param string|array $order
199
     * @param bool         $refund
200
     *
201
     * @throws GatewayException
202
     * @throws InvalidSignException
203
     * @throws \Yansongda\Pay\Exceptions\InvalidArgumentException
204
     *
205
     * @return Collection
206
     */
207 View Code Duplication
    public function find($order, $refund = false): Collection
0 ignored issues
show
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...
208
    {
209
        if ($refund) {
210
            unset($this->payload['spbill_create_ip']);
211
        }
212
213
        $this->payload = Support::filterPayload($this->payload, $order);
214
215
        Events::dispatch(Events::METHOD_CALLED, new Events\MethodCalled('Wechat', 'Find', $this->gateway, $this->payload));
216
217
        return Support::requestApi(
218
            $refund ? 'pay/refundquery' : 'pay/orderquery',
219
            $this->payload
220
        );
221
    }
222
223
    /**
224
     * Refund an order.
225
     *
226
     * @author yansongda <[email protected]>
227
     *
228
     * @param array $order
229
     *
230
     * @throws GatewayException
231
     * @throws InvalidSignException
232
     * @throws \Yansongda\Pay\Exceptions\InvalidArgumentException
233
     *
234
     * @return Collection
235
     */
236
    public function refund($order): Collection
237
    {
238
        $this->payload = Support::filterPayload($this->payload, $order, true);
239
240
        Events::dispatch(Events::METHOD_CALLED, new Events\MethodCalled('Wechat', 'Refund', $this->gateway, $this->payload));
241
242
        return Support::requestApi(
243
            'secapi/pay/refund',
244
            $this->payload,
245
            true
246
        );
247
    }
248
249
    /**
250
     * Cancel an order.
251
     *
252
     * @author yansongda <[email protected]>
253
     *
254
     * @param array $order
255
     *
256
     * @throws GatewayException
257
     *
258
     * @return Collection
259
     */
260
    public function cancel($order): Collection
261
    {
262
        Events::dispatch(Events::METHOD_CALLED, new Events\MethodCalled('Wechat', 'Cancel', $this->gateway, $this->payload));
263
264
        throw new GatewayException('Wechat Do Not Have Cancel API! Please use Close API!');
265
    }
266
267
    /**
268
     * Close an order.
269
     *
270
     * @author yansongda <[email protected]>
271
     *
272
     * @param string|array $order
273
     *
274
     * @throws GatewayException
275
     * @throws InvalidSignException
276
     * @throws \Yansongda\Pay\Exceptions\InvalidArgumentException
277
     *
278
     * @return Collection
279
     */
280 View Code Duplication
    public function close($order): Collection
281
    {
282
        unset($this->payload['spbill_create_ip']);
283
284
        $this->payload = Support::filterPayload($this->payload, $order);
285
286
        Events::dispatch(Events::METHOD_CALLED, new Events\MethodCalled('Wechat', 'Close', $this->gateway, $this->payload));
287
288
        return Support::requestApi('pay/closeorder', $this->payload);
289
    }
290
291
    /**
292
     * Echo success to server.
293
     *
294
     * @author yansongda <[email protected]>
295
     *
296
     * @throws \Yansongda\Pay\Exceptions\InvalidArgumentException
297
     *
298
     * @return Response
299
     */
300
    public function success(): Response
301
    {
302
        Events::dispatch(Events::METHOD_CALLED, new Events\MethodCalled('Wechat', 'Success', $this->gateway));
303
304
        return Response::create(
305
            Support::toXml(['return_code' => 'SUCCESS', 'return_msg' => 'OK']),
306
            200,
307
            ['Content-Type' => 'application/xml']
308
        );
309
    }
310
311
    /**
312
     * Make pay gateway.
313
     *
314
     * @author yansongda <[email protected]>
315
     *
316
     * @param string $gateway
317
     *
318
     * @throws InvalidGatewayException
319
     *
320
     * @return Response|Collection
321
     */
322 View Code Duplication
    protected function makePay($gateway)
323
    {
324
        $app = new $gateway();
325
326
        if ($app instanceof GatewayInterface) {
327
            return $app->pay($this->gateway, array_filter($this->payload, function ($value) {
328
                return $value !== '' && !is_null($value);
329
            }));
330
        }
331
332
        throw new InvalidGatewayException("Pay Gateway [{$gateway}] Must Be An Instance Of GatewayInterface");
333
    }
334
}
335