<?php /* * This file is part of the overtrue/wechat. * * (c) overtrue <i@overtrue.me> * * This source file is subject to the MIT license that is bundled * with this source code in the file LICENSE. */ /** * Card.php. * * @author overtrue <i@overtrue.me> * @copyright 2016 overtrue <i@overtrue.me> * * @see https://github.com/overtrue * @see http://overtrue.me */ namespace EasyWeChat\Card; use Doctrine\Common\Cache\Cache; use Doctrine\Common\Cache\FilesystemCache; use EasyWeChat\Core\AbstractAPI; use EasyWeChat\Support\Arr; use Psr\Http\Message\ResponseInterface; class Card extends AbstractAPI { /** * Cache. * * @var Cache */ protected $cache; /** * Ticket cache key. * * @var string */ protected $ticketCacheKey; /** * Ticket cache prefix. * * @var string */ protected $ticketCachePrefix = 'overtrue.wechat.card_api_ticket.'; const API_GET_COLORS = 'https://api.weixin.qq.com/card/getcolors'; const API_CREATE_CARD = 'https://api.weixin.qq.com/card/create'; const API_CREATE_QRCODE = 'https://api.weixin.qq.com/card/qrcode/create'; const API_SHOW_QRCODE = 'https://mp.weixin.qq.com/cgi-bin/showqrcode'; const API_GET_CARD_TICKET = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket'; const API_CREATE_LANDING_PAGE = 'https://api.weixin.qq.com/card/landingpage/create'; const API_DEPOSIT_CODE = 'https://api.weixin.qq.com/card/code/deposit'; const API_GET_DEPOSIT_COUNT = 'https://api.weixin.qq.com/card/code/getdepositcount'; const API_CHECK_CODE = 'https://api.weixin.qq.com/card/code/checkcode'; const API_GET_HTML = 'https://api.weixin.qq.com/card/mpnews/gethtml'; const API_SET_TEST_WHITE_LIST = 'https://api.weixin.qq.com/card/testwhitelist/set'; const API_GET_CODE = 'https://api.weixin.qq.com/card/code/get'; const API_CONSUME_CARD = 'https://api.weixin.qq.com/card/code/consume'; const API_DECRYPT_CODE = 'https://api.weixin.qq.com/card/code/decrypt'; const API_GET_CARD_LIST = 'https://api.weixin.qq.com/card/user/getcardlist'; const API_GET_CARD = 'https://api.weixin.qq.com/card/get'; const API_LIST_CARD = 'https://api.weixin.qq.com/card/batchget'; const API_UPDATE_CARD = 'https://api.weixin.qq.com/card/update'; const API_SET_PAY_CELL = 'https://api.weixin.qq.com/card/paycell/set'; const API_MODIFY_STOCK = 'https://api.weixin.qq.com/card/modifystock'; const API_UPDATE_CODE = 'https://api.weixin.qq.com/card/code/update'; const API_DELETE_CARD = 'https://api.weixin.qq.com/card/delete'; const API_DISABLE_CARD = 'https://api.weixin.qq.com/card/code/unavailable'; const API_ACTIVATE_MEMBER_CARD = 'https://api.weixin.qq.com/card/membercard/activate'; const API_ACTIVATE_MEMBER_USER_FORM = 'https://api.weixin.qq.com/card/membercard/activateuserform/set'; const API_GET_MEMBER_USER_INFO = 'https://api.weixin.qq.com/card/membercard/userinfo/get'; const API_UPDATE_MEMBER_CARD_USER = 'https://api.weixin.qq.com/card/membercard/updateuser'; const API_CREATE_SUB_MERCHANT = 'https://api.weixin.qq.com/card/submerchant/submit'; const API_UPDATE_SUB_MERCHANT = 'https://api.weixin.qq.com/card/submerchant/update'; const API_GET_SUB_MERCHANT = 'https://api.weixin.qq.com/card/submerchant/get'; const API_LIST_SUB_MERCHANT = 'https://api.weixin.qq.com/card/submerchant/batchget'; const API_GET_CATEGORIES = 'https://api.weixin.qq.com/card/getapplyprotocol'; const API_ACTIVATE_GENERAL_CARD = 'https://api.weixin.qq.com/card/generalcard/activate'; const API_UPDATE_GENERAL_CARD_USER = 'https://api.weixin.qq.com/card/generalcard/updateuser'; /** * 获取卡券颜色. * * @return \EasyWeChat\Support\Collection */ public function getColors() { return $this->parseJSON('get', [self::API_GET_COLORS]); } /** * 创建卡券. * * @param string $cardType * @param array $baseInfo * @param array $especial * @param array $advancedInfo * * @return \EasyWeChat\Support\Collection */ public function create($cardType = 'member_card', array $baseInfo = [], array $especial = [], array $advancedInfo = []) { $params = [ 'card' => [ 'card_type' => strtoupper($cardType), strtolower($cardType) => array_merge(['base_info' => $baseInfo], $especial, ['advanced_info' => $advancedInfo]), ], ]; return $this->parseJSON('json', [self::API_CREATE_CARD, $params]); } /** * 创建二维码. * * @param array $cards * * @return \EasyWeChat\Support\Collection */ public function QRCode(array $cards = []) { return $this->parseJSON('json', [self::API_CREATE_QRCODE, $cards]); } /** * ticket 换取二维码图片. * * @param string $ticket * * @return array */ public function showQRCode($ticket = null) { $params = [ 'ticket' => $ticket, ]; $http = $this->getHttp(); /** @var ResponseInterface $response */ $response = $http->get(self::API_SHOW_QRCODE, $params); return [ 'status' => $response->getStatusCode(), 'reason' => $response->getReasonPhrase(), 'headers' => $response->getHeaders(), 'body' => strval($response->getBody()), 'url' => self::API_SHOW_QRCODE.'?'.http_build_query($params), ]; } /** * 通过ticket换取二维码 链接. * * @param string $ticket * * @return string */ public function getQRCodeUrl($ticket) { return self::API_SHOW_QRCODE.'?ticket='.$ticket; } /** * 获取 卡券 Api_ticket. * * @param bool $refresh 是否强制刷新 * * @return string $apiTicket */ public function getAPITicket($refresh = false) { $key = $this->getTicketCacheKey(); $ticket = $this->getCache()->fetch($key); if (!$ticket || $refresh) { $result = $this->parseJSON('get', [self::API_GET_CARD_TICKET, ['type' => 'wx_card']]); $this->getCache()->save($key, $result['ticket'], $result['expires_in'] - 500); return $result['ticket']; } return $ticket; } /** * 微信卡券:JSAPI 卡券发放. * * @param array $cards * * @return string */ public function jsConfigForAssign(array $cards) { return json_encode(array_map(function ($card) { return $this->attachExtension($card['card_id'], $card); }, $cards)); } /** * 生成 js添加到卡包 需要的 card_list 项. * * @param string $cardId * @param array $extension * * @return string */ public function attachExtension($cardId, array $extension = []) { $timestamp = time(); $ext = [ 'code' => Arr::get($extension, 'code'), 'openid' => Arr::get($extension, 'openid', Arr::get($extension, 'open_id')), 'timestamp' => $timestamp, 'outer_id' => Arr::get($extension, 'outer_id'), 'balance' => Arr::get($extension, 'balance'), 'fixed_begintimestamp' => Arr::get($extension, 'fixed_begintimestamp'), 'outer_str' => Arr::get($extension, 'outer_str'), ]; $ext['signature'] = $this->getSignature( $this->getAPITicket(), $timestamp, $cardId, $ext['code'], $ext['openid'], $ext['balance'] ); return [ 'cardId' => $cardId, 'cardExt' => json_encode($ext), ]; } /** * 生成签名. * * @return string */ public function getSignature() { $params = func_get_args(); sort($params, SORT_STRING); return sha1(implode($params)); } /** * 创建货架接口. * * @param string $banner * @param string $pageTitle * @param bool $canShare * @param string $scene [SCENE_NEAR_BY 附近,SCENE_MENU 自定义菜单,SCENE_QRCODE 二维码,SCENE_ARTICLE 公众号文章, * SCENE_H5 h5页面,SCENE_IVR 自动回复,SCENE_CARD_CUSTOM_CELL 卡券自定义cell] * @param array $cardList * * @return \EasyWeChat\Support\Collection */ public function createLandingPage($banner, $pageTitle, $canShare, $scene, $cardList) { $params = [ 'banner' => $banner, 'page_title' => $pageTitle, 'can_share' => $canShare, 'scene' => $scene, 'card_list' => $cardList, ]; return $this->parseJSON('json', [self::API_CREATE_LANDING_PAGE, $params]); } /** * 导入code接口. * * @param string $cardId * @param array $code * * @return \EasyWeChat\Support\Collection */ public function deposit($cardId, $code) { $params = [ 'card_id' => $cardId, 'code' => $code, ]; return $this->parseJSON('json', [self::API_DEPOSIT_CODE, $params]); } /** * 查询导入code数目. * * @param string $cardId * * @return \EasyWeChat\Support\Collection */ public function getDepositedCount($cardId) { $params = [ 'card_id' => $cardId, ]; return $this->parseJSON('json', [self::API_GET_DEPOSIT_COUNT, $params]); } /** * 核查code接口. * * @param string $cardId * @param array $code * * @return \EasyWeChat\Support\Collection */ public function checkCode($cardId, $code) { $params = [ 'card_id' => $cardId, 'code' => $code, ]; return $this->parseJSON('json', [self::API_CHECK_CODE, $params]); } /** * 查询Code接口. * * @param string $code * @param bool $checkConsume * @param string $cardId * * @return \EasyWeChat\Support\Collection */ public function getCode($code, $checkConsume, $cardId) { $params = [ 'code' => $code, 'check_consume' => $checkConsume, 'card_id' => $cardId, ]; return $this->parseJSON('json', [self::API_GET_CODE, $params]); } /** * 核销Code接口. * * @param string $code * @param string $cardId * * @return \EasyWeChat\Support\Collection */ public function consume($code, $cardId = null) { if (28 === strlen($code) && $cardId && 28 !== strlen($cardId)) { list($code, $cardId) = [$cardId, $code]; } $params = [ 'code' => $code, ]; if ($cardId) { $params['card_id'] = $cardId; } return $this->parseJSON('json', [self::API_CONSUME_CARD, $params]); } /** * Code解码接口. * * @param string $encryptedCode * * @return \EasyWeChat\Support\Collection */ public function decryptCode($encryptedCode) { $params = [ 'encrypt_code' => $encryptedCode, ]; return $this->parseJSON('json', [self::API_DECRYPT_CODE, $params]); } /** * 图文消息群发卡券. * * @param string $cardId * * @return \EasyWeChat\Support\Collection */ public function getHtml($cardId) { $params = [ 'card_id' => $cardId, ]; return $this->parseJSON('json', [self::API_GET_HTML, $params]); } /** * 设置测试白名单. * * @param array $openids * * @return \EasyWeChat\Support\Collection */ public function setTestWhitelist($openids) { $params = [ 'openid' => $openids, ]; return $this->parseJSON('json', [self::API_SET_TEST_WHITE_LIST, $params]); } /** * 设置测试白名单(by username). * * @param array $usernames * * @return \EasyWeChat\Support\Collection */ public function setTestWhitelistByUsername($usernames) { $params = [ 'username' => $usernames, ]; return $this->parseJSON('json', [self::API_SET_TEST_WHITE_LIST, $params]); } /** * 获取用户已领取卡券接口. * * @param string $openid * @param string $cardId * * @return \EasyWeChat\Support\Collection */ public function getUserCards($openid, $cardId = '') { $params = [ 'openid' => $openid, 'card_id' => $cardId, ]; return $this->parseJSON('json', [self::API_GET_CARD_LIST, $params]); } /** * 查看卡券详情. * * @param string $cardId * * @return \EasyWeChat\Support\Collection */ public function getCard($cardId) { $params = [ 'card_id' => $cardId, ]; return $this->parseJSON('json', [self::API_GET_CARD, $params]); } /** * 批量查询卡列表. * * @param int $offset * @param int $count * @param string $statusList * * @return \EasyWeChat\Support\Collection */ public function lists($offset = 0, $count = 10, $statusList = 'CARD_STATUS_VERIFY_OK') { $params = [ 'offset' => $offset, 'count' => $count, 'status_list' => $statusList, ]; return $this->parseJSON('json', [self::API_LIST_CARD, $params]); } /** * 更改卡券信息接口 and 设置跟随推荐接口. * * @param string $cardId * @param string $type * @param array $baseInfo * @param array $especial * * @return \EasyWeChat\Support\Collection */ public function update($cardId, $type, $baseInfo = [], $especial = []) { $card = []; $card['card_id'] = $cardId; $card[$type] = []; $cardInfo = []; if ($baseInfo) { $cardInfo['base_info'] = $baseInfo; } $card[$type] = array_merge($cardInfo, $especial); return $this->parseJSON('json', [self::API_UPDATE_CARD, $card]); } /** * 设置微信买单接口. * 设置买单的 card_id 必须已经配置了门店,否则会报错. * * @param string $cardId * @param bool $isOpen * * @return \EasyWeChat\Support\Collection */ public function setPayCell($cardId, $isOpen = true) { $params = [ 'card_id' => $cardId, 'is_open' => $isOpen, ]; return $this->parseJSON('json', [self::API_SET_PAY_CELL, $params]); } /** * 增加库存. * * @param string $cardId * @param int $amount * * @return \EasyWeChat\Support\Collection */ public function increaseStock($cardId, $amount) { return $this->updateStock($cardId, $amount, 'increase'); } /** * 减少库存. * * @param string $cardId * @param int $amount * * @return \EasyWeChat\Support\Collection */ public function reduceStock($cardId, $amount) { return $this->updateStock($cardId, $amount, 'reduce'); } /** * 修改库存接口. * * @param string $cardId * @param int $amount * @param string $action * * @return \EasyWeChat\Support\Collection */ protected function updateStock($cardId, $amount, $action = 'increase') { $key = 'increase' === $action ? 'increase_stock_value' : 'reduce_stock_value'; $params = [ 'card_id' => $cardId, $key => abs($amount), ]; return $this->parseJSON('json', [self::API_MODIFY_STOCK, $params]); } /** * 更改Code接口. * * @param string $code * @param string $newCode * @param array $cardId * * @return \EasyWeChat\Support\Collection */ public function updateCode($code, $newCode, $cardId = []) { $params = [ 'code' => $code, 'new_code' => $newCode, 'card_id' => $cardId, ]; return $this->parseJSON('json', [self::API_UPDATE_CODE, $params]); } /** * 删除卡券接口. * * @param string $cardId * * @return \EasyWeChat\Support\Collection */ public function delete($cardId) { $params = [ 'card_id' => $cardId, ]; return $this->parseJSON('json', [self::API_DELETE_CARD, $params]); } /** * 设置卡券失效. * * @param string $code * @param string $cardId * * @return \EasyWeChat\Support\Collection */ public function disable($code, $cardId = '') { $params = [ 'code' => $code, 'card_id' => $cardId, ]; return $this->parseJSON('json', [self::API_DISABLE_CARD, $params]); } /** * 会员卡接口激活. * * @param array $info * * @return \EasyWeChat\Support\Collection */ public function activate($info = [], $cardType = 'member_card') { if ('general_card' === $cardType) { return $this->parseJSON('json', [self::API_ACTIVATE_GENERAL_CARD, $info]); } return $this->parseJSON('json', [self::API_ACTIVATE_MEMBER_CARD, $info]); } /** * 设置开卡字段接口. * * @param string $cardId * @param array $requiredForm * @param array $optionalForm * * @return \EasyWeChat\Support\Collection */ public function activateUserForm($cardId, array $requiredForm = [], array $optionalForm = []) { $params = array_merge(['card_id' => $cardId], $requiredForm, $optionalForm); return $this->parseJSON('json', [self::API_ACTIVATE_MEMBER_USER_FORM, $params]); } /** * 拉取会员信息接口. * * @param string $cardId * @param string $code * * @return \EasyWeChat\Support\Collection */ public function getMemberCardUser($cardId, $code) { $params = [ 'card_id' => $cardId, 'code' => $code, ]; return $this->parseJSON('json', [self::API_GET_MEMBER_USER_INFO, $params]); } /** * 更新会员信息. * * @param array $params * * @return \EasyWeChat\Support\Collection */ public function updateMemberCardUser(array $params = []) { return $this->parseJSON('json', [self::API_UPDATE_MEMBER_CARD_USER, $params]); } /** * 更新通用员信息. * * @param array $params * * @return \EasyWeChat\Support\Collection */ public function updateGeneralCardUser(array $params = []) { return $this->parseJSON('json', [self::API_UPDATE_GENERAL_CARD_USER, $params]); } /** * 添加子商户. * * @param array $info * * @return \EasyWeChat\Support\Collection */ public function createSubMerchant(array $info = []) { $params = [ 'info' => Arr::only($info, [ 'brand_name', 'logo_url', 'protocol', 'end_time', 'primary_category_id', 'secondary_category_id', 'agreement_media_id', 'operator_media_id', 'app_id', ]), ]; return $this->parseJSON('json', [self::API_CREATE_SUB_MERCHANT, $params]); } /** * 更新子商户. * * @param int $merchantId * @param array $info * * @return \EasyWeChat\Support\Collection */ public function updateSubMerchant($merchantId, array $info = []) { $params = [ 'info' => array_merge(['merchant_id' => $merchantId], Arr::only($info, [ 'brand_name', 'logo_url', 'protocol', 'end_time', 'primary_category_id', 'secondary_category_id', 'agreement_media_id', 'operator_media_id', 'app_id', ])), ]; return $this->parseJSON('json', [self::API_UPDATE_SUB_MERCHANT, $params]); } /** * 获取子商户信息. * * @param int $merchantId * * @return \EasyWeChat\Support\Collection */ public function getSubMerchant($merchantId) { return $this->parseJSON('json', [self::API_GET_SUB_MERCHANT, ['merchant_id' => $merchantId]]); } /** * 批量获取子商户信息. * * @param int $beginId * @param int $limit * @param string $status * * @return \EasyWeChat\Support\Collection */ public function listSubMerchants($beginId = 0, $limit = 50, $status = 'CHECKING') { $params = [ 'begin_id' => $beginId, 'limit' => $limit, 'status' => $status, ]; return $this->parseJSON('json', [self::API_LIST_SUB_MERCHANT, $params]); } /** * 卡券开放类目查询接口. * * @return \EasyWeChat\Support\Collection */ public function getCategories() { return $this->parseJSON('get', [self::API_GET_CATEGORIES]); } /** * Set cache manager. * * @param \Doctrine\Common\Cache\Cache $cache * * @return $this */ public function setCache(Cache $cache) { $this->cache = $cache; return $this; } /** * Return cache manager. * * @return \Doctrine\Common\Cache\Cache */ public function getCache() { return $this->cache ?: $this->cache = new FilesystemCache(sys_get_temp_dir()); } /** * Set Api_ticket cache prifix. * * @param string $prefix * * @return $this */ public function setTicketCachePrefix($prefix) { $this->ticketCachePrefix = $prefix; return $this; } /** * Set Api_ticket cache key. * * @param string $cacheKey * * @return $this */ public function setTicketCacheKey($cacheKey) { $this->ticketCacheKey = $cacheKey; return $this; } /** * Get ApiTicket token cache key. * * @return string */ public function getTicketCacheKey() { if (is_null($this->ticketCacheKey)) { return $this->ticketCachePrefix.$this->getAccessToken()->getAppId(); } return $this->ticketCacheKey; } /** * Set current url. * * @param string $url * * @return Card */ public function setUrl($url) { $this->url = $url; return $this; } }