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