1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace EntWeChat\Card; |
4
|
|
|
|
5
|
|
|
use Doctrine\Common\Cache\Cache; |
6
|
|
|
use Doctrine\Common\Cache\FilesystemCache; |
7
|
|
|
use EntWeChat\Core\AbstractAPI; |
8
|
|
|
use EntWeChat\Support\Arr; |
9
|
|
|
use Psr\Http\Message\ResponseInterface; |
10
|
|
|
|
11
|
|
|
class Card extends AbstractAPI |
12
|
|
|
{ |
13
|
|
|
/** |
14
|
|
|
* Cache. |
15
|
|
|
* |
16
|
|
|
* @var Cache |
17
|
|
|
*/ |
18
|
|
|
protected $cache; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Ticket cache key. |
22
|
|
|
* |
23
|
|
|
* @var string |
24
|
|
|
*/ |
25
|
|
|
protected $ticketCacheKey; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Ticket cache prefix. |
29
|
|
|
* |
30
|
|
|
* @var string |
31
|
|
|
*/ |
32
|
|
|
protected $ticketCachePrefix = 'entwechat.card_api_ticket.'; |
33
|
|
|
|
34
|
|
|
const API_CREATE_CARD = 'https://qyapi.weixin.qq.com/cgi-bin/card/create'; |
35
|
|
|
const API_CREATE_QRCODE = 'https://qyapi.weixin.qq.com/cgi-bin/card/qrcode/create'; |
36
|
|
|
const API_SHOW_QRCODE = 'https://mp.weixin.qq.com/cgi-bin/showqrcode'; |
37
|
|
|
const API_GET_CARD_TICKET = 'https://qyapi.weixin.qq.com/cgi-bin/ticket/get'; |
38
|
|
|
const API_GET_HTML = 'https://qyapi.weixin.qq.com/cgi-bin/card/mpnews/gethtml'; |
39
|
|
|
const API_GET_CODE = 'https://qyapi.weixin.qq.com/cgi-bin/card/code/get'; |
40
|
|
|
const API_CONSUME_CARD = 'https://qyapi.weixin.qq.com/cgi-bin/card/code/consume'; |
41
|
|
|
const API_GET_CARD = 'https://qyapi.weixin.qq.com/cgi-bin/card/get'; |
42
|
|
|
const API_LIST_CARD = 'https://qyapi.weixin.qq.com/cgi-bin/card/batchget'; |
43
|
|
|
const API_MODIFY_STOCK = 'https://qyapi.weixin.qq.com/cgi-bin/card/modifystock'; |
44
|
|
|
const API_DELETE_CARD = 'https://qyapi.weixin.qq.com/cgi-bin/card/delete'; |
45
|
|
|
|
46
|
|
|
// 卡券类型 |
47
|
|
|
const TYPE_GENERAL_COUPON = 'GENERAL_COUPON'; // 通用券 |
48
|
|
|
const TYPE_GROUPON = 'GROUPON'; // 团购券 |
49
|
|
|
const TYPE_DISCOUNT = 'DISCOUNT'; // 折扣券 |
50
|
|
|
const TYPE_GIFT = 'GIFT'; // 礼品券 |
51
|
|
|
const TYPE_CASH = 'CASH'; // 代金券 |
52
|
|
|
|
53
|
|
|
// 卡券状态 |
54
|
|
|
const CARD_STATUS_NOT_VERIFY = 'CARD_STATUS_NOT_VERIFY'; // 待审核 |
55
|
|
|
const CARD_STATUS_VERIFY_FAIL = 'CARD_STATUS_VERIFY_FAIL'; // 审核失败 |
56
|
|
|
const CARD_STATUS_VERIFY_OK = 'CARD_STATUS_VERIFY_OK'; // 通过审核 |
57
|
|
|
const CARD_STATUS_USER_DELETE = 'CARD_STATUS_USER_DELETE'; // 卡券被商户删除 |
58
|
|
|
const CARD_STATUS_USER_DISPATCH = 'CARD_STATUS_USER_DISPATCH'; // 在公众平台投放过的卡券 |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* 创建卡券. |
62
|
|
|
* |
63
|
|
|
* @param string $cardType |
64
|
|
|
* @param array $baseInfo |
65
|
|
|
* @param array $especial |
66
|
|
|
* @param array $advancedInfo |
67
|
|
|
* |
68
|
|
|
* @return \EntWeChat\Support\Collection |
69
|
|
|
*/ |
70
|
|
|
public function create($cardType = 'member_card', array $baseInfo = [], array $especial = [], array $advancedInfo = []) |
71
|
|
|
{ |
72
|
|
|
$params = [ |
73
|
|
|
'card' => [ |
74
|
|
|
'card_type' => strtoupper($cardType), |
75
|
|
|
strtolower($cardType) => array_merge(['base_info' => $baseInfo], $especial, ['advanced_info' => $advancedInfo]), |
76
|
|
|
], |
77
|
|
|
]; |
78
|
|
|
|
79
|
|
|
return $this->parseJSON('json', [self::API_CREATE_CARD, $params]); |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* 创建二维码. |
84
|
|
|
* |
85
|
|
|
* @param array $cards |
86
|
|
|
* |
87
|
|
|
* @return \EntWeChat\Support\Collection |
88
|
|
|
*/ |
89
|
|
|
public function QRCode(array $cards = []) |
90
|
|
|
{ |
91
|
|
|
return $this->parseJSON('json', [self::API_CREATE_QRCODE, $cards]); |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* ticket 换取二维码图片. |
96
|
|
|
* |
97
|
|
|
* @param string $ticket |
98
|
|
|
* |
99
|
|
|
* @return array |
100
|
|
|
*/ |
101
|
|
|
public function showQRCode($ticket = null) |
102
|
|
|
{ |
103
|
|
|
$params = [ |
104
|
|
|
'ticket' => $ticket, |
105
|
|
|
]; |
106
|
|
|
|
107
|
|
|
$http = $this->getHttp(); |
108
|
|
|
|
109
|
|
|
/** @var ResponseInterface $response */ |
110
|
|
|
$response = $http->get(self::API_SHOW_QRCODE, $params); |
111
|
|
|
|
112
|
|
|
return [ |
113
|
|
|
'status' => $response->getStatusCode(), |
114
|
|
|
'reason' => $response->getReasonPhrase(), |
115
|
|
|
'headers' => $response->getHeaders(), |
116
|
|
|
'body' => strval($response->getBody()), |
117
|
|
|
'url' => self::API_SHOW_QRCODE.'?'.http_build_query($params), |
118
|
|
|
]; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* 通过ticket换取二维码 链接. |
123
|
|
|
* |
124
|
|
|
* @param string $ticket |
125
|
|
|
* |
126
|
|
|
* @return string |
127
|
|
|
*/ |
128
|
|
|
public function getQRCodeUrl($ticket) |
129
|
|
|
{ |
130
|
|
|
return self::API_SHOW_QRCODE.'?ticket='.$ticket; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* 获取 卡券 Api_ticket. |
135
|
|
|
* |
136
|
|
|
* @param bool $refresh 是否强制刷新 |
137
|
|
|
* |
138
|
|
|
* @return string $apiTicket |
139
|
|
|
*/ |
140
|
|
|
public function getAPITicket($refresh = false) |
141
|
|
|
{ |
142
|
|
|
$key = $this->getTicketCacheKey(); |
143
|
|
|
|
144
|
|
|
$ticket = $this->getCache()->fetch($key); |
145
|
|
|
|
146
|
|
|
if (!$ticket || $refresh) { |
147
|
|
|
$result = $this->parseJSON('get', [self::API_GET_CARD_TICKET, ['type' => 'wx_card']]); |
148
|
|
|
|
149
|
|
|
$this->getCache()->save($key, $result['ticket'], $result['expires_in'] - 500); |
150
|
|
|
|
151
|
|
|
return $result['ticket']; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
return $ticket; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* 微信卡券:JSAPI 卡券发放. |
159
|
|
|
* |
160
|
|
|
* @param array $cards |
161
|
|
|
* |
162
|
|
|
* @return string |
163
|
|
|
*/ |
164
|
|
|
public function jsConfigForAssign(array $cards) |
165
|
|
|
{ |
166
|
|
|
return json_encode(array_map(function ($card) { |
167
|
|
|
return $this->attachExtension($card['card_id'], $card); |
168
|
|
|
}, $cards)); |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* 生成 js添加到卡包 需要的 card_list 项. |
173
|
|
|
* |
174
|
|
|
* @param string $cardId |
175
|
|
|
* @param array $extension |
176
|
|
|
* |
177
|
|
|
* @return string |
178
|
|
|
*/ |
179
|
|
|
public function attachExtension($cardId, array $extension = []) |
180
|
|
|
{ |
181
|
|
|
$timestamp = time(); |
182
|
|
|
$ext = [ |
183
|
|
|
'code' => Arr::get($extension, 'code'), |
184
|
|
|
'openid' => Arr::get($extension, 'openid', Arr::get($extension, 'open_id')), |
185
|
|
|
'timestamp' => $timestamp, |
186
|
|
|
'outer_id' => Arr::get($extension, 'outer_id'), |
187
|
|
|
'balance' => Arr::get($extension, 'balance'), |
188
|
|
|
'fixed_begintimestamp' => Arr::get($extension, 'fixed_begintimestamp'), |
189
|
|
|
'outer_str' => Arr::get($extension, 'outer_str'), |
190
|
|
|
]; |
191
|
|
|
$ext['signature'] = $this->getSignature( |
192
|
|
|
$this->getAPITicket(), |
193
|
|
|
$timestamp, |
194
|
|
|
$cardId, |
195
|
|
|
$ext['code'], |
196
|
|
|
$ext['openid'], |
197
|
|
|
$ext['balance'] |
198
|
|
|
); |
199
|
|
|
|
200
|
|
|
return [ |
201
|
|
|
'cardId' => $cardId, |
202
|
|
|
'cardExt' => json_encode($ext), |
203
|
|
|
]; |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* 生成签名. |
208
|
|
|
* |
209
|
|
|
* @return string |
210
|
|
|
*/ |
211
|
|
|
public function getSignature() |
212
|
|
|
{ |
213
|
|
|
$params = func_get_args(); |
214
|
|
|
sort($params, SORT_STRING); |
215
|
|
|
|
216
|
|
|
return sha1(implode($params)); |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* 核查code接口. |
221
|
|
|
* |
222
|
|
|
* @param string $cardId |
223
|
|
|
* @param array $code |
224
|
|
|
* |
225
|
|
|
* @return \EntWeChat\Support\Collection |
226
|
|
|
*/ |
227
|
|
View Code Duplication |
public function checkCode($cardId, $code) |
|
|
|
|
228
|
|
|
{ |
229
|
|
|
$params = [ |
230
|
|
|
'card_id' => $cardId, |
231
|
|
|
'code' => $code, |
232
|
|
|
]; |
233
|
|
|
|
234
|
|
|
return $this->parseJSON('json', [self::API_CHECK_CODE, $params]); |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* 查询Code接口. |
239
|
|
|
* |
240
|
|
|
* @param string $code |
241
|
|
|
* @param bool $checkConsume |
242
|
|
|
* @param string $cardId |
243
|
|
|
* |
244
|
|
|
* @return \EntWeChat\Support\Collection |
245
|
|
|
*/ |
246
|
|
View Code Duplication |
public function getCode($code, $checkConsume, $cardId) |
|
|
|
|
247
|
|
|
{ |
248
|
|
|
$params = [ |
249
|
|
|
'code' => $code, |
250
|
|
|
'check_consume' => $checkConsume, |
251
|
|
|
'card_id' => $cardId, |
252
|
|
|
]; |
253
|
|
|
|
254
|
|
|
return $this->parseJSON('json', [self::API_GET_CODE, $params]); |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* 核销Code接口. |
259
|
|
|
* |
260
|
|
|
* @param string $code |
261
|
|
|
* @param string $cardId |
262
|
|
|
* |
263
|
|
|
* @return \EntWeChat\Support\Collection |
264
|
|
|
*/ |
265
|
|
|
public function consume($code, $cardId = null) |
266
|
|
|
{ |
267
|
|
|
if (strlen($code) === 28 && $cardId && strlen($cardId) !== 28) { |
|
|
|
|
268
|
|
|
list($code, $cardId) = [$cardId, $code]; |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
$params = [ |
272
|
|
|
'code' => $code, |
273
|
|
|
]; |
274
|
|
|
|
275
|
|
|
if ($cardId) { |
|
|
|
|
276
|
|
|
$params['card_id'] = $cardId; |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
return $this->parseJSON('json', [self::API_CONSUME_CARD, $params]); |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* 图文消息群发卡券. |
284
|
|
|
* |
285
|
|
|
* @param string $cardId |
286
|
|
|
* |
287
|
|
|
* @return \EntWeChat\Support\Collection |
288
|
|
|
*/ |
289
|
|
View Code Duplication |
public function getHtml($cardId) |
|
|
|
|
290
|
|
|
{ |
291
|
|
|
$params = [ |
292
|
|
|
'card_id' => $cardId, |
293
|
|
|
]; |
294
|
|
|
|
295
|
|
|
return $this->parseJSON('json', [self::API_GET_HTML, $params]); |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
/** |
299
|
|
|
* 查看卡券详情. |
300
|
|
|
* |
301
|
|
|
* @param string $cardId |
302
|
|
|
* |
303
|
|
|
* @return \EntWeChat\Support\Collection |
304
|
|
|
*/ |
305
|
|
View Code Duplication |
public function getCard($cardId) |
|
|
|
|
306
|
|
|
{ |
307
|
|
|
$params = [ |
308
|
|
|
'card_id' => $cardId, |
309
|
|
|
]; |
310
|
|
|
|
311
|
|
|
return $this->parseJSON('json', [self::API_GET_CARD, $params]); |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
/** |
315
|
|
|
* 批量查询卡列表. |
316
|
|
|
* |
317
|
|
|
* @param int $offset |
318
|
|
|
* @param int $count |
319
|
|
|
* @param string $statusList |
320
|
|
|
* |
321
|
|
|
* @return \EntWeChat\Support\Collection |
322
|
|
|
*/ |
323
|
|
View Code Duplication |
public function lists($offset = 0, $count = 10, $statusList = 'CARD_STATUS_VERIFY_OK') |
|
|
|
|
324
|
|
|
{ |
325
|
|
|
$params = [ |
326
|
|
|
'offset' => $offset, |
327
|
|
|
'count' => $count, |
328
|
|
|
'status_list' => $statusList, |
329
|
|
|
]; |
330
|
|
|
|
331
|
|
|
return $this->parseJSON('json', [self::API_LIST_CARD, $params]); |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
/** |
335
|
|
|
* 增加库存. |
336
|
|
|
* |
337
|
|
|
* @param string $cardId |
338
|
|
|
* @param int $amount |
339
|
|
|
* |
340
|
|
|
* @return \EntWeChat\Support\Collection |
341
|
|
|
*/ |
342
|
|
|
public function increaseStock($cardId, $amount) |
343
|
|
|
{ |
344
|
|
|
return $this->updateStock($cardId, $amount, 'increase'); |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
/** |
348
|
|
|
* 减少库存. |
349
|
|
|
* |
350
|
|
|
* @param string $cardId |
351
|
|
|
* @param int $amount |
352
|
|
|
* |
353
|
|
|
* @return \EntWeChat\Support\Collection |
354
|
|
|
*/ |
355
|
|
|
public function reduceStock($cardId, $amount) |
356
|
|
|
{ |
357
|
|
|
return $this->updateStock($cardId, $amount, 'reduce'); |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
/** |
361
|
|
|
* 修改库存接口. |
362
|
|
|
* |
363
|
|
|
* @param string $cardId |
364
|
|
|
* @param int $amount |
365
|
|
|
* @param string $action |
366
|
|
|
* |
367
|
|
|
* @return \EntWeChat\Support\Collection |
368
|
|
|
*/ |
369
|
|
|
protected function updateStock($cardId, $amount, $action = 'increase') |
370
|
|
|
{ |
371
|
|
|
$key = $action === 'increase' ? 'increase_stock_value' : 'reduce_stock_value'; |
372
|
|
|
$params = [ |
373
|
|
|
'card_id' => $cardId, |
374
|
|
|
$key => abs($amount), |
375
|
|
|
]; |
376
|
|
|
|
377
|
|
|
return $this->parseJSON('json', [self::API_MODIFY_STOCK, $params]); |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
/** |
381
|
|
|
* 删除卡券接口. |
382
|
|
|
* |
383
|
|
|
* @param string $cardId |
384
|
|
|
* |
385
|
|
|
* @return \EntWeChat\Support\Collection |
386
|
|
|
*/ |
387
|
|
View Code Duplication |
public function delete($cardId) |
|
|
|
|
388
|
|
|
{ |
389
|
|
|
$params = [ |
390
|
|
|
'card_id' => $cardId, |
391
|
|
|
]; |
392
|
|
|
|
393
|
|
|
return $this->parseJSON('json', [self::API_DELETE_CARD, $params]); |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
/** |
397
|
|
|
* Set cache manager. |
398
|
|
|
* |
399
|
|
|
* @param \Doctrine\Common\Cache\Cache $cache |
400
|
|
|
* |
401
|
|
|
* @return $this |
402
|
|
|
*/ |
403
|
|
|
public function setCache(Cache $cache) |
404
|
|
|
{ |
405
|
|
|
$this->cache = $cache; |
406
|
|
|
|
407
|
|
|
return $this; |
408
|
|
|
} |
409
|
|
|
|
410
|
|
|
/** |
411
|
|
|
* Return cache manager. |
412
|
|
|
* |
413
|
|
|
* @return \Doctrine\Common\Cache\Cache |
414
|
|
|
*/ |
415
|
|
|
public function getCache() |
416
|
|
|
{ |
417
|
|
|
return $this->cache ?: $this->cache = new FilesystemCache(sys_get_temp_dir()); |
418
|
|
|
} |
419
|
|
|
|
420
|
|
|
/** |
421
|
|
|
* Set Api_ticket cache prifix. |
422
|
|
|
* |
423
|
|
|
* @param string $prefix |
424
|
|
|
* |
425
|
|
|
* @return $this |
426
|
|
|
*/ |
427
|
|
|
public function setTicketCachePrefix($prefix) |
428
|
|
|
{ |
429
|
|
|
$this->ticketCachePrefix = $prefix; |
430
|
|
|
|
431
|
|
|
return $this; |
432
|
|
|
} |
433
|
|
|
|
434
|
|
|
/** |
435
|
|
|
* Set Api_ticket cache key. |
436
|
|
|
* |
437
|
|
|
* @param string $cacheKey |
438
|
|
|
* |
439
|
|
|
* @return $this |
440
|
|
|
*/ |
441
|
|
|
public function setTicketCacheKey($cacheKey) |
442
|
|
|
{ |
443
|
|
|
$this->ticketCacheKey = $cacheKey; |
444
|
|
|
|
445
|
|
|
return $this; |
446
|
|
|
} |
447
|
|
|
|
448
|
|
|
/** |
449
|
|
|
* Get ApiTicket token cache key. |
450
|
|
|
* |
451
|
|
|
* @return string |
452
|
|
|
*/ |
453
|
|
|
public function getTicketCacheKey() |
454
|
|
|
{ |
455
|
|
|
if (is_null($this->ticketCacheKey)) { |
456
|
|
|
return $this->ticketCachePrefix.$this->getAccessToken()->getFingerprint(); |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
return $this->ticketCacheKey; |
460
|
|
|
} |
461
|
|
|
|
462
|
|
|
/** |
463
|
|
|
* Set current url. |
464
|
|
|
* |
465
|
|
|
* @param string $url |
466
|
|
|
* |
467
|
|
|
* @return Card |
468
|
|
|
*/ |
469
|
|
|
public function setUrl($url) |
470
|
|
|
{ |
471
|
|
|
$this->url = $url; |
|
|
|
|
472
|
|
|
|
473
|
|
|
return $this; |
474
|
|
|
} |
475
|
|
|
} |
476
|
|
|
|
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.