<?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
{
protected $cache;
protected $ticketCacheKey;
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';
public function getColors()
{
return $this->parseJSON('get', [self::API_GET_COLORS]);
}
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]);
}
public function QRCode(array $cards = [])
{
return $this->parseJSON('json', [self::API_CREATE_QRCODE, $cards]);
}
public function showQRCode($ticket = null)
{
$params = [
'ticket' => $ticket,
];
$http = $this->getHttp();
$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),
];
}
public function getQRCodeUrl($ticket)
{
return self::API_SHOW_QRCODE.'?ticket='.$ticket;
}
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;
}
public function jsConfigForAssign(array $cards)
{
return json_encode(array_map(function ($card) {
return $this->attachExtension($card['card_id'], $card);
}, $cards));
}
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),
];
}
public function getSignature()
{
$params = func_get_args();
sort($params, SORT_STRING);
return sha1(implode($params));
}
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]);
}
public function deposit($cardId, $code)
{
$params = [
'card_id' => $cardId,
'code' => $code,
];
return $this->parseJSON('json', [self::API_DEPOSIT_CODE, $params]);
}
public function getDepositedCount($cardId)
{
$params = [
'card_id' => $cardId,
];
return $this->parseJSON('json', [self::API_GET_DEPOSIT_COUNT, $params]);
}
public function checkCode($cardId, $code)
{
$params = [
'card_id' => $cardId,
'code' => $code,
];
return $this->parseJSON('json', [self::API_CHECK_CODE, $params]);
}
public function getCode($code, $checkConsume, $cardId)
{
$params = [
'code' => $code,
'check_consume' => $checkConsume,
'card_id' => $cardId,
];
return $this->parseJSON('json', [self::API_GET_CODE, $params]);
}
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]);
}
public function decryptCode($encryptedCode)
{
$params = [
'encrypt_code' => $encryptedCode,
];
return $this->parseJSON('json', [self::API_DECRYPT_CODE, $params]);
}
public function getHtml($cardId)
{
$params = [
'card_id' => $cardId,
];
return $this->parseJSON('json', [self::API_GET_HTML, $params]);
}
public function setTestWhitelist($openids)
{
$params = [
'openid' => $openids,
];
return $this->parseJSON('json', [self::API_SET_TEST_WHITE_LIST, $params]);
}
public function setTestWhitelistByUsername($usernames)
{
$params = [
'username' => $usernames,
];
return $this->parseJSON('json', [self::API_SET_TEST_WHITE_LIST, $params]);
}
public function getUserCards($openid, $cardId = '')
{
$params = [
'openid' => $openid,
'card_id' => $cardId,
];
return $this->parseJSON('json', [self::API_GET_CARD_LIST, $params]);
}
public function getCard($cardId)
{
$params = [
'card_id' => $cardId,
];
return $this->parseJSON('json', [self::API_GET_CARD, $params]);
}
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]);
}
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]);
}
public function setPayCell($cardId, $isOpen = true)
{
$params = [
'card_id' => $cardId,
'is_open' => $isOpen,
];
return $this->parseJSON('json', [self::API_SET_PAY_CELL, $params]);
}
public function increaseStock($cardId, $amount)
{
return $this->updateStock($cardId, $amount, 'increase');
}
public function reduceStock($cardId, $amount)
{
return $this->updateStock($cardId, $amount, 'reduce');
}
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]);
}
public function updateCode($code, $newCode, $cardId = [])
{
$params = [
'code' => $code,
'new_code' => $newCode,
'card_id' => $cardId,
];
return $this->parseJSON('json', [self::API_UPDATE_CODE, $params]);
}
public function delete($cardId)
{
$params = [
'card_id' => $cardId,
];
return $this->parseJSON('json', [self::API_DELETE_CARD, $params]);
}
public function disable($code, $cardId = '')
{
$params = [
'code' => $code,
'card_id' => $cardId,
];
return $this->parseJSON('json', [self::API_DISABLE_CARD, $params]);
}
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]);
}
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]);
}
public function getMemberCardUser($cardId, $code)
{
$params = [
'card_id' => $cardId,
'code' => $code,
];
return $this->parseJSON('json', [self::API_GET_MEMBER_USER_INFO, $params]);
}
public function updateMemberCardUser(array $params = [])
{
return $this->parseJSON('json', [self::API_UPDATE_MEMBER_CARD_USER, $params]);
}
public function updateGeneralCardUser(array $params = [])
{
return $this->parseJSON('json', [self::API_UPDATE_GENERAL_CARD_USER, $params]);
}
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]);
}
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]);
}
public function getSubMerchant($merchantId)
{
return $this->parseJSON('json', [self::API_GET_SUB_MERCHANT, ['merchant_id' => $merchantId]]);
}
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]);
}
public function getCategories()
{
return $this->parseJSON('get', [self::API_GET_CATEGORIES]);
}
public function setCache(Cache $cache)
{
$this->cache = $cache;
return $this;
}
public function getCache()
{
return $this->cache ?: $this->cache = new FilesystemCache(sys_get_temp_dir());
}
public function setTicketCachePrefix($prefix)
{
$this->ticketCachePrefix = $prefix;
return $this;
}
public function setTicketCacheKey($cacheKey)
{
$this->ticketCacheKey = $cacheKey;
return $this;
}
public function getTicketCacheKey()
{
if (is_null($this->ticketCacheKey)) {
return $this->ticketCachePrefix.$this->getAccessToken()->getAppId();
}
return $this->ticketCacheKey;
}
public function setUrl($url)
{
$this->url = $url;
return $this;
}
}