Completed
Push — master ( c3ded3...e46555 )
by Songda
13s
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\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);
0 ignored issues
show
$params is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
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
    public function refund($order): Collection
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
    public function close($order): Collection
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