Completed
Push — master ( c3ded3...e46555 )
by Songda
13s
created

src/Gateways/Wechat.php (2 issues)

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