#1 {closure} (8 , Undefined index: expired_at , /usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php , 257 , Array(8) )/usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php (257)
<?php
/**
* weixin library
* @author geekzhumail@gmail.com
* @since 2014-09-24
* @edit 2015-03-05
*/
class SDKRuntimeException extends Exception {
public function errorMessage() {
return $this->getMessage();
}
}
class WechatSDK extends WechatBase{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => 'wxdf0a039cf4ea94d1',
'appsecret' => '7100f0fe837a5287f075b6d2d9814ca0',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/';
/**
* @var access_token
*/
protected $accessToken = '';
/**
* 是否从远程服务器拿token
*/
protected $isRemote = false;
public function __construct() {
parent::__construct();
$this->isRemote = $this->config->is_remote;
$this->accessToken = $this->getToken();
}
/**
* 初始化数据
*/
public function init($config) {
$this->config = $config;
}
/**
* 获取Appid
*/
public function getAppid() {
return $this->config['appid'];
}
/**
* weixin signature
*/
public function checkSignature($signature, $timestamp, $nonce) {
$token = $this->config['token'];
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr);
$tmpStr = implode( $tmpArr );
$tmpStr = sha1( $tmpStr );
if( $tmpStr == $signature ){
return true;
}else{
return false;
}
}
/**
* web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function webOauth2($redirectUri, $state = [], $type = 'snsapi_base') {
$params = [];
$params['appid'] = $this->config['appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = $type;
if (empty($state['type'])) {
$state['type'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* pc web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function pcWebOauth2($redirectUri, $state = []) {
$params = [];
$params['appid'] = $this->config['open_web_appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = 'snsapi_login';
if (empty($state['env'])) {
$state['env'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/qrconnect?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* get access token by code
*/
public function getUserTokenByCode($code) {
$params = array();
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$params['code'] = $code;
$params['grant_type'] = 'authorization_code';
$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* refresh access token
* @param string $token 填写通过access_token获取到的refresh_token参数
*/
public function refreshUserToken($token) {
$params = [];
$params['appid'] = $this->config['appid'];
$params['grant_type'] = 'refresh_token';
$params['refresh_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 根据网页授权拉取用户信息(需scope为 snsapi_userinfo)
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function getUserInfoByOauth($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('sns/userinfo', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 验证授权凭证(access_token)是否有效
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function validUserToken($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/auth?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 获取公众号token
*/
public function getToken() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
if ($this->isRemote) {
return $this->getTokenFromRemote();
}
$params = [];
$params['grant_type'] = 'client_credential';
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$url = 'https://api.weixin.qq.com/cgi-bin/token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
// 写入缓存
$expiredAt = $response['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $response['access_token'], $expiredAt);
return $response['access_token'];
}
/**
* 获取access_token from remote
*/
public function getTokenFromRemote() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/token/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$cache->save('wechat_access_token_expired', $resp['expired_at'], $expiredAt);
$expiredAt = 120;
$cache->save($key, $resp['access_token'], $expiredAt);
return $resp['access_token'];
} else {
throw new Exception('wx admin get token error');
}
}
/**
* 获取用户基本信息(包括UnionID机制)
* @param string $openid 用户的唯一标识
* @param string $token 调用接口凭证,默认值为Wechat::getToken()
*/
public function getUserInfo($openid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('cgi-bin/user/info', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 下载已经上传到微信的资源文件
*/
public function downloadMedia($mediaid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['access_token'] = $token;
$params['media_id'] = $mediaid;
$url = 'http://file.api.weixin.qq.com/cgi-bin/media/get?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => false,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$content = curl_exec($ch);
$info = curl_getinfo($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == '200') {
// 正常则分割header和body
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($content, 0, $headerSize);
$body = substr($content, $headerSize);
}
curl_close($ch);
$resp['info'] = $info;
$resp['header'] = $header;
$resp['body'] = $body;
return $resp;
}
/**
* 获取 js api ticket
*/
public function getJsapiTicket()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
if ($this->isRemote) {
return $this->getJsapiTicketFromRemote();
}
$resp = json_decode($this->get('cgi-bin/ticket/getticket', ['access_token' => $this->accessToken, 'type' => 'jsapi']), true);
// 写入缓存
$expiredAt = $resp['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
}
/**
* 获取 js api ticket
*/
public function getJsapiTicketFromRemote()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/jsticket/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$expiredAt = 120;
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
} else {
throw new Exception('wx admin get ticket error');
}
}
/**
* 获取js api 签名
*/
public function getSignPackage($url = '')
{
$jsticket = $this->getJsapiTicket();
if (! $url) {
$url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}
$timestamp = time();
$nonceStr = $this->createNoncestr(16);
// 这里参数的顺序要按照 key 值 ASCII 码升序排序
$string = "jsapi_ticket={$jsticket}&noncestr={$nonceStr}×tamp={$timestamp}&url={$url}";
return [
"appId" => $this->config['appid'],
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => sha1($string),
"rawString" => $string
];
}
/**
* 新建一个类,由于PSR-4无法获取不以文件名命名的类;
*/
public function newClass($name)
{
return new $name();
}
// 微信支付
/**
* 获取微信付款码的prepayid
* @params array $params 参数
*/
public function getPrepayId($params) {
$sdk = new UnifiedOrderSDK;
$sdk->setParameter("trade_type","JSAPI");//交易类型
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$prepayid = $sdk->getPrepayId();
return $prepayid;
}
/**
* 获取支付js api 签名
*/
public function getPaySignPackage($prepayid)
{
$jsApiObj["appId"] = $this->config['appid'];
$timeStamp = time();
$jsApiObj["timeStamp"] = "$timeStamp";
$jsApiObj["nonceStr"] = $this->createNoncestr();
$jsApiObj["package"] = "prepay_id=$prepayid";
$jsApiObj["signType"] = "MD5";
$jsApiObj["paySign"] = $this->getSign($jsApiObj);
$signPackage = $jsApiObj;
return $signPackage;
}
/**
* 发送红包接口
* @params array $params 参数
*/
public function sendRedPack($params) {
$sdk = new RedPackSDK;
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$result = $sdk->send();
return $result;
}
}
/**
* 所有接口的基类
*/
class WechatBase
{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => '',
'appsecret' => '',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/cgi-bin/';
public function __construct() {
$this->di = Phalcon\DI::getDefault();
$this->config = $this->di->getConfig()['wechat'];
}
protected function get($url, $params = '')
{
if (strpos($url, 'https://') === false && strpos($url, 'http://') === false) {
$url = $this->apiPrefix.$url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl get error');
}
}
protected function post($url, $params = '', $data = '')
{
if (strpos($url, 'https://') === false) {
$url = $this->apiPrefix . $url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
if ($data) {
$options[CURLOPT_POSTFIELDS] = json_encode($data, JSON_UNESCAPED_UNICODE);
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl post error');
}
}
public function trimString($value)
{
$ret = null;
if (null != $value)
{
$ret = $value;
if (strlen($ret) == 0)
{
$ret = null;
}
}
return $ret;
}
/**
* 作用:产生随机字符串,不长于32位
*/
public function createNoncestr( $length = 32, $type = 'all')
{
if ($type == 'all') {
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
} else if($type == 'number') {
$chars = "0123456789";
}
$str ="";
for ( $i = 0; $i < $length; $i++ ) {
$str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
/**
* 作用:格式化参数,签名过程需要使用
*/
public function formatBizQueryParaMap($paraMap, $urlencode)
{
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v)
{
if($urlencode)
{
$v = urlencode($v);
}
//$buff .= strtolower($k) . "=" . $v . "&";
$buff .= $k . "=" . $v . "&";
}
$reqPar;
if (strlen($buff) > 0)
{
$reqPar = substr($buff, 0, strlen($buff)-1);
}
return $reqPar;
}
/**
* 作用:生成签名
*/
public function getSign($Obj)
{
foreach ($Obj as $k => $v)
{
$Parameters[$k] = $v;
}
//签名步骤一:按字典序排序参数
ksort($Parameters);
$String = $this->formatBizQueryParaMap($Parameters, false);
//echo '【string1】'.$String.'</br>';
//签名步骤二:在string后加入KEY
$String = $String . "&key=" . $this->config['key'];
//echo "【string2】".$String."</br>";
//签名步骤三:MD5加密
$String = md5($String);
//echo "【string3】 ".$String."</br>";
//签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
//echo "【result】 ".$result_."</br>";
return $result_;
}
/**
* 作用:array转xml
*/
public function arrayToXml($arr)
{
$xml = "<xml>";
foreach ($arr as $key=>$val)
{
if (is_numeric($val))
{
$xml.="<".$key.">".$val."</".$key.">";
}
else
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
$xml.="</xml>";
return $xml;
}
/**
* 作用:将xml转为array
*/
public function xmlToArray($xml)
{
//将XML转为array
$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $array_data;
}
/**
* 作用:以post方式提交xml到对应的接口url
*/
public function postXmlCurl($xml,$url,$second=30)
{
//初始化curl
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
curl_close($ch);
//返回结果
if($data)
{
return $data;
}
else
{
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
return false;
}
}
/**
* 作用:使用证书,以post方式提交xml到对应的接口url
*/
public function postXmlSSLCurl($xml,$url,$second=30)
{
$ch = curl_init();
//超时时间
curl_setopt($ch,CURLOPT_TIMEOUT,$second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch,CURLOPT_HEADER,FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT, $this->config['sslcert_path']);
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY, $this->config['sslkey_path']);
//post提交方式
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
$data = curl_exec($ch);
//返回结果
if($data){
curl_close($ch);
return $data;
}
else {
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
curl_close($ch);
return false;
}
}
}
/**
* 请求型接口的基类
*/
class WechatPayRequestBase extends WechatBase
{
var $parameters;//请求参数,类型为关联数组
public $response;//微信返回的响应
public $result;//返回参数,类型为关联数组
var $url;//接口链接
var $curl_timeout;//curl超时时间
/**
* 作用:设置请求参数
*/
function setParameter($parameter, $parameterValue)
{
$this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 作用:设置标配的请求参数,生成签名,生成接口参数xml
*/
function createXml()
{
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 作用:post请求xml
*/
function postXml()
{
$xml = $this->createXml();
$this->response = $this->postXmlCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:使用证书post请求xml
*/
function postXmlSSL()
{
$xml = $this->createXml();
$this->response = $this->postXmlSSLCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:获取结果,默认不使用证书
*/
function getResult()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
return $this->result;
}
}
/**
* 统一支付接口类
*/
class UnifiedOrderSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
//检测必填参数
if(empty($this->parameters["out_trade_no"]))
{
throw new SDKRuntimeException("缺少统一支付接口必填参数out_trade_no!"."<br>");
}elseif(empty($this->parameters["body"])){
throw new SDKRuntimeException("缺少统一支付接口必填参数body!"."<br>");
}elseif (empty($this->parameters["total_fee"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数total_fee!"."<br>");
}elseif (empty($this->parameters["notify_url"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数notify_url!"."<br>");
}elseif (empty($this->parameters["trade_type"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数trade_type!"."<br>");
}elseif ($this->parameters["trade_type"] == "JSAPI" &&
empty($this->parameters["openid"])){
throw new SDKRuntimeException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!"."<br>");
}
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["spbill_create_ip"] = $this->di->getRequest()->getClientAddress();//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 获取prepay_id
*/
function getPrepayId()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
$prepay_id = $this->result["prepay_id"];
return $prepay_id;
}
}
/**
* 响应型接口基类
*/
class WechatPayServerBase extends WechatBase
{
public $data;//接收到的数据,类型为关联数组
var $returnParameters;//返回参数,类型为关联数组
/**
* 将微信的请求xml转换成关联数组,以方便数据处理
*/
function saveData($xml)
{
$this->data = $this->xmlToArray($xml);
}
function checkSign()
{
$tmpData = $this->data;
unset($tmpData['sign']);
$sign = $this->getSign($tmpData);//本地签名
if (isLocal()) {
echo $sign;
}
if ($this->data['sign'] == $sign) {
return TRUE;
}
return FALSE;
}
/**
* 获取微信的请求数据
*/
function getData()
{
return $this->data;
}
/**
* 设置返回微信的xml数据
*/
function setReturnParameter($parameter, $parameterValue)
{
$this->returnParameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 生成接口参数xml
*/
function createXml()
{
return $this->arrayToXml($this->returnParameters);
}
/**
* 将xml数据返回微信
*/
function returnXml()
{
$returnXml = $this->createXml();
return $returnXml;
}
}
/**
* 红包发送接口类
*/
class RedPackSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
$requiredParams = [
'mch_billno',
'nick_name',
'send_name',
're_openid',
'total_amount',
'min_value',
'max_value',
'total_num',
'wishing',
'act_name',
'remark',
];
//检测必填参数
foreach ($requiredParams as $val) {
if (empty($this->parameters[$val])) {
throw new SDKRuntimeException("缺少红包接口必填参数{$val}!"."<br>");
}
}
$this->parameters["wxappid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["client_ip"] = gethostbyname(gethostname());//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 发送红包
*
* curl 返回例子
* <xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[发放成功.]]></return_msg>
<result_code><![CDATA[SUCCESS]]></result_code>
<err_code><![CDATA[0]]></err_code>
<err_code_des><![CDATA[发放成功.]]></err_code_des>
<mch_billno><![CDATA[1228563102201503261427336509]]></mch_billno>
<mch_id>1228563102</mch_id>
<wxappid><![CDATA[wxdceccb9098a978b8]]></wxappid>
<re_openid><![CDATA[o55e0t1NHPxdcRCjbIu_xnjHc2u8]]></re_openid>
<total_amount>100</total_amount>
</xml>
*/
public function send() {
$this->postXmlSSL();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
return $this->result;
}
#2 WechatSDK ->getTokenFromRemote ()/usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php (209)
<?php
/**
* weixin library
* @author geekzhumail@gmail.com
* @since 2014-09-24
* @edit 2015-03-05
*/
class SDKRuntimeException extends Exception {
public function errorMessage() {
return $this->getMessage();
}
}
class WechatSDK extends WechatBase{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => 'wxdf0a039cf4ea94d1',
'appsecret' => '7100f0fe837a5287f075b6d2d9814ca0',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/';
/**
* @var access_token
*/
protected $accessToken = '';
/**
* 是否从远程服务器拿token
*/
protected $isRemote = false;
public function __construct() {
parent::__construct();
$this->isRemote = $this->config->is_remote;
$this->accessToken = $this->getToken();
}
/**
* 初始化数据
*/
public function init($config) {
$this->config = $config;
}
/**
* 获取Appid
*/
public function getAppid() {
return $this->config['appid'];
}
/**
* weixin signature
*/
public function checkSignature($signature, $timestamp, $nonce) {
$token = $this->config['token'];
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr);
$tmpStr = implode( $tmpArr );
$tmpStr = sha1( $tmpStr );
if( $tmpStr == $signature ){
return true;
}else{
return false;
}
}
/**
* web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function webOauth2($redirectUri, $state = [], $type = 'snsapi_base') {
$params = [];
$params['appid'] = $this->config['appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = $type;
if (empty($state['type'])) {
$state['type'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* pc web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function pcWebOauth2($redirectUri, $state = []) {
$params = [];
$params['appid'] = $this->config['open_web_appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = 'snsapi_login';
if (empty($state['env'])) {
$state['env'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/qrconnect?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* get access token by code
*/
public function getUserTokenByCode($code) {
$params = array();
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$params['code'] = $code;
$params['grant_type'] = 'authorization_code';
$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* refresh access token
* @param string $token 填写通过access_token获取到的refresh_token参数
*/
public function refreshUserToken($token) {
$params = [];
$params['appid'] = $this->config['appid'];
$params['grant_type'] = 'refresh_token';
$params['refresh_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 根据网页授权拉取用户信息(需scope为 snsapi_userinfo)
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function getUserInfoByOauth($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('sns/userinfo', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 验证授权凭证(access_token)是否有效
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function validUserToken($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/auth?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 获取公众号token
*/
public function getToken() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
if ($this->isRemote) {
return $this->getTokenFromRemote();
}
$params = [];
$params['grant_type'] = 'client_credential';
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$url = 'https://api.weixin.qq.com/cgi-bin/token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
// 写入缓存
$expiredAt = $response['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $response['access_token'], $expiredAt);
return $response['access_token'];
}
/**
* 获取access_token from remote
*/
public function getTokenFromRemote() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/token/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$cache->save('wechat_access_token_expired', $resp['expired_at'], $expiredAt);
$expiredAt = 120;
$cache->save($key, $resp['access_token'], $expiredAt);
return $resp['access_token'];
} else {
throw new Exception('wx admin get token error');
}
}
/**
* 获取用户基本信息(包括UnionID机制)
* @param string $openid 用户的唯一标识
* @param string $token 调用接口凭证,默认值为Wechat::getToken()
*/
public function getUserInfo($openid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('cgi-bin/user/info', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 下载已经上传到微信的资源文件
*/
public function downloadMedia($mediaid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['access_token'] = $token;
$params['media_id'] = $mediaid;
$url = 'http://file.api.weixin.qq.com/cgi-bin/media/get?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => false,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$content = curl_exec($ch);
$info = curl_getinfo($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == '200') {
// 正常则分割header和body
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($content, 0, $headerSize);
$body = substr($content, $headerSize);
}
curl_close($ch);
$resp['info'] = $info;
$resp['header'] = $header;
$resp['body'] = $body;
return $resp;
}
/**
* 获取 js api ticket
*/
public function getJsapiTicket()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
if ($this->isRemote) {
return $this->getJsapiTicketFromRemote();
}
$resp = json_decode($this->get('cgi-bin/ticket/getticket', ['access_token' => $this->accessToken, 'type' => 'jsapi']), true);
// 写入缓存
$expiredAt = $resp['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
}
/**
* 获取 js api ticket
*/
public function getJsapiTicketFromRemote()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/jsticket/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$expiredAt = 120;
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
} else {
throw new Exception('wx admin get ticket error');
}
}
/**
* 获取js api 签名
*/
public function getSignPackage($url = '')
{
$jsticket = $this->getJsapiTicket();
if (! $url) {
$url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}
$timestamp = time();
$nonceStr = $this->createNoncestr(16);
// 这里参数的顺序要按照 key 值 ASCII 码升序排序
$string = "jsapi_ticket={$jsticket}&noncestr={$nonceStr}×tamp={$timestamp}&url={$url}";
return [
"appId" => $this->config['appid'],
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => sha1($string),
"rawString" => $string
];
}
/**
* 新建一个类,由于PSR-4无法获取不以文件名命名的类;
*/
public function newClass($name)
{
return new $name();
}
// 微信支付
/**
* 获取微信付款码的prepayid
* @params array $params 参数
*/
public function getPrepayId($params) {
$sdk = new UnifiedOrderSDK;
$sdk->setParameter("trade_type","JSAPI");//交易类型
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$prepayid = $sdk->getPrepayId();
return $prepayid;
}
/**
* 获取支付js api 签名
*/
public function getPaySignPackage($prepayid)
{
$jsApiObj["appId"] = $this->config['appid'];
$timeStamp = time();
$jsApiObj["timeStamp"] = "$timeStamp";
$jsApiObj["nonceStr"] = $this->createNoncestr();
$jsApiObj["package"] = "prepay_id=$prepayid";
$jsApiObj["signType"] = "MD5";
$jsApiObj["paySign"] = $this->getSign($jsApiObj);
$signPackage = $jsApiObj;
return $signPackage;
}
/**
* 发送红包接口
* @params array $params 参数
*/
public function sendRedPack($params) {
$sdk = new RedPackSDK;
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$result = $sdk->send();
return $result;
}
}
/**
* 所有接口的基类
*/
class WechatBase
{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => '',
'appsecret' => '',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/cgi-bin/';
public function __construct() {
$this->di = Phalcon\DI::getDefault();
$this->config = $this->di->getConfig()['wechat'];
}
protected function get($url, $params = '')
{
if (strpos($url, 'https://') === false && strpos($url, 'http://') === false) {
$url = $this->apiPrefix.$url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl get error');
}
}
protected function post($url, $params = '', $data = '')
{
if (strpos($url, 'https://') === false) {
$url = $this->apiPrefix . $url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
if ($data) {
$options[CURLOPT_POSTFIELDS] = json_encode($data, JSON_UNESCAPED_UNICODE);
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl post error');
}
}
public function trimString($value)
{
$ret = null;
if (null != $value)
{
$ret = $value;
if (strlen($ret) == 0)
{
$ret = null;
}
}
return $ret;
}
/**
* 作用:产生随机字符串,不长于32位
*/
public function createNoncestr( $length = 32, $type = 'all')
{
if ($type == 'all') {
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
} else if($type == 'number') {
$chars = "0123456789";
}
$str ="";
for ( $i = 0; $i < $length; $i++ ) {
$str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
/**
* 作用:格式化参数,签名过程需要使用
*/
public function formatBizQueryParaMap($paraMap, $urlencode)
{
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v)
{
if($urlencode)
{
$v = urlencode($v);
}
//$buff .= strtolower($k) . "=" . $v . "&";
$buff .= $k . "=" . $v . "&";
}
$reqPar;
if (strlen($buff) > 0)
{
$reqPar = substr($buff, 0, strlen($buff)-1);
}
return $reqPar;
}
/**
* 作用:生成签名
*/
public function getSign($Obj)
{
foreach ($Obj as $k => $v)
{
$Parameters[$k] = $v;
}
//签名步骤一:按字典序排序参数
ksort($Parameters);
$String = $this->formatBizQueryParaMap($Parameters, false);
//echo '【string1】'.$String.'</br>';
//签名步骤二:在string后加入KEY
$String = $String . "&key=" . $this->config['key'];
//echo "【string2】".$String."</br>";
//签名步骤三:MD5加密
$String = md5($String);
//echo "【string3】 ".$String."</br>";
//签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
//echo "【result】 ".$result_."</br>";
return $result_;
}
/**
* 作用:array转xml
*/
public function arrayToXml($arr)
{
$xml = "<xml>";
foreach ($arr as $key=>$val)
{
if (is_numeric($val))
{
$xml.="<".$key.">".$val."</".$key.">";
}
else
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
$xml.="</xml>";
return $xml;
}
/**
* 作用:将xml转为array
*/
public function xmlToArray($xml)
{
//将XML转为array
$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $array_data;
}
/**
* 作用:以post方式提交xml到对应的接口url
*/
public function postXmlCurl($xml,$url,$second=30)
{
//初始化curl
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
curl_close($ch);
//返回结果
if($data)
{
return $data;
}
else
{
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
return false;
}
}
/**
* 作用:使用证书,以post方式提交xml到对应的接口url
*/
public function postXmlSSLCurl($xml,$url,$second=30)
{
$ch = curl_init();
//超时时间
curl_setopt($ch,CURLOPT_TIMEOUT,$second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch,CURLOPT_HEADER,FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT, $this->config['sslcert_path']);
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY, $this->config['sslkey_path']);
//post提交方式
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
$data = curl_exec($ch);
//返回结果
if($data){
curl_close($ch);
return $data;
}
else {
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
curl_close($ch);
return false;
}
}
}
/**
* 请求型接口的基类
*/
class WechatPayRequestBase extends WechatBase
{
var $parameters;//请求参数,类型为关联数组
public $response;//微信返回的响应
public $result;//返回参数,类型为关联数组
var $url;//接口链接
var $curl_timeout;//curl超时时间
/**
* 作用:设置请求参数
*/
function setParameter($parameter, $parameterValue)
{
$this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 作用:设置标配的请求参数,生成签名,生成接口参数xml
*/
function createXml()
{
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 作用:post请求xml
*/
function postXml()
{
$xml = $this->createXml();
$this->response = $this->postXmlCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:使用证书post请求xml
*/
function postXmlSSL()
{
$xml = $this->createXml();
$this->response = $this->postXmlSSLCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:获取结果,默认不使用证书
*/
function getResult()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
return $this->result;
}
}
/**
* 统一支付接口类
*/
class UnifiedOrderSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
//检测必填参数
if(empty($this->parameters["out_trade_no"]))
{
throw new SDKRuntimeException("缺少统一支付接口必填参数out_trade_no!"."<br>");
}elseif(empty($this->parameters["body"])){
throw new SDKRuntimeException("缺少统一支付接口必填参数body!"."<br>");
}elseif (empty($this->parameters["total_fee"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数total_fee!"."<br>");
}elseif (empty($this->parameters["notify_url"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数notify_url!"."<br>");
}elseif (empty($this->parameters["trade_type"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数trade_type!"."<br>");
}elseif ($this->parameters["trade_type"] == "JSAPI" &&
empty($this->parameters["openid"])){
throw new SDKRuntimeException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!"."<br>");
}
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["spbill_create_ip"] = $this->di->getRequest()->getClientAddress();//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 获取prepay_id
*/
function getPrepayId()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
$prepay_id = $this->result["prepay_id"];
return $prepay_id;
}
}
/**
* 响应型接口基类
*/
class WechatPayServerBase extends WechatBase
{
public $data;//接收到的数据,类型为关联数组
var $returnParameters;//返回参数,类型为关联数组
/**
* 将微信的请求xml转换成关联数组,以方便数据处理
*/
function saveData($xml)
{
$this->data = $this->xmlToArray($xml);
}
function checkSign()
{
$tmpData = $this->data;
unset($tmpData['sign']);
$sign = $this->getSign($tmpData);//本地签名
if (isLocal()) {
echo $sign;
}
if ($this->data['sign'] == $sign) {
return TRUE;
}
return FALSE;
}
/**
* 获取微信的请求数据
*/
function getData()
{
return $this->data;
}
/**
* 设置返回微信的xml数据
*/
function setReturnParameter($parameter, $parameterValue)
{
$this->returnParameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 生成接口参数xml
*/
function createXml()
{
return $this->arrayToXml($this->returnParameters);
}
/**
* 将xml数据返回微信
*/
function returnXml()
{
$returnXml = $this->createXml();
return $returnXml;
}
}
/**
* 红包发送接口类
*/
class RedPackSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
$requiredParams = [
'mch_billno',
'nick_name',
'send_name',
're_openid',
'total_amount',
'min_value',
'max_value',
'total_num',
'wishing',
'act_name',
'remark',
];
//检测必填参数
foreach ($requiredParams as $val) {
if (empty($this->parameters[$val])) {
throw new SDKRuntimeException("缺少红包接口必填参数{$val}!"."<br>");
}
}
$this->parameters["wxappid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["client_ip"] = gethostbyname(gethostname());//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 发送红包
*
* curl 返回例子
* <xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[发放成功.]]></return_msg>
<result_code><![CDATA[SUCCESS]]></result_code>
<err_code><![CDATA[0]]></err_code>
<err_code_des><![CDATA[发放成功.]]></err_code_des>
<mch_billno><![CDATA[1228563102201503261427336509]]></mch_billno>
<mch_id>1228563102</mch_id>
<wxappid><![CDATA[wxdceccb9098a978b8]]></wxappid>
<re_openid><![CDATA[o55e0t1NHPxdcRCjbIu_xnjHc2u8]]></re_openid>
<total_amount>100</total_amount>
</xml>
*/
public function send() {
$this->postXmlSSL();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
return $this->result;
}
#3 WechatSDK ->getToken ()/usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php (45)
<?php
/**
* weixin library
* @author geekzhumail@gmail.com
* @since 2014-09-24
* @edit 2015-03-05
*/
class SDKRuntimeException extends Exception {
public function errorMessage() {
return $this->getMessage();
}
}
class WechatSDK extends WechatBase{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => 'wxdf0a039cf4ea94d1',
'appsecret' => '7100f0fe837a5287f075b6d2d9814ca0',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/';
/**
* @var access_token
*/
protected $accessToken = '';
/**
* 是否从远程服务器拿token
*/
protected $isRemote = false;
public function __construct() {
parent::__construct();
$this->isRemote = $this->config->is_remote;
$this->accessToken = $this->getToken();
}
/**
* 初始化数据
*/
public function init($config) {
$this->config = $config;
}
/**
* 获取Appid
*/
public function getAppid() {
return $this->config['appid'];
}
/**
* weixin signature
*/
public function checkSignature($signature, $timestamp, $nonce) {
$token = $this->config['token'];
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr);
$tmpStr = implode( $tmpArr );
$tmpStr = sha1( $tmpStr );
if( $tmpStr == $signature ){
return true;
}else{
return false;
}
}
/**
* web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function webOauth2($redirectUri, $state = [], $type = 'snsapi_base') {
$params = [];
$params['appid'] = $this->config['appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = $type;
if (empty($state['type'])) {
$state['type'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* pc web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function pcWebOauth2($redirectUri, $state = []) {
$params = [];
$params['appid'] = $this->config['open_web_appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = 'snsapi_login';
if (empty($state['env'])) {
$state['env'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/qrconnect?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* get access token by code
*/
public function getUserTokenByCode($code) {
$params = array();
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$params['code'] = $code;
$params['grant_type'] = 'authorization_code';
$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* refresh access token
* @param string $token 填写通过access_token获取到的refresh_token参数
*/
public function refreshUserToken($token) {
$params = [];
$params['appid'] = $this->config['appid'];
$params['grant_type'] = 'refresh_token';
$params['refresh_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 根据网页授权拉取用户信息(需scope为 snsapi_userinfo)
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function getUserInfoByOauth($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('sns/userinfo', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 验证授权凭证(access_token)是否有效
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function validUserToken($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/auth?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 获取公众号token
*/
public function getToken() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
if ($this->isRemote) {
return $this->getTokenFromRemote();
}
$params = [];
$params['grant_type'] = 'client_credential';
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$url = 'https://api.weixin.qq.com/cgi-bin/token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
// 写入缓存
$expiredAt = $response['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $response['access_token'], $expiredAt);
return $response['access_token'];
}
/**
* 获取access_token from remote
*/
public function getTokenFromRemote() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/token/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$cache->save('wechat_access_token_expired', $resp['expired_at'], $expiredAt);
$expiredAt = 120;
$cache->save($key, $resp['access_token'], $expiredAt);
return $resp['access_token'];
} else {
throw new Exception('wx admin get token error');
}
}
/**
* 获取用户基本信息(包括UnionID机制)
* @param string $openid 用户的唯一标识
* @param string $token 调用接口凭证,默认值为Wechat::getToken()
*/
public function getUserInfo($openid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('cgi-bin/user/info', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 下载已经上传到微信的资源文件
*/
public function downloadMedia($mediaid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['access_token'] = $token;
$params['media_id'] = $mediaid;
$url = 'http://file.api.weixin.qq.com/cgi-bin/media/get?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => false,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$content = curl_exec($ch);
$info = curl_getinfo($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == '200') {
// 正常则分割header和body
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($content, 0, $headerSize);
$body = substr($content, $headerSize);
}
curl_close($ch);
$resp['info'] = $info;
$resp['header'] = $header;
$resp['body'] = $body;
return $resp;
}
/**
* 获取 js api ticket
*/
public function getJsapiTicket()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
if ($this->isRemote) {
return $this->getJsapiTicketFromRemote();
}
$resp = json_decode($this->get('cgi-bin/ticket/getticket', ['access_token' => $this->accessToken, 'type' => 'jsapi']), true);
// 写入缓存
$expiredAt = $resp['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
}
/**
* 获取 js api ticket
*/
public function getJsapiTicketFromRemote()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/jsticket/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$expiredAt = 120;
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
} else {
throw new Exception('wx admin get ticket error');
}
}
/**
* 获取js api 签名
*/
public function getSignPackage($url = '')
{
$jsticket = $this->getJsapiTicket();
if (! $url) {
$url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}
$timestamp = time();
$nonceStr = $this->createNoncestr(16);
// 这里参数的顺序要按照 key 值 ASCII 码升序排序
$string = "jsapi_ticket={$jsticket}&noncestr={$nonceStr}×tamp={$timestamp}&url={$url}";
return [
"appId" => $this->config['appid'],
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => sha1($string),
"rawString" => $string
];
}
/**
* 新建一个类,由于PSR-4无法获取不以文件名命名的类;
*/
public function newClass($name)
{
return new $name();
}
// 微信支付
/**
* 获取微信付款码的prepayid
* @params array $params 参数
*/
public function getPrepayId($params) {
$sdk = new UnifiedOrderSDK;
$sdk->setParameter("trade_type","JSAPI");//交易类型
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$prepayid = $sdk->getPrepayId();
return $prepayid;
}
/**
* 获取支付js api 签名
*/
public function getPaySignPackage($prepayid)
{
$jsApiObj["appId"] = $this->config['appid'];
$timeStamp = time();
$jsApiObj["timeStamp"] = "$timeStamp";
$jsApiObj["nonceStr"] = $this->createNoncestr();
$jsApiObj["package"] = "prepay_id=$prepayid";
$jsApiObj["signType"] = "MD5";
$jsApiObj["paySign"] = $this->getSign($jsApiObj);
$signPackage = $jsApiObj;
return $signPackage;
}
/**
* 发送红包接口
* @params array $params 参数
*/
public function sendRedPack($params) {
$sdk = new RedPackSDK;
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$result = $sdk->send();
return $result;
}
}
/**
* 所有接口的基类
*/
class WechatBase
{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => '',
'appsecret' => '',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/cgi-bin/';
public function __construct() {
$this->di = Phalcon\DI::getDefault();
$this->config = $this->di->getConfig()['wechat'];
}
protected function get($url, $params = '')
{
if (strpos($url, 'https://') === false && strpos($url, 'http://') === false) {
$url = $this->apiPrefix.$url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl get error');
}
}
protected function post($url, $params = '', $data = '')
{
if (strpos($url, 'https://') === false) {
$url = $this->apiPrefix . $url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
if ($data) {
$options[CURLOPT_POSTFIELDS] = json_encode($data, JSON_UNESCAPED_UNICODE);
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl post error');
}
}
public function trimString($value)
{
$ret = null;
if (null != $value)
{
$ret = $value;
if (strlen($ret) == 0)
{
$ret = null;
}
}
return $ret;
}
/**
* 作用:产生随机字符串,不长于32位
*/
public function createNoncestr( $length = 32, $type = 'all')
{
if ($type == 'all') {
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
} else if($type == 'number') {
$chars = "0123456789";
}
$str ="";
for ( $i = 0; $i < $length; $i++ ) {
$str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
/**
* 作用:格式化参数,签名过程需要使用
*/
public function formatBizQueryParaMap($paraMap, $urlencode)
{
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v)
{
if($urlencode)
{
$v = urlencode($v);
}
//$buff .= strtolower($k) . "=" . $v . "&";
$buff .= $k . "=" . $v . "&";
}
$reqPar;
if (strlen($buff) > 0)
{
$reqPar = substr($buff, 0, strlen($buff)-1);
}
return $reqPar;
}
/**
* 作用:生成签名
*/
public function getSign($Obj)
{
foreach ($Obj as $k => $v)
{
$Parameters[$k] = $v;
}
//签名步骤一:按字典序排序参数
ksort($Parameters);
$String = $this->formatBizQueryParaMap($Parameters, false);
//echo '【string1】'.$String.'</br>';
//签名步骤二:在string后加入KEY
$String = $String . "&key=" . $this->config['key'];
//echo "【string2】".$String."</br>";
//签名步骤三:MD5加密
$String = md5($String);
//echo "【string3】 ".$String."</br>";
//签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
//echo "【result】 ".$result_."</br>";
return $result_;
}
/**
* 作用:array转xml
*/
public function arrayToXml($arr)
{
$xml = "<xml>";
foreach ($arr as $key=>$val)
{
if (is_numeric($val))
{
$xml.="<".$key.">".$val."</".$key.">";
}
else
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
$xml.="</xml>";
return $xml;
}
/**
* 作用:将xml转为array
*/
public function xmlToArray($xml)
{
//将XML转为array
$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $array_data;
}
/**
* 作用:以post方式提交xml到对应的接口url
*/
public function postXmlCurl($xml,$url,$second=30)
{
//初始化curl
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
curl_close($ch);
//返回结果
if($data)
{
return $data;
}
else
{
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
return false;
}
}
/**
* 作用:使用证书,以post方式提交xml到对应的接口url
*/
public function postXmlSSLCurl($xml,$url,$second=30)
{
$ch = curl_init();
//超时时间
curl_setopt($ch,CURLOPT_TIMEOUT,$second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch,CURLOPT_HEADER,FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT, $this->config['sslcert_path']);
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY, $this->config['sslkey_path']);
//post提交方式
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
$data = curl_exec($ch);
//返回结果
if($data){
curl_close($ch);
return $data;
}
else {
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
curl_close($ch);
return false;
}
}
}
/**
* 请求型接口的基类
*/
class WechatPayRequestBase extends WechatBase
{
var $parameters;//请求参数,类型为关联数组
public $response;//微信返回的响应
public $result;//返回参数,类型为关联数组
var $url;//接口链接
var $curl_timeout;//curl超时时间
/**
* 作用:设置请求参数
*/
function setParameter($parameter, $parameterValue)
{
$this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 作用:设置标配的请求参数,生成签名,生成接口参数xml
*/
function createXml()
{
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 作用:post请求xml
*/
function postXml()
{
$xml = $this->createXml();
$this->response = $this->postXmlCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:使用证书post请求xml
*/
function postXmlSSL()
{
$xml = $this->createXml();
$this->response = $this->postXmlSSLCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:获取结果,默认不使用证书
*/
function getResult()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
return $this->result;
}
}
/**
* 统一支付接口类
*/
class UnifiedOrderSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
//检测必填参数
if(empty($this->parameters["out_trade_no"]))
{
throw new SDKRuntimeException("缺少统一支付接口必填参数out_trade_no!"."<br>");
}elseif(empty($this->parameters["body"])){
throw new SDKRuntimeException("缺少统一支付接口必填参数body!"."<br>");
}elseif (empty($this->parameters["total_fee"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数total_fee!"."<br>");
}elseif (empty($this->parameters["notify_url"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数notify_url!"."<br>");
}elseif (empty($this->parameters["trade_type"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数trade_type!"."<br>");
}elseif ($this->parameters["trade_type"] == "JSAPI" &&
empty($this->parameters["openid"])){
throw new SDKRuntimeException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!"."<br>");
}
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["spbill_create_ip"] = $this->di->getRequest()->getClientAddress();//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 获取prepay_id
*/
function getPrepayId()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
$prepay_id = $this->result["prepay_id"];
return $prepay_id;
}
}
/**
* 响应型接口基类
*/
class WechatPayServerBase extends WechatBase
{
public $data;//接收到的数据,类型为关联数组
var $returnParameters;//返回参数,类型为关联数组
/**
* 将微信的请求xml转换成关联数组,以方便数据处理
*/
function saveData($xml)
{
$this->data = $this->xmlToArray($xml);
}
function checkSign()
{
$tmpData = $this->data;
unset($tmpData['sign']);
$sign = $this->getSign($tmpData);//本地签名
if (isLocal()) {
echo $sign;
}
if ($this->data['sign'] == $sign) {
return TRUE;
}
return FALSE;
}
/**
* 获取微信的请求数据
*/
function getData()
{
return $this->data;
}
/**
* 设置返回微信的xml数据
*/
function setReturnParameter($parameter, $parameterValue)
{
$this->returnParameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 生成接口参数xml
*/
function createXml()
{
return $this->arrayToXml($this->returnParameters);
}
/**
* 将xml数据返回微信
*/
function returnXml()
{
$returnXml = $this->createXml();
return $returnXml;
}
}
/**
* 红包发送接口类
*/
class RedPackSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
$requiredParams = [
'mch_billno',
'nick_name',
'send_name',
're_openid',
'total_amount',
'min_value',
'max_value',
'total_num',
'wishing',
'act_name',
'remark',
];
//检测必填参数
foreach ($requiredParams as $val) {
if (empty($this->parameters[$val])) {
throw new SDKRuntimeException("缺少红包接口必填参数{$val}!"."<br>");
}
}
$this->parameters["wxappid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["client_ip"] = gethostbyname(gethostname());//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 发送红包
*
* curl 返回例子
* <xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[发放成功.]]></return_msg>
<result_code><![CDATA[SUCCESS]]></result_code>
<err_code><![CDATA[0]]></err_code>
<err_code_des><![CDATA[发放成功.]]></err_code_des>
<mch_billno><![CDATA[1228563102201503261427336509]]></mch_billno>
<mch_id>1228563102</mch_id>
<wxappid><![CDATA[wxdceccb9098a978b8]]></wxappid>
<re_openid><![CDATA[o55e0t1NHPxdcRCjbIu_xnjHc2u8]]></re_openid>
<total_amount>100</total_amount>
</xml>
*/
public function send() {
$this->postXmlSSL();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
return $this->result;
}
#4 WechatSDK ->__construct ()/usr/local/www/liowang-shop-robotwar/shop/app/controllers/PageUserController.php (24)
<?php
use HomeSpace\Model;
class PageUserController extends PageBaseController {
/**
* 登录
*/
public function loginAction()
{
$data = [];
//echo '登录';
$act = $this->request->getQuery('act');
if ($act == 'not_active') {
if (empty($_SESSION['tmp_email'])) {
$act = '';
}
$data['email'] = $_SESSION['tmp_email'];
}
$data['act'] = $act;
$data['errmsg'] = $this->flash->getMessages('error');
$this->flash->clear();
$wechat = new WechatSDK;
//$url = $this->url->get('wechat/oauth2/callback');
$url = 'http://www.liowang.com/shop/wechat/oauth2/callback';
$state = ['env' => 'product'];
if (isLocal()) {
$state = ['env' => 'local'];
}
$data['wechat_login_url'] = $wechat->pcWebOauth2($url, $state);
echo $this->viewoutput('pc/login', $data);
}
/**
* 登录 POST
*/
public function postLoginAction()
{
global $user, $_LANG;
$errmsg = '';
$account = $this->request->getPost('account');
$password = $this->request->getPost('password');
$remember = ! empty($this->request->getQuery('remember'));
if (filter_var($account, FILTER_VALIDATE_EMAIL)) {
// 邮箱登录
$muser = Model\User::findFirst(['columns' => 'user_name', 'conditions' => "email = '$account'"]);
} else if(is_numeric($account) && strlen($account) == 11) {
// 手机登录
$muser = Model\User::findFirst(['columns' => 'user_name', 'conditions' => "mobile_phone = '$account'"]);
}
if (! empty($muser) && ! empty($password)) {
if ($user->login($muser['user_name'], $password, $remember)) {
$row = Model\User::findFirstByUserId($_SESSION['user_id']);
if ($row['is_validated'] == 1) {
update_user_info();
recalculate_price();
$this->auth->loginUsingId($_SESSION['user_id']);
$_SESSION['pc_login'] = true;
return $this->response->redirect('home/index');
} else {
//$_SESSION['login_fail'] ++ ;
//$errmsg = '该账号尚未激活请查阅该账号邮箱的激活邮件,或者联系客服';
$_SESSION['tmp_email'] = $row['email'];
return $this->response->redirect('login?act=not_active');
}
} else {
$_SESSION['login_fail'] ++ ;
$errmsg = $_LANG['login_failure'];
}
}
$errmsg = $errmsg ?: '账号或者密码错误';
$this->flash->error($errmsg);
return $this->response->redirect('login');
}
/**
* 退出登录
*/
public function logoutAction()
{
global $user;
$user->logout();
return $this->response->redirect($this->request->getHTTPReferer(), true);
}
/**
* 邮箱注册验证
*/
public function emailValidateAction()
{
include_once(ROOT_PATH . 'includes/lib_passport.php');
$data = [];
$data['status'] = 'user_not_exist';
$hash = $this->request->getQuery('hash');
if ($hash) {
$id = intval(register_hash('decode', $hash));
$user = Model\User::findFirstByUserId($id);
if ($user) {
if ($user['is_validated'] == 0) {
Model\User::update(['is_validated' => 1], ['user_id' => $id]);
$data['status'] = 'success';
} else {
$data['status'] = 'validated';
}
$data['user'] = $user;
}
}
echo $this->viewoutput('pc/email-validate', $data);
}
/**
* PC 微信回调 oauth2 函数
*/
public function wechatOauth2CallbackAction()
{
global $ecs, $db, $user;
include_once(ROOT_PATH . 'includes/lib_passport.php');
$code = $this->request->getQuery('code');
if (empty($code)) {
echo '授权失败';
exit;
}
$state = $this->request->getQuery('state');
parse_str($state, $params);
if (! islocal() && ! empty($params['env']) && $params['env'] == 'local') {
return $this->response->redirect('http://liowang.dev.seafarer.me/shop/wechat/oauth2/callback?' . http_build_query($this->request->getQuery()));
}
$wechat = new WechatSDK;
$config = $this->config->wechat;
$config->appid = $config->open_web_appid;
$config->appsecret = $config->open_web_secret;
$wechat->init($config);
$response = $wechat->getUserTokenByCode($code);
if (empty($response['errcode'])) {
// 授权成功
$ewxpcuser = Model\WxPcUser::findFirstByOpenid($response['openid']);
$data = [];
$isFirst = false;
$isNeedCreate = false;
$uuser = [];
$wxpcuser = [];
if ($ewxpcuser) {
// 用户已经存在
$euser = Model\User::findFirstByWxPcUserid($ewxpcuser['id']);
} else {
// 第一次访问
$isFirst = true;
$wxuser = Model\WxUser::findFirstByUnionid($response['unionid']);
if (empty($wxuser)) {
$isNeedCreate = true;
} else {
$euser = Model\User::findFirstByWxUserid($wxuser['id']);
}
}
$wxpcuser['openid'] = $response['openid'];
$wxpcuser['unionid'] = $response['unionid'];
$wxpcuser['access_token'] = $response['access_token'];
$wxpcuser['refresh_token'] = $response['refresh_token'];
$wxpcuser['expired_at'] = $response['expires_in'] + time() - 200;
$wxpcuser['scope'] = $response['scope'];
$wxpcuser['ip'] = $this->request->getClientAddress();
$info = $wechat->getUserInfoByOauth($response['openid'], $response['access_token']);
$wxpcuser['nickname'] = $info['nickname'];
$wxpcuser['gender'] = $info['sex'];
$wxpcuser['language'] = $info['language'];
$wxpcuser['city'] = $info['city'];
$wxpcuser['country'] = $info['country'];
$wxpcuser['province'] = $info['province'];
$wxpcuser['headimgurl'] = $info['headimgurl'];
if ($isFirst) {
// 首次PC扫码登录
$id = Model\WxPcUser::insert($wxpcuser);
$_SESSION['wx_pc_user_id'] = $id;
$uuser['wx_pc_userid'] = $id;
if (! $isNeedCreate) {
Model\User::update($uuser, "user_id = {$euser['user_id']}");
}
/**
if ($isNeedCreate) {
// 创建新用户
$uuser['avatar'] = 'shop/assets/pc/IMGS/defaultPhoto.png';
$name = '会员_' . time() . '_' . $id;
$uuser['nickname'] = $wxpcuser['nickname'];
$email = 'email_' . time() . '_' . $id . '@liowang.com';
if (register($name, '123123', $email) !== false) {;
$db->autoExecute($ecs->table('users'), $uuser, 'UPDATE', "user_id = {$_SESSION['user_id']}");
} else {
SystemException::error('授权失败');
}
} else {
// 绑定已在微信授权的用户
Model\User::update($uuser, "user_id = {$euser['user_id']}");
Model\WxPcUser::update($wxpcuser, "openid = '{$response['openid']}'");
$user->set_session($euser['user_name']);
$user->set_cookie($euser['user_name']);
update_user_info();
recalculate_price();
}**/
} else {
Model\WxPcUser::update($wxpcuser, "openid = '{$response['openid']}'");
$_SESSION['wx_pc_user_id'] = $ewxpcuser['id'];
}
if (empty($euser)) {
return $this->response->redirect('wechat/oauth2/register');
}
// 已注册直接登录
$user->set_session($euser['user_name']);
$user->set_cookie($euser['user_name']);
update_user_info();
recalculate_price();
$this->auth->loginUsingId($_SESSION['user_id']);
$_SESSION['pc_login'] = true;
} else {
SystemException::error($response['errmsg']);
}
$referer = $this->url->get('');
return $this->response->redirect($referer, true);
}
/**
* PC 微信账号登录绑定
*/
public function wechatOauth2RegisterAction()
{
global $ecs, $db, $user;
if (empty($_SESSION['wx_pc_user_id'])) {
return $this->response->redirect('login');
}
$data = [];
echo $this->viewoutput('pc/wechat-oauth2-register', $data);
}
/******** 需要登录授权 *********/
/**
* 个人信息
*/
public function infoAction()
{
$user = $this->auth->user();
list($user['birYear'], $user['birMonth'], $user['birDay']) = explode('-', $user['birthday']);
$user['birMonth'] = ltrim($user['birMonth'], 0);
$user['birDay'] = ltrim($user['birDay'], 0);
if (strpos($user['avatar'], 'http://') === false && strpos($user['avatar'], 'https://') === false) {
$user['avatar'] = 'http://' . $this->request->getHttpHost() . '/' . $user['avatar'];
}
$data = [];
$data['user'] = $user;
$data['navName'] = '';
$data['userSidebar'] = 'user-info';
echo $this->viewoutput('pc/user-info', $data);
}
/**
* 购物车
*/
public function cartAction()
{
require(ROOT_PATH . 'includes/lib_order.php');
$user = $this->auth->user();
$mcart = new Model\Cart;
$cartCount = $mcart->getCount();
$cartGoods = get_cart_goods();
$discount = compute_discount();
$goodsids = [];
foreach ($cartGoods['goods_list'] as $goods) {
$goodsids[] = $goods['goods_id'];
}
if ($goodsids) {
$collectGoodsList = Model\CollectGoods::find(['conditions' => "user_id='$_SESSION[user_id]' AND " . db_create_in($goodsids, 'goods_id')]);
foreach ($cartGoods['goods_list'] as &$goods) {
$goods['is_collect'] = false;
foreach ($collectGoodsList as $row) {
if ($row['goods_id'] == $goods['goods_id']) {
$goods['is_collect'] = true;
break;
}
}
}
unset($goods);
}
$data = [];
$data['user'] = $user;
$data['cartGoods'] = $cartGoods;
$data['navName'] = '';
echo $this->viewoutput('pc/user-cart', $data);
}
/**
* 购物车结算
*/
public function cartSettleAction()
{
global $db, $ecs, $_CFG, $_LANG, $err;
require(ROOT_PATH . 'includes/lib_order.php');
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'languages/' .$_CFG['lang']. '/shopping_flow.php');
$user = $this->auth->user();
$recids = $this->request->getQuery('rec_ids') ?: [];
$cartGoods = get_cart_goods($recids);
if (empty($cartGoods['goods_list'])) {
// 购物车为空
return $this->response->redirect('user/cart');
}
$discount = compute_discount();
$data = [];
$address_id = intval($this->request->getQuery('address_id'));
if ($address_id == 0) {
$address_id = $user['address_id'];
}
$address = Model\UserAddress::findFirst(['conditions' => "address_id = $address_id AND user_id = {$user['user_id']}"]);
$addressList = Model\UserAddress::find(['conditions' => "address_id != $address_id AND user_id = {$user['user_id']}", 'order' => 'address_id DESC']);
if ($address) {
array_unshift($addressList, $address);
}
$regions = Model\Region::getAll();
foreach ($addressList as &$row) {
$country = $regions[$row['country']];
$province = $country['list'][$row['province']];
$city = $province['list'][$row['city']];
$district = $city['list'][$row['district']];
$row['address_detail'] = $province['region_name'] . $city['region_name'] . $district['region_name'] . $row['address'];
}
unset($row);
$data['addressList'] = $addressList;
foreach ($cartGoods['goods_list'] as &$goods) {
//$goods['goods_name'] = mb_substr($goods['goods_name'], 0, 15, 'utf-8') . '......';
}
unset($goods);
$regions = Model\Region::getAll();
$data['user'] = $user;
$data['regions'] = $regions;
$data['cartGoods'] = $cartGoods;
$data['navName'] = '';
echo $this->viewoutput('pc/cart-settle', $data);
}
/**
* 已购买的商品
*/
public function boughtListAction()
{
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'includes/lib_order.php');
$user = $this->auth->user();
$mcart = new Model\Cart;
global $db, $ecs;
$page = intval($this->request->getQuery('page')) ?: 1;
$limit = 10;
$offset = ($page - 1) * $limit;
$userid = $_SESSION['user_id'];
$morder = new Model\OrderInfo;
$arr = array();
$osArr = [];
$ssArr = [];
$psArr = [];
// 需要评论的order_id
$result = $morder->needComment();
$needCommentOrderids = [];
foreach ($result as $row) {
$needCommentOrderids[$row['order_id']] = $row['order_id'];
}
$osArr[] = OS_CONFIRMED;
$osArr[] = OS_SPLITED;
$ssArr[] = SS_RECEIVED;
$psArr[] = PS_PAYED;
$ossql = $sssql = $pssql = '';
if ($osArr) {
$ossql = ' AND ' . db_create_in($osArr, 'order_status');
}
if ($ssArr) {
$sssql = ' AND ' . db_create_in($ssArr, 'shipping_status');
}
if ($psArr) {
$pssql = ' AND ' . db_create_in($psArr, 'pay_status');
}
$sql = "SELECT order_id, order_sn, order_status, shipping_status, pay_status, add_time, " .
"(goods_amount + shipping_fee + insure_fee + pay_fee + pack_fee + card_fee + tax - discount) AS total_fee ".
" FROM " .$GLOBALS['ecs']->table('order_info') .
' WHERE 1 = 1';
$sql .= " AND user_id = '$userid' $ossql $sssql $pssql ORDER BY add_time DESC";
//$res = $GLOBALS['db']->SelectLimit($sql, 20, ($page - 1) * 20);
$res = $GLOBALS['db']->query($sql);
$orderids = [];
while ($row = $GLOBALS['db']->fetchRow($res))
{
$orderids[] = $row['order_id'];
$row['shipping_status'] = ($row['shipping_status'] == SS_SHIPPED_ING) ? SS_PREPARING : $row['shipping_status'];
$arr[$row['order_id']] = array('order_id' => $row['order_id'],
'order_sn' => $row['order_sn'],
'order_time' => local_date($GLOBALS['_CFG']['time_format'], $row['add_time']),
'order_status' => $row['order_status'],
'shipping_status' => $row['shipping_status'],
'pay_status' => $row['pay_status'],
'total_fee' => price_format($row['total_fee'], false),
'needComment' => isset($needCommentOrderids[$row['order_id']]) ? true : false,
'goods' => [],
);
}
$orders = $arr;
$count = 0;
if ($orderids) {
$orderidsStr = implode(',', $orderids);
/* 订单商品 */
$params = [
'alias' => 'og',
'columns' => "og.rec_id, og.order_id, og.goods_id, og.goods_name, og.goods_sn, og.market_price, og.goods_number, " .
"og.goods_price, og.goods_attr, og.is_real, og.parent_id, og.is_gift, " .
"og.goods_price * og.goods_number AS subtotal, og.extension_code, g.goods_thumb, g.goods_img " .
',c.comment_id ',
'conditions' => "og.order_id IN ($orderidsStr)",
'offset' => $offset,
'limit' => $limit,
'join' => [
[
'type' => 'LEFT',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = og.goods_id'
],
[
'type' => 'LEFT',
'table' => 'comment',
'alias' => 'c',
'on' => 'c.id_value = og.goods_id AND c.order_id = og.order_id '
]
],
];
$colls = Model\OrderGoods::find($params);
unset($params['limit'], $params['offset']);
$count = Model\OrderGoods::count($params);
foreach ($colls as $row) {
if (isset($orders[$row['order_id']])) {
$row['market_price'] = price_format($row['market_price'], false);
$row['goods_price'] = price_format($row['goods_price'], false);
$row['subtotal'] = price_format($row['subtotal'], false);
$orders[$row['order_id']]['goods'][] = $row;
}
}
}
$totalPage = $count ? intval(($count - 1) / $limit) + 1 : 1;
$data = [];
$data['user'] = $user;
$data['page'] = $page;
$data['totalPage'] = $totalPage;
$data['count'] = $count;
$data['orders'] = $orders;
$data['navName'] = '';
$data['userSidebar'] = 'bought-list';
echo $this->viewoutput('pc/bought-list', $data);
}
/**
* 我的收藏
*/
public function collectListAction()
{
global $db, $ecs;
$user = $this->auth->user();
$page = intval($this->request->getQuery('page')) ?: 1;
$ctype = $this->request->getQuery('collect_type') == 'brand' ? 'brand' : 'goods';
$limit = 10;
$offset = ($page - 1) * $limit;
$userid = $user['user_id'];
$data = [];
if ($ctype == 'goods') {
$params = [
'alias' => 'c',
'columns' => 'g.goods_id, g.goods_name, g.market_price, g.shop_price AS org_price, '.
"IFNULL(mp.user_price, g.shop_price * '$_SESSION[discount]') AS shop_price, g.goods_img, ".
'g.promote_price, g.promote_start_date,g.promote_end_date, c.rec_id, c.is_attention',
'conditions' => "c.user_id = '$userid' ORDER BY c.rec_id DESC",
'offset' => $offset,
'limit' => $limit,
'join' => [
[
'type' => 'LEFT',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = c.goods_id'
],
[
'type' => 'LEFT',
'table' => 'member_price',
'alias' => 'mp',
'on' => "mp.goods_id = g.goods_id AND mp.user_rank = '$_SESSION[user_rank]' "
],
],
];
$goodsList = Model\CollectGoods::find($params);
foreach ($goodsList as &$row) {
if ($row['promote_price'] > 0) {
$promote_price = bargain_price($row['promote_price'], $row['promote_start_date'], $row['promote_end_date']);
$row['promote_price'] = $promote_price > 0 ? price_format($promote_price) : '';
} else {
$row['promote_price'] = '';
}
if ($row['promote_price'] > 0) {
$row['price'] = $row['promote_price'];
} else {
$row['price'] = $row['shop_price'];
}
}
unset($row);
unset($params['limit'], $params['offset']);
$count = Model\CollectGoods::count($params);
$data['list'] = $goodsList;
} else {
$params = [
'alias' => 'cb',
'columns' => 'b.*',
'conditions' => "cb.user_id = '$userid'",
'offset' => $offset,
'limit' => $limit,
'order' => 'cb.id DESC',
'join' => [
[
'type' => 'LEFT',
'table' => 'brand',
'alias' => 'b',
'on' => 'b.brand_id = cb.brand_id'
]
],
];
$brandList = Model\CollectBrand::find($params);
foreach ($brandList as &$row) {
}
unset($row);
unset($params['limit'], $params['offset']);
$count = Model\CollectBrand::count($params);
$data['list'] = $brandList;
}
$paginator = new \Juice\Paginator(['type' => 'emptyArray', 'config' => [
'count' => $count,
'limit' => $limit,
'page' => $page,
]]);
$paginate = $paginator->getPaginate();
$data['user'] = $user;
$data['ctype'] = $ctype;
$data['paginate'] = $paginate;
$data['navName'] = '';
$data['userSidebar'] = 'collect-list';
echo $this->viewoutput('pc/collect-list', $data);
}
/**
* 评论商品
*/
public function commentAction()
{
global $db, $ecs;
$orderid = intval($this->request->getQuery('order_id'));
$goodsid = intval($this->request->getQuery('goods_id'));
if (! $orderid || ! $goodsid) {
return $this->response->redirect('user/bought/list');
}
$user = $this->auth->user();
$goods = Model\OrderGoods::findFirst([
'alias' => 'og',
'columns' => "og.rec_id, og.goods_id, og.goods_name, og.goods_attr, og.goods_sn, og.goods_number, goods_price, g.goods_thumb, o.add_time, o.order_id",
'conditions' => "og.order_id = $orderid AND og.goods_id = $goodsid AND o.user_id = " . $user['user_id'] . ' AND o.shipping_status = 2 AND c.comment_id IS NULL',
'join' => [
[
'type' => 'INNER',
'table' => 'order_info',
'alias' => 'o',
'on' => 'o.order_id = og.order_id'
],
[
'type' => 'INNER',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = og.goods_id'
],
[
'type' => 'LEFT',
'table' => 'comment',
'alias' => 'c',
'on' => 'c.order_id = og.order_id AND c.id_value = og.goods_id'
]
],
]);
if (empty($goods)) {
return $this->response->redirect('user/bought/list');
}
$goods['add_time_format'] = local_date('Y-m-d', $goods['add_time']);
$data = [];
$data['user'] = $user;
$data['goods'] = $goods;
$data['navName'] = '';
$data['userSidebar'] = 'bought-list';
echo $this->viewoutput('pc/comment', $data);
}
/**
* 订单列表
*/
public function orderListAction()
{
global $db, $ecs;
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'includes/lib_order.php');
$data = [];
$user = $this->auth->user();
$mcart = new Model\Cart;
$page = intval($this->request->getQuery('page')) ?: 1;
$limit = 10;
$offset = ($page - 1) * $limit;
$status = $this->request->getQuery('status') ?: 0;
$userid = $_SESSION['user_id'];
$morder = new Model\OrderInfo;
// 需要评论的order_id
$result = $morder->needComment();
$needCommentOrderids = [];
foreach ($result as $row) {
$needCommentOrderids[$row['order_id']] = $row['order_id'];
}
if (! empty($status)) {
$orderAttr = Model\OrderInfo::$orderStatus[$status];
}
$ossql = $sssql = $pssql = '';
if (! empty($orderAttr['os'])) {
$ossql = ' AND ' . db_create_in($orderAttr['os'], 'o.order_status');
}
if (! empty($orderAttr['ss'])) {
$sssql = ' AND ' . db_create_in($orderAttr['ss'], 'o.shipping_status');
}
if (! empty($orderAttr['ps'])) {
$pssql = ' AND ' . db_create_in($orderAttr['ps'], 'o.pay_status');
}
$conditions = '1 = 1';
if ($status == 4) {
if (empty($needCommentOrderids)) {
$conditions .= ' AND 1 = 2';
} else {
$conditions .= ' AND o.order_id IN (' . implode(',', $needCommentOrderids) . ')';
}
}
$conditions .= " AND o.user_id = '$userid' $ossql $sssql $pssql ORDER BY o.add_time DESC LIMIT $offset, $limit";
$colls = Model\OrderInfo::find([
'alias' => 'o',
'columns' => "o.order_id, o.order_sn, o.order_status, o.shipping_status, o.pay_status, o.add_time, " .
"(o.goods_amount + o.shipping_fee + o.insure_fee + o.pay_fee + o.pack_fee + o.card_fee + o.tax - o.discount) AS total_fee, " .
"do.delivery_id, do.delivery_sn"
,
'conditions' => $conditions,
'join' => [
[
'type' => 'LEFT',
'table' => 'delivery_order',
'alias' => 'do',
'on' => 'o.order_id = do.order_id'
]
],
]);
$orderids = [];
$orders = [];
foreach ($colls as $row) {
$orderids[] = $row['order_id'];
$row['shipping_status'] = ($row['shipping_status'] == SS_SHIPPED_ING) ? SS_PREPARING : $row['shipping_status'];
$row['status'] = Model\OrderInfo::getOrderStatus($row);
$orders[$row['order_id']] = [
'order_id' => $row['order_id'],
'status' => $row['status'],
'order_sn' => $row['order_sn'],
'order_time' => local_date($GLOBALS['_CFG']['time_format'], $row['add_time']),
'order_status' => $row['order_status'],
'shipping_status' => $row['shipping_status'],
'pay_status' => $row['pay_status'],
'total_fee' => price_format($row['total_fee'], false),
'delivery_id' => $row['delivery_id'],
'delivery_sn' => $row['delivery_sn'],
'needComment' => isset($needCommentOrderids[$row['order_id']]) ? true : false,
'goods' => [],
];
}
$statistics = $morder->getAllCount();
$count = empty($status) ? $statistics['total'] : $statistics['count'][$status];
$paginator = new \Juice\Paginator(['type' => 'emptyArray', 'config' => [
'count' => $count,
'limit' => $limit,
'page' => $page,
]]);
$paginate = $paginator->getPaginate();
$data['paginate'] = $paginate;
if ($orderids) {
$orderidsStr = implode(',', $orderids);
$params = [
'alias' => 'og',
'columns' => "og.rec_id, og.order_id, og.goods_id, og.goods_name, og.goods_sn, og.market_price, og.goods_number, " .
"og.goods_price, og.goods_attr, og.is_real, og.parent_id, og.is_gift, " .
"og.goods_price * og.goods_number AS subtotal, og.extension_code, g.goods_thumb, g.goods_img " .
',c.comment_id ',
'conditions' => "og.order_id IN ($orderidsStr)",
'join' => [
[
'type' => 'LEFT',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = og.goods_id'
],
[
'type' => 'LEFT',
'table' => 'comment',
'alias' => 'c',
'on' => 'c.id_value = og.goods_id AND c.order_id = og.order_id '
],
],
];
$colls = Model\OrderGoods::find($params);
foreach ($colls as $row) {
if (isset($orders[$row['order_id']])) {
$row['market_price'] = price_format($row['market_price'], false);
$row['goods_price'] = price_format($row['goods_price'], false);
$row['subtotal'] = price_format($row['subtotal'], false);
$orders[$row['order_id']]['goods'][] = $row;
}
}
}
$data['user'] = $user;
$data['orders'] = $orders;
$data['status'] = $status;
$data['statistics'] = $statistics;
$data['navName'] = '';
$data['userSidebar'] = 'order-list';
echo $this->viewoutput('pc/order-list', $data);
}
/**
* 地址
*/
public function addressListAction()
{
global $db, $ecs, $_CFG, $_LANG, $err;
$user = $this->auth->user();
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'languages/' .$_CFG['lang']. '/shopping_flow.php');
$addressList = Model\UserAddress::findByUserId($user['user_id']);
$regionsids = [];
foreach ($addressList as $region_id => $row) {
$row['province'] = isset($row['province']) ? intval($row['province']) : 0;
$row['city'] = isset($row['city']) ? intval($row['city']) : 0;
$row['district'] = isset($row['district']) ? intval($row['district']) : 0;
$regionsids[] = $row['district'];
$regionsids[] = $row['province'];
$regionsids[] = $row['city'];
}
$regions = [];
if ($regionsids) {
$idsstr = implode(', ', $regionsids);
$sql = 'SELECT region_id, region_name FROM ' . $GLOBALS['ecs']->table('region') .
" WHERE region_id IN ($idsstr)";
$res = $GLOBALS['db']->query($sql);
while ($row = $GLOBALS['db']->fetchRow($res)) {
$regions[$row['region_id']] = $row;
}
}
foreach ($addressList as $key => &$row) {
$row['province_name'] = isset($regions[$row['province']]) ? $regions[$row['province']]['region_name'] : '';
$row['city_name'] = isset($regions[$row['city']]) ? $regions[$row['city']]['region_name'] : '';
$row['district_name'] = isset($regions[$row['district']]) ? $regions[$row['district']]['region_name'] : '';
}
unset($row);
$status = $this->request->getQuery('status');
$_SESSION['origin_from'] = $status;
$regions = Model\Region::getAll();
$data = [];
$data['user'] = $user;
$data['addressList'] = $addressList;
$data['regions'] = $regions;
$data['navName'] = '';
$data['userSidebar'] = 'address-list';
echo $this->viewoutput('pc/address-list', $data);
}
/**
* 代理信息
*/
public function agentInfoAction()
{
global $db, $ecs, $_CFG, $_LANG, $err;
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'languages/' .$_CFG['lang']. '/shopping_flow.php');
$user = $this->auth->user();
$data = [];
$agent = Model\Agent::findFirstByAgentId($user['agent_id']);
if (empty($agent)) {
$data['isEdit'] = false;
} else {
$data['isEdit'] = true;
}
$data['user'] = $user;
$data['agent'] = $agent;
$data['navName'] = '';
$data['userSidebar'] = 'agent';
echo $this->viewoutput('pc/agent-info', $data);
}
/**
* 代理订单信息
*/
public function agentOrderListAction()
{
global $db, $ecs, $_CFG, $_LANG, $err;
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'languages/' .$_CFG['lang']. '/shopping_flow.php');
$user = $this->auth->user();
$page = intval($this->request->getQuery('page')) ?: 1;
$limit = 10;
$offset = ($page - 1) * $limit;
$ossql = $sssql = $pssql = '';
if (! empty($orderAttr['os'])) {
$ossql = ' AND ' . db_create_in($orderAttr['os'], 'order_status');
}
if (! empty($orderAttr['ss'])) {
$sssql = ' AND ' . db_create_in($orderAttr['ss'], 'shipping_status');
}
if (! empty($orderAttr['ps'])) {
$pssql = ' AND ' . db_create_in($orderAttr['ps'], 'pay_status');
}
$orderids = [];
$arr = [];
$count = 0;
if ($user['agent_id']) {
$params = [
'alias' => 'o',
'columns' => "o.order_id, o.order_sn, o.order_status, o.shipping_status, o.pay_status, o.add_time, " .
"(o.goods_amount + o.shipping_fee + o.insure_fee + o.pay_fee + o.pack_fee + o.card_fee + o.tax - o.discount) AS total_fee , u.user_name",
'conditions' => "o.agent_id = '{$user['agent_id']}' $ossql $sssql $pssql",
'offset' => $offset,
'limit' => $limit,
'join' => [
[
'type' => 'LEFT',
'table' => 'users',
'alias' => 'u',
'on' => 'u.user_id = o.user_id'
]
],
'order' => 'add_time DESC',
];
$colls = Model\OrderInfo::find($params);
foreach ($colls as $row)
{
$orderids[] = $row['order_id'];
$row['shipping_status'] = ($row['shipping_status'] == SS_SHIPPED_ING) ? SS_PREPARING : $row['shipping_status'];
$row['status'] = Model\OrderInfo::getOrderStatus($row);
$orderTime = local_date($GLOBALS['_CFG']['time_format'], $row['add_time']);
$dateArr = explode(' ', $orderTime);
$arr[$row['order_id']] = [
'order_id' => $row['order_id'],
'user_name' => $row['user_name'],
'status' => $row['status'],
'order_sn' => $row['order_sn'],
'order_time' => $orderTime,
'order_time_date' => $orderTime,
'order_time_arr' => $dateArr,
'order_status' => $row['order_status'],
'shipping_status' => $row['shipping_status'],
'pay_status' => $row['pay_status'],
'total_fee' => price_format($row['total_fee'], false),
'goods' => [],
];
}
unset($params['limit'], $params['offset']);
$count = Model\OrderInfo::count($params);
}
$paginator = new \Juice\Paginator(['type' => 'emptyArray', 'config' => [
'count' => $count,
'limit' => $limit,
'page' => $page,
]]);
$paginate = $paginator->getPaginate();
$orders = $arr;
if ($orderids) {
$orderidsStr = implode(',', $orderids);
$params = [
'alias' => 'og',
'columns' => "og.rec_id, og.order_id, og.goods_id, og.goods_name, og.goods_sn, og.market_price, og.goods_number, " .
"og.goods_price, og.goods_attr, og.is_real, og.parent_id, og.is_gift, " .
"og.goods_price * og.goods_number AS subtotal, og.extension_code, g.goods_thumb, g.goods_img " .
',c.comment_id ',
'conditions' => "og.order_id IN ($orderidsStr)",
'join' => [
[
'type' => 'LEFT',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = og.goods_id'
],
[
'type' => 'LEFT',
'table' => 'comment',
'alias' => 'c',
'on' => 'c.id_value = og.goods_id AND c.order_id = og.order_id '
]
],
];
$colls = Model\OrderGoods::find($params);
foreach ($colls as $row) {
if (isset($orders[$row['order_id']])) {
$row['market_price'] = price_format($row['market_price'], false);
$row['goods_price'] = price_format($row['goods_price'], false);
$row['subtotal'] = price_format($row['subtotal'], false);
$orders[$row['order_id']]['goods'][] = $row;
}
}
}
$data = [];
$data['user'] = $user;
$data['orders'] = $orders;
$data['paginate'] = $paginate;
$data['navName'] = '';
$data['userSidebar'] = 'agent-order-list';
echo $this->viewoutput('pc/agent-order-list', $data);
}
/**
* 绑定微信号
*/
public function wechatBindAction()
{
$user = $this->auth->user();
$wechat = new WechatSDK;
$url = 'http://www.liowang.com/shop/wechat/oauth2/callback/bind';
$state = ['env' => 'product'];
if (isLocal()) {
$state = ['env' => 'local'];
}
return $this->response->redirect($wechat->pcWebOauth2($url, $state), true);
}
/**
* PC 微信回调 oauth2 绑定已登录用户
*/
public function wechatOauth2CallbackBindAction()
{
global $ecs, $db, $user;
include_once(ROOT_PATH . 'includes/lib_passport.php');
$code = $this->request->getQuery('code');
if (empty($code)) {
echo '授权失败';
exit;
}
$state = $this->request->getQuery('state');
parse_str($state, $params);
if (! islocal() && ! empty($params['env']) && $params['env'] == 'local') {
return $this->response->redirect('http://liowang.dev.seafarer.me/shop/wechat/oauth2/callback/bind?' . http_build_query($this->request->getQuery()));
}
$wechat = new WechatSDK;
$config = $this->config->wechat;
$config->appid = $config->open_web_appid;
$config->appsecret = $config->open_web_secret;
$wechat->init($config);
$response = $wechat->getUserTokenByCode($code);
if (empty($response['errcode'])) {
// 授权成功
$ewxpcuser = Model\WxPcUser::findFirstByOpenid($response['openid']);
$data = [];
$isFirst = false;
$isNeedCreate = false;
$uuser = [];
$wxpcuser = [];
if ($ewxpcuser) {
// 用户已经存在
$euser = Model\User::findFirstByWxPcUserid($ewxpcuser['id']);
} else {
// 第一次访问
$isFirst = true;
$wxuser = Model\WxUser::findFirstByUnionid($response['unionid']);
if (empty($wxuser)) {
$isNeedCreate = true;
} else {
$euser = Model\User::findFirstByWxUserid($wxuser['id']);
}
}
$wxpcuser['openid'] = $response['openid'];
$wxpcuser['unionid'] = $response['unionid'];
$wxpcuser['access_token'] = $response['access_token'];
$wxpcuser['refresh_token'] = $response['refresh_token'];
$wxpcuser['expired_at'] = $response['expires_in'] + time() - 200;
$wxpcuser['scope'] = $response['scope'];
$wxpcuser['ip'] = $this->request->getClientAddress();
$info = $wechat->getUserInfoByOauth($response['openid'], $response['access_token']);
$wxpcuser['nickname'] = $info['nickname'];
$wxpcuser['gender'] = $info['sex'];
$wxpcuser['language'] = $info['language'];
$wxpcuser['city'] = $info['city'];
$wxpcuser['country'] = $info['country'];
$wxpcuser['province'] = $info['province'];
$wxpcuser['headimgurl'] = $info['headimgurl'];
if (! empty($euser) && $euser['user_id'] != $_SESSION['user_id']) {
exit('该微信已被绑定');
}
if ($isFirst) {
// 首次PC扫码登录
$id = Model\WxPcUser::insert($wxpcuser);
$uuser['wx_pc_userid'] = $id;
Model\User::update($uuser, "user_id = {$_SESSION['user_id']}");
} else {
$uuser['wx_pc_userid'] = $ewxpcuser['id'];
Model\User::update($uuser, "user_id = {$_SESSION['user_id']}");
Model\WxPcUser::update($wxpcuser, "openid = '{$response['openid']}'");
Warning : Cannot modify header information - headers already sent by (output started at /usr/local/www/liowang-shop-robotwar/shop/app/libraries/PrettyExceptions/Library/Phalcon/Utils/PrettyExceptions.php:339) in /usr/local/www/liowang-shop-robotwar/shop/app/libraries/PrettyExceptions/Library/Phalcon/Utils/PrettyExceptions.php on line 440
Exception - Undefined index: expired_at
Undefined index: expired_at
/usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php (258)
#1 {closure} (8 , Undefined index: expired_at , /usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php , 258 , Array(9) )/usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php (258)
<?php
/**
* weixin library
* @author geekzhumail@gmail.com
* @since 2014-09-24
* @edit 2015-03-05
*/
class SDKRuntimeException extends Exception {
public function errorMessage() {
return $this->getMessage();
}
}
class WechatSDK extends WechatBase{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => 'wxdf0a039cf4ea94d1',
'appsecret' => '7100f0fe837a5287f075b6d2d9814ca0',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/';
/**
* @var access_token
*/
protected $accessToken = '';
/**
* 是否从远程服务器拿token
*/
protected $isRemote = false;
public function __construct() {
parent::__construct();
$this->isRemote = $this->config->is_remote;
$this->accessToken = $this->getToken();
}
/**
* 初始化数据
*/
public function init($config) {
$this->config = $config;
}
/**
* 获取Appid
*/
public function getAppid() {
return $this->config['appid'];
}
/**
* weixin signature
*/
public function checkSignature($signature, $timestamp, $nonce) {
$token = $this->config['token'];
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr);
$tmpStr = implode( $tmpArr );
$tmpStr = sha1( $tmpStr );
if( $tmpStr == $signature ){
return true;
}else{
return false;
}
}
/**
* web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function webOauth2($redirectUri, $state = [], $type = 'snsapi_base') {
$params = [];
$params['appid'] = $this->config['appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = $type;
if (empty($state['type'])) {
$state['type'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* pc web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function pcWebOauth2($redirectUri, $state = []) {
$params = [];
$params['appid'] = $this->config['open_web_appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = 'snsapi_login';
if (empty($state['env'])) {
$state['env'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/qrconnect?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* get access token by code
*/
public function getUserTokenByCode($code) {
$params = array();
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$params['code'] = $code;
$params['grant_type'] = 'authorization_code';
$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* refresh access token
* @param string $token 填写通过access_token获取到的refresh_token参数
*/
public function refreshUserToken($token) {
$params = [];
$params['appid'] = $this->config['appid'];
$params['grant_type'] = 'refresh_token';
$params['refresh_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 根据网页授权拉取用户信息(需scope为 snsapi_userinfo)
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function getUserInfoByOauth($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('sns/userinfo', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 验证授权凭证(access_token)是否有效
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function validUserToken($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/auth?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 获取公众号token
*/
public function getToken() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
if ($this->isRemote) {
return $this->getTokenFromRemote();
}
$params = [];
$params['grant_type'] = 'client_credential';
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$url = 'https://api.weixin.qq.com/cgi-bin/token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
// 写入缓存
$expiredAt = $response['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $response['access_token'], $expiredAt);
return $response['access_token'];
}
/**
* 获取access_token from remote
*/
public function getTokenFromRemote() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/token/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$cache->save('wechat_access_token_expired', $resp['expired_at'], $expiredAt);
$expiredAt = 120;
$cache->save($key, $resp['access_token'], $expiredAt);
return $resp['access_token'];
} else {
throw new Exception('wx admin get token error');
}
}
/**
* 获取用户基本信息(包括UnionID机制)
* @param string $openid 用户的唯一标识
* @param string $token 调用接口凭证,默认值为Wechat::getToken()
*/
public function getUserInfo($openid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('cgi-bin/user/info', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 下载已经上传到微信的资源文件
*/
public function downloadMedia($mediaid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['access_token'] = $token;
$params['media_id'] = $mediaid;
$url = 'http://file.api.weixin.qq.com/cgi-bin/media/get?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => false,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$content = curl_exec($ch);
$info = curl_getinfo($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == '200') {
// 正常则分割header和body
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($content, 0, $headerSize);
$body = substr($content, $headerSize);
}
curl_close($ch);
$resp['info'] = $info;
$resp['header'] = $header;
$resp['body'] = $body;
return $resp;
}
/**
* 获取 js api ticket
*/
public function getJsapiTicket()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
if ($this->isRemote) {
return $this->getJsapiTicketFromRemote();
}
$resp = json_decode($this->get('cgi-bin/ticket/getticket', ['access_token' => $this->accessToken, 'type' => 'jsapi']), true);
// 写入缓存
$expiredAt = $resp['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
}
/**
* 获取 js api ticket
*/
public function getJsapiTicketFromRemote()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/jsticket/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$expiredAt = 120;
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
} else {
throw new Exception('wx admin get ticket error');
}
}
/**
* 获取js api 签名
*/
public function getSignPackage($url = '')
{
$jsticket = $this->getJsapiTicket();
if (! $url) {
$url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}
$timestamp = time();
$nonceStr = $this->createNoncestr(16);
// 这里参数的顺序要按照 key 值 ASCII 码升序排序
$string = "jsapi_ticket={$jsticket}&noncestr={$nonceStr}×tamp={$timestamp}&url={$url}";
return [
"appId" => $this->config['appid'],
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => sha1($string),
"rawString" => $string
];
}
/**
* 新建一个类,由于PSR-4无法获取不以文件名命名的类;
*/
public function newClass($name)
{
return new $name();
}
// 微信支付
/**
* 获取微信付款码的prepayid
* @params array $params 参数
*/
public function getPrepayId($params) {
$sdk = new UnifiedOrderSDK;
$sdk->setParameter("trade_type","JSAPI");//交易类型
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$prepayid = $sdk->getPrepayId();
return $prepayid;
}
/**
* 获取支付js api 签名
*/
public function getPaySignPackage($prepayid)
{
$jsApiObj["appId"] = $this->config['appid'];
$timeStamp = time();
$jsApiObj["timeStamp"] = "$timeStamp";
$jsApiObj["nonceStr"] = $this->createNoncestr();
$jsApiObj["package"] = "prepay_id=$prepayid";
$jsApiObj["signType"] = "MD5";
$jsApiObj["paySign"] = $this->getSign($jsApiObj);
$signPackage = $jsApiObj;
return $signPackage;
}
/**
* 发送红包接口
* @params array $params 参数
*/
public function sendRedPack($params) {
$sdk = new RedPackSDK;
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$result = $sdk->send();
return $result;
}
}
/**
* 所有接口的基类
*/
class WechatBase
{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => '',
'appsecret' => '',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/cgi-bin/';
public function __construct() {
$this->di = Phalcon\DI::getDefault();
$this->config = $this->di->getConfig()['wechat'];
}
protected function get($url, $params = '')
{
if (strpos($url, 'https://') === false && strpos($url, 'http://') === false) {
$url = $this->apiPrefix.$url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl get error');
}
}
protected function post($url, $params = '', $data = '')
{
if (strpos($url, 'https://') === false) {
$url = $this->apiPrefix . $url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
if ($data) {
$options[CURLOPT_POSTFIELDS] = json_encode($data, JSON_UNESCAPED_UNICODE);
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl post error');
}
}
public function trimString($value)
{
$ret = null;
if (null != $value)
{
$ret = $value;
if (strlen($ret) == 0)
{
$ret = null;
}
}
return $ret;
}
/**
* 作用:产生随机字符串,不长于32位
*/
public function createNoncestr( $length = 32, $type = 'all')
{
if ($type == 'all') {
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
} else if($type == 'number') {
$chars = "0123456789";
}
$str ="";
for ( $i = 0; $i < $length; $i++ ) {
$str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
/**
* 作用:格式化参数,签名过程需要使用
*/
public function formatBizQueryParaMap($paraMap, $urlencode)
{
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v)
{
if($urlencode)
{
$v = urlencode($v);
}
//$buff .= strtolower($k) . "=" . $v . "&";
$buff .= $k . "=" . $v . "&";
}
$reqPar;
if (strlen($buff) > 0)
{
$reqPar = substr($buff, 0, strlen($buff)-1);
}
return $reqPar;
}
/**
* 作用:生成签名
*/
public function getSign($Obj)
{
foreach ($Obj as $k => $v)
{
$Parameters[$k] = $v;
}
//签名步骤一:按字典序排序参数
ksort($Parameters);
$String = $this->formatBizQueryParaMap($Parameters, false);
//echo '【string1】'.$String.'</br>';
//签名步骤二:在string后加入KEY
$String = $String . "&key=" . $this->config['key'];
//echo "【string2】".$String."</br>";
//签名步骤三:MD5加密
$String = md5($String);
//echo "【string3】 ".$String."</br>";
//签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
//echo "【result】 ".$result_."</br>";
return $result_;
}
/**
* 作用:array转xml
*/
public function arrayToXml($arr)
{
$xml = "<xml>";
foreach ($arr as $key=>$val)
{
if (is_numeric($val))
{
$xml.="<".$key.">".$val."</".$key.">";
}
else
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
$xml.="</xml>";
return $xml;
}
/**
* 作用:将xml转为array
*/
public function xmlToArray($xml)
{
//将XML转为array
$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $array_data;
}
/**
* 作用:以post方式提交xml到对应的接口url
*/
public function postXmlCurl($xml,$url,$second=30)
{
//初始化curl
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
curl_close($ch);
//返回结果
if($data)
{
return $data;
}
else
{
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
return false;
}
}
/**
* 作用:使用证书,以post方式提交xml到对应的接口url
*/
public function postXmlSSLCurl($xml,$url,$second=30)
{
$ch = curl_init();
//超时时间
curl_setopt($ch,CURLOPT_TIMEOUT,$second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch,CURLOPT_HEADER,FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT, $this->config['sslcert_path']);
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY, $this->config['sslkey_path']);
//post提交方式
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
$data = curl_exec($ch);
//返回结果
if($data){
curl_close($ch);
return $data;
}
else {
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
curl_close($ch);
return false;
}
}
}
/**
* 请求型接口的基类
*/
class WechatPayRequestBase extends WechatBase
{
var $parameters;//请求参数,类型为关联数组
public $response;//微信返回的响应
public $result;//返回参数,类型为关联数组
var $url;//接口链接
var $curl_timeout;//curl超时时间
/**
* 作用:设置请求参数
*/
function setParameter($parameter, $parameterValue)
{
$this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 作用:设置标配的请求参数,生成签名,生成接口参数xml
*/
function createXml()
{
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 作用:post请求xml
*/
function postXml()
{
$xml = $this->createXml();
$this->response = $this->postXmlCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:使用证书post请求xml
*/
function postXmlSSL()
{
$xml = $this->createXml();
$this->response = $this->postXmlSSLCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:获取结果,默认不使用证书
*/
function getResult()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
return $this->result;
}
}
/**
* 统一支付接口类
*/
class UnifiedOrderSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
//检测必填参数
if(empty($this->parameters["out_trade_no"]))
{
throw new SDKRuntimeException("缺少统一支付接口必填参数out_trade_no!"."<br>");
}elseif(empty($this->parameters["body"])){
throw new SDKRuntimeException("缺少统一支付接口必填参数body!"."<br>");
}elseif (empty($this->parameters["total_fee"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数total_fee!"."<br>");
}elseif (empty($this->parameters["notify_url"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数notify_url!"."<br>");
}elseif (empty($this->parameters["trade_type"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数trade_type!"."<br>");
}elseif ($this->parameters["trade_type"] == "JSAPI" &&
empty($this->parameters["openid"])){
throw new SDKRuntimeException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!"."<br>");
}
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["spbill_create_ip"] = $this->di->getRequest()->getClientAddress();//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 获取prepay_id
*/
function getPrepayId()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
$prepay_id = $this->result["prepay_id"];
return $prepay_id;
}
}
/**
* 响应型接口基类
*/
class WechatPayServerBase extends WechatBase
{
public $data;//接收到的数据,类型为关联数组
var $returnParameters;//返回参数,类型为关联数组
/**
* 将微信的请求xml转换成关联数组,以方便数据处理
*/
function saveData($xml)
{
$this->data = $this->xmlToArray($xml);
}
function checkSign()
{
$tmpData = $this->data;
unset($tmpData['sign']);
$sign = $this->getSign($tmpData);//本地签名
if (isLocal()) {
echo $sign;
}
if ($this->data['sign'] == $sign) {
return TRUE;
}
return FALSE;
}
/**
* 获取微信的请求数据
*/
function getData()
{
return $this->data;
}
/**
* 设置返回微信的xml数据
*/
function setReturnParameter($parameter, $parameterValue)
{
$this->returnParameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 生成接口参数xml
*/
function createXml()
{
return $this->arrayToXml($this->returnParameters);
}
/**
* 将xml数据返回微信
*/
function returnXml()
{
$returnXml = $this->createXml();
return $returnXml;
}
}
/**
* 红包发送接口类
*/
class RedPackSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
$requiredParams = [
'mch_billno',
'nick_name',
'send_name',
're_openid',
'total_amount',
'min_value',
'max_value',
'total_num',
'wishing',
'act_name',
'remark',
];
//检测必填参数
foreach ($requiredParams as $val) {
if (empty($this->parameters[$val])) {
throw new SDKRuntimeException("缺少红包接口必填参数{$val}!"."<br>");
}
}
$this->parameters["wxappid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["client_ip"] = gethostbyname(gethostname());//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 发送红包
*
* curl 返回例子
* <xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[发放成功.]]></return_msg>
<result_code><![CDATA[SUCCESS]]></result_code>
<err_code><![CDATA[0]]></err_code>
<err_code_des><![CDATA[发放成功.]]></err_code_des>
<mch_billno><![CDATA[1228563102201503261427336509]]></mch_billno>
<mch_id>1228563102</mch_id>
<wxappid><![CDATA[wxdceccb9098a978b8]]></wxappid>
<re_openid><![CDATA[o55e0t1NHPxdcRCjbIu_xnjHc2u8]]></re_openid>
<total_amount>100</total_amount>
</xml>
*/
public function send() {
$this->postXmlSSL();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
return $this->result;
}
#2 WechatSDK ->getTokenFromRemote ()/usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php (209)
<?php
/**
* weixin library
* @author geekzhumail@gmail.com
* @since 2014-09-24
* @edit 2015-03-05
*/
class SDKRuntimeException extends Exception {
public function errorMessage() {
return $this->getMessage();
}
}
class WechatSDK extends WechatBase{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => 'wxdf0a039cf4ea94d1',
'appsecret' => '7100f0fe837a5287f075b6d2d9814ca0',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/';
/**
* @var access_token
*/
protected $accessToken = '';
/**
* 是否从远程服务器拿token
*/
protected $isRemote = false;
public function __construct() {
parent::__construct();
$this->isRemote = $this->config->is_remote;
$this->accessToken = $this->getToken();
}
/**
* 初始化数据
*/
public function init($config) {
$this->config = $config;
}
/**
* 获取Appid
*/
public function getAppid() {
return $this->config['appid'];
}
/**
* weixin signature
*/
public function checkSignature($signature, $timestamp, $nonce) {
$token = $this->config['token'];
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr);
$tmpStr = implode( $tmpArr );
$tmpStr = sha1( $tmpStr );
if( $tmpStr == $signature ){
return true;
}else{
return false;
}
}
/**
* web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function webOauth2($redirectUri, $state = [], $type = 'snsapi_base') {
$params = [];
$params['appid'] = $this->config['appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = $type;
if (empty($state['type'])) {
$state['type'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* pc web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function pcWebOauth2($redirectUri, $state = []) {
$params = [];
$params['appid'] = $this->config['open_web_appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = 'snsapi_login';
if (empty($state['env'])) {
$state['env'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/qrconnect?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* get access token by code
*/
public function getUserTokenByCode($code) {
$params = array();
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$params['code'] = $code;
$params['grant_type'] = 'authorization_code';
$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* refresh access token
* @param string $token 填写通过access_token获取到的refresh_token参数
*/
public function refreshUserToken($token) {
$params = [];
$params['appid'] = $this->config['appid'];
$params['grant_type'] = 'refresh_token';
$params['refresh_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 根据网页授权拉取用户信息(需scope为 snsapi_userinfo)
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function getUserInfoByOauth($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('sns/userinfo', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 验证授权凭证(access_token)是否有效
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function validUserToken($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/auth?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 获取公众号token
*/
public function getToken() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
if ($this->isRemote) {
return $this->getTokenFromRemote();
}
$params = [];
$params['grant_type'] = 'client_credential';
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$url = 'https://api.weixin.qq.com/cgi-bin/token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
// 写入缓存
$expiredAt = $response['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $response['access_token'], $expiredAt);
return $response['access_token'];
}
/**
* 获取access_token from remote
*/
public function getTokenFromRemote() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/token/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$cache->save('wechat_access_token_expired', $resp['expired_at'], $expiredAt);
$expiredAt = 120;
$cache->save($key, $resp['access_token'], $expiredAt);
return $resp['access_token'];
} else {
throw new Exception('wx admin get token error');
}
}
/**
* 获取用户基本信息(包括UnionID机制)
* @param string $openid 用户的唯一标识
* @param string $token 调用接口凭证,默认值为Wechat::getToken()
*/
public function getUserInfo($openid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('cgi-bin/user/info', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 下载已经上传到微信的资源文件
*/
public function downloadMedia($mediaid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['access_token'] = $token;
$params['media_id'] = $mediaid;
$url = 'http://file.api.weixin.qq.com/cgi-bin/media/get?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => false,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$content = curl_exec($ch);
$info = curl_getinfo($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == '200') {
// 正常则分割header和body
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($content, 0, $headerSize);
$body = substr($content, $headerSize);
}
curl_close($ch);
$resp['info'] = $info;
$resp['header'] = $header;
$resp['body'] = $body;
return $resp;
}
/**
* 获取 js api ticket
*/
public function getJsapiTicket()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
if ($this->isRemote) {
return $this->getJsapiTicketFromRemote();
}
$resp = json_decode($this->get('cgi-bin/ticket/getticket', ['access_token' => $this->accessToken, 'type' => 'jsapi']), true);
// 写入缓存
$expiredAt = $resp['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
}
/**
* 获取 js api ticket
*/
public function getJsapiTicketFromRemote()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/jsticket/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$expiredAt = 120;
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
} else {
throw new Exception('wx admin get ticket error');
}
}
/**
* 获取js api 签名
*/
public function getSignPackage($url = '')
{
$jsticket = $this->getJsapiTicket();
if (! $url) {
$url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}
$timestamp = time();
$nonceStr = $this->createNoncestr(16);
// 这里参数的顺序要按照 key 值 ASCII 码升序排序
$string = "jsapi_ticket={$jsticket}&noncestr={$nonceStr}×tamp={$timestamp}&url={$url}";
return [
"appId" => $this->config['appid'],
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => sha1($string),
"rawString" => $string
];
}
/**
* 新建一个类,由于PSR-4无法获取不以文件名命名的类;
*/
public function newClass($name)
{
return new $name();
}
// 微信支付
/**
* 获取微信付款码的prepayid
* @params array $params 参数
*/
public function getPrepayId($params) {
$sdk = new UnifiedOrderSDK;
$sdk->setParameter("trade_type","JSAPI");//交易类型
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$prepayid = $sdk->getPrepayId();
return $prepayid;
}
/**
* 获取支付js api 签名
*/
public function getPaySignPackage($prepayid)
{
$jsApiObj["appId"] = $this->config['appid'];
$timeStamp = time();
$jsApiObj["timeStamp"] = "$timeStamp";
$jsApiObj["nonceStr"] = $this->createNoncestr();
$jsApiObj["package"] = "prepay_id=$prepayid";
$jsApiObj["signType"] = "MD5";
$jsApiObj["paySign"] = $this->getSign($jsApiObj);
$signPackage = $jsApiObj;
return $signPackage;
}
/**
* 发送红包接口
* @params array $params 参数
*/
public function sendRedPack($params) {
$sdk = new RedPackSDK;
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$result = $sdk->send();
return $result;
}
}
/**
* 所有接口的基类
*/
class WechatBase
{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => '',
'appsecret' => '',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/cgi-bin/';
public function __construct() {
$this->di = Phalcon\DI::getDefault();
$this->config = $this->di->getConfig()['wechat'];
}
protected function get($url, $params = '')
{
if (strpos($url, 'https://') === false && strpos($url, 'http://') === false) {
$url = $this->apiPrefix.$url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl get error');
}
}
protected function post($url, $params = '', $data = '')
{
if (strpos($url, 'https://') === false) {
$url = $this->apiPrefix . $url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
if ($data) {
$options[CURLOPT_POSTFIELDS] = json_encode($data, JSON_UNESCAPED_UNICODE);
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl post error');
}
}
public function trimString($value)
{
$ret = null;
if (null != $value)
{
$ret = $value;
if (strlen($ret) == 0)
{
$ret = null;
}
}
return $ret;
}
/**
* 作用:产生随机字符串,不长于32位
*/
public function createNoncestr( $length = 32, $type = 'all')
{
if ($type == 'all') {
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
} else if($type == 'number') {
$chars = "0123456789";
}
$str ="";
for ( $i = 0; $i < $length; $i++ ) {
$str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
/**
* 作用:格式化参数,签名过程需要使用
*/
public function formatBizQueryParaMap($paraMap, $urlencode)
{
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v)
{
if($urlencode)
{
$v = urlencode($v);
}
//$buff .= strtolower($k) . "=" . $v . "&";
$buff .= $k . "=" . $v . "&";
}
$reqPar;
if (strlen($buff) > 0)
{
$reqPar = substr($buff, 0, strlen($buff)-1);
}
return $reqPar;
}
/**
* 作用:生成签名
*/
public function getSign($Obj)
{
foreach ($Obj as $k => $v)
{
$Parameters[$k] = $v;
}
//签名步骤一:按字典序排序参数
ksort($Parameters);
$String = $this->formatBizQueryParaMap($Parameters, false);
//echo '【string1】'.$String.'</br>';
//签名步骤二:在string后加入KEY
$String = $String . "&key=" . $this->config['key'];
//echo "【string2】".$String."</br>";
//签名步骤三:MD5加密
$String = md5($String);
//echo "【string3】 ".$String."</br>";
//签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
//echo "【result】 ".$result_."</br>";
return $result_;
}
/**
* 作用:array转xml
*/
public function arrayToXml($arr)
{
$xml = "<xml>";
foreach ($arr as $key=>$val)
{
if (is_numeric($val))
{
$xml.="<".$key.">".$val."</".$key.">";
}
else
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
$xml.="</xml>";
return $xml;
}
/**
* 作用:将xml转为array
*/
public function xmlToArray($xml)
{
//将XML转为array
$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $array_data;
}
/**
* 作用:以post方式提交xml到对应的接口url
*/
public function postXmlCurl($xml,$url,$second=30)
{
//初始化curl
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
curl_close($ch);
//返回结果
if($data)
{
return $data;
}
else
{
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
return false;
}
}
/**
* 作用:使用证书,以post方式提交xml到对应的接口url
*/
public function postXmlSSLCurl($xml,$url,$second=30)
{
$ch = curl_init();
//超时时间
curl_setopt($ch,CURLOPT_TIMEOUT,$second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch,CURLOPT_HEADER,FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT, $this->config['sslcert_path']);
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY, $this->config['sslkey_path']);
//post提交方式
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
$data = curl_exec($ch);
//返回结果
if($data){
curl_close($ch);
return $data;
}
else {
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
curl_close($ch);
return false;
}
}
}
/**
* 请求型接口的基类
*/
class WechatPayRequestBase extends WechatBase
{
var $parameters;//请求参数,类型为关联数组
public $response;//微信返回的响应
public $result;//返回参数,类型为关联数组
var $url;//接口链接
var $curl_timeout;//curl超时时间
/**
* 作用:设置请求参数
*/
function setParameter($parameter, $parameterValue)
{
$this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 作用:设置标配的请求参数,生成签名,生成接口参数xml
*/
function createXml()
{
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 作用:post请求xml
*/
function postXml()
{
$xml = $this->createXml();
$this->response = $this->postXmlCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:使用证书post请求xml
*/
function postXmlSSL()
{
$xml = $this->createXml();
$this->response = $this->postXmlSSLCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:获取结果,默认不使用证书
*/
function getResult()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
return $this->result;
}
}
/**
* 统一支付接口类
*/
class UnifiedOrderSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
//检测必填参数
if(empty($this->parameters["out_trade_no"]))
{
throw new SDKRuntimeException("缺少统一支付接口必填参数out_trade_no!"."<br>");
}elseif(empty($this->parameters["body"])){
throw new SDKRuntimeException("缺少统一支付接口必填参数body!"."<br>");
}elseif (empty($this->parameters["total_fee"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数total_fee!"."<br>");
}elseif (empty($this->parameters["notify_url"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数notify_url!"."<br>");
}elseif (empty($this->parameters["trade_type"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数trade_type!"."<br>");
}elseif ($this->parameters["trade_type"] == "JSAPI" &&
empty($this->parameters["openid"])){
throw new SDKRuntimeException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!"."<br>");
}
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["spbill_create_ip"] = $this->di->getRequest()->getClientAddress();//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 获取prepay_id
*/
function getPrepayId()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
$prepay_id = $this->result["prepay_id"];
return $prepay_id;
}
}
/**
* 响应型接口基类
*/
class WechatPayServerBase extends WechatBase
{
public $data;//接收到的数据,类型为关联数组
var $returnParameters;//返回参数,类型为关联数组
/**
* 将微信的请求xml转换成关联数组,以方便数据处理
*/
function saveData($xml)
{
$this->data = $this->xmlToArray($xml);
}
function checkSign()
{
$tmpData = $this->data;
unset($tmpData['sign']);
$sign = $this->getSign($tmpData);//本地签名
if (isLocal()) {
echo $sign;
}
if ($this->data['sign'] == $sign) {
return TRUE;
}
return FALSE;
}
/**
* 获取微信的请求数据
*/
function getData()
{
return $this->data;
}
/**
* 设置返回微信的xml数据
*/
function setReturnParameter($parameter, $parameterValue)
{
$this->returnParameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 生成接口参数xml
*/
function createXml()
{
return $this->arrayToXml($this->returnParameters);
}
/**
* 将xml数据返回微信
*/
function returnXml()
{
$returnXml = $this->createXml();
return $returnXml;
}
}
/**
* 红包发送接口类
*/
class RedPackSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
$requiredParams = [
'mch_billno',
'nick_name',
'send_name',
're_openid',
'total_amount',
'min_value',
'max_value',
'total_num',
'wishing',
'act_name',
'remark',
];
//检测必填参数
foreach ($requiredParams as $val) {
if (empty($this->parameters[$val])) {
throw new SDKRuntimeException("缺少红包接口必填参数{$val}!"."<br>");
}
}
$this->parameters["wxappid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["client_ip"] = gethostbyname(gethostname());//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 发送红包
*
* curl 返回例子
* <xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[发放成功.]]></return_msg>
<result_code><![CDATA[SUCCESS]]></result_code>
<err_code><![CDATA[0]]></err_code>
<err_code_des><![CDATA[发放成功.]]></err_code_des>
<mch_billno><![CDATA[1228563102201503261427336509]]></mch_billno>
<mch_id>1228563102</mch_id>
<wxappid><![CDATA[wxdceccb9098a978b8]]></wxappid>
<re_openid><![CDATA[o55e0t1NHPxdcRCjbIu_xnjHc2u8]]></re_openid>
<total_amount>100</total_amount>
</xml>
*/
public function send() {
$this->postXmlSSL();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
return $this->result;
}
#3 WechatSDK ->getToken ()/usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php (45)
<?php
/**
* weixin library
* @author geekzhumail@gmail.com
* @since 2014-09-24
* @edit 2015-03-05
*/
class SDKRuntimeException extends Exception {
public function errorMessage() {
return $this->getMessage();
}
}
class WechatSDK extends WechatBase{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => 'wxdf0a039cf4ea94d1',
'appsecret' => '7100f0fe837a5287f075b6d2d9814ca0',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/';
/**
* @var access_token
*/
protected $accessToken = '';
/**
* 是否从远程服务器拿token
*/
protected $isRemote = false;
public function __construct() {
parent::__construct();
$this->isRemote = $this->config->is_remote;
$this->accessToken = $this->getToken();
}
/**
* 初始化数据
*/
public function init($config) {
$this->config = $config;
}
/**
* 获取Appid
*/
public function getAppid() {
return $this->config['appid'];
}
/**
* weixin signature
*/
public function checkSignature($signature, $timestamp, $nonce) {
$token = $this->config['token'];
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr);
$tmpStr = implode( $tmpArr );
$tmpStr = sha1( $tmpStr );
if( $tmpStr == $signature ){
return true;
}else{
return false;
}
}
/**
* web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function webOauth2($redirectUri, $state = [], $type = 'snsapi_base') {
$params = [];
$params['appid'] = $this->config['appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = $type;
if (empty($state['type'])) {
$state['type'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* pc web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function pcWebOauth2($redirectUri, $state = []) {
$params = [];
$params['appid'] = $this->config['open_web_appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = 'snsapi_login';
if (empty($state['env'])) {
$state['env'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/qrconnect?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* get access token by code
*/
public function getUserTokenByCode($code) {
$params = array();
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$params['code'] = $code;
$params['grant_type'] = 'authorization_code';
$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* refresh access token
* @param string $token 填写通过access_token获取到的refresh_token参数
*/
public function refreshUserToken($token) {
$params = [];
$params['appid'] = $this->config['appid'];
$params['grant_type'] = 'refresh_token';
$params['refresh_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 根据网页授权拉取用户信息(需scope为 snsapi_userinfo)
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function getUserInfoByOauth($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('sns/userinfo', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 验证授权凭证(access_token)是否有效
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function validUserToken($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/auth?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 获取公众号token
*/
public function getToken() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
if ($this->isRemote) {
return $this->getTokenFromRemote();
}
$params = [];
$params['grant_type'] = 'client_credential';
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$url = 'https://api.weixin.qq.com/cgi-bin/token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
// 写入缓存
$expiredAt = $response['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $response['access_token'], $expiredAt);
return $response['access_token'];
}
/**
* 获取access_token from remote
*/
public function getTokenFromRemote() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/token/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$cache->save('wechat_access_token_expired', $resp['expired_at'], $expiredAt);
$expiredAt = 120;
$cache->save($key, $resp['access_token'], $expiredAt);
return $resp['access_token'];
} else {
throw new Exception('wx admin get token error');
}
}
/**
* 获取用户基本信息(包括UnionID机制)
* @param string $openid 用户的唯一标识
* @param string $token 调用接口凭证,默认值为Wechat::getToken()
*/
public function getUserInfo($openid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('cgi-bin/user/info', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 下载已经上传到微信的资源文件
*/
public function downloadMedia($mediaid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['access_token'] = $token;
$params['media_id'] = $mediaid;
$url = 'http://file.api.weixin.qq.com/cgi-bin/media/get?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => false,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$content = curl_exec($ch);
$info = curl_getinfo($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == '200') {
// 正常则分割header和body
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($content, 0, $headerSize);
$body = substr($content, $headerSize);
}
curl_close($ch);
$resp['info'] = $info;
$resp['header'] = $header;
$resp['body'] = $body;
return $resp;
}
/**
* 获取 js api ticket
*/
public function getJsapiTicket()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
if ($this->isRemote) {
return $this->getJsapiTicketFromRemote();
}
$resp = json_decode($this->get('cgi-bin/ticket/getticket', ['access_token' => $this->accessToken, 'type' => 'jsapi']), true);
// 写入缓存
$expiredAt = $resp['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
}
/**
* 获取 js api ticket
*/
public function getJsapiTicketFromRemote()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/jsticket/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$expiredAt = 120;
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
} else {
throw new Exception('wx admin get ticket error');
}
}
/**
* 获取js api 签名
*/
public function getSignPackage($url = '')
{
$jsticket = $this->getJsapiTicket();
if (! $url) {
$url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}
$timestamp = time();
$nonceStr = $this->createNoncestr(16);
// 这里参数的顺序要按照 key 值 ASCII 码升序排序
$string = "jsapi_ticket={$jsticket}&noncestr={$nonceStr}×tamp={$timestamp}&url={$url}";
return [
"appId" => $this->config['appid'],
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => sha1($string),
"rawString" => $string
];
}
/**
* 新建一个类,由于PSR-4无法获取不以文件名命名的类;
*/
public function newClass($name)
{
return new $name();
}
// 微信支付
/**
* 获取微信付款码的prepayid
* @params array $params 参数
*/
public function getPrepayId($params) {
$sdk = new UnifiedOrderSDK;
$sdk->setParameter("trade_type","JSAPI");//交易类型
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$prepayid = $sdk->getPrepayId();
return $prepayid;
}
/**
* 获取支付js api 签名
*/
public function getPaySignPackage($prepayid)
{
$jsApiObj["appId"] = $this->config['appid'];
$timeStamp = time();
$jsApiObj["timeStamp"] = "$timeStamp";
$jsApiObj["nonceStr"] = $this->createNoncestr();
$jsApiObj["package"] = "prepay_id=$prepayid";
$jsApiObj["signType"] = "MD5";
$jsApiObj["paySign"] = $this->getSign($jsApiObj);
$signPackage = $jsApiObj;
return $signPackage;
}
/**
* 发送红包接口
* @params array $params 参数
*/
public function sendRedPack($params) {
$sdk = new RedPackSDK;
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$result = $sdk->send();
return $result;
}
}
/**
* 所有接口的基类
*/
class WechatBase
{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => '',
'appsecret' => '',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/cgi-bin/';
public function __construct() {
$this->di = Phalcon\DI::getDefault();
$this->config = $this->di->getConfig()['wechat'];
}
protected function get($url, $params = '')
{
if (strpos($url, 'https://') === false && strpos($url, 'http://') === false) {
$url = $this->apiPrefix.$url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl get error');
}
}
protected function post($url, $params = '', $data = '')
{
if (strpos($url, 'https://') === false) {
$url = $this->apiPrefix . $url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
if ($data) {
$options[CURLOPT_POSTFIELDS] = json_encode($data, JSON_UNESCAPED_UNICODE);
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl post error');
}
}
public function trimString($value)
{
$ret = null;
if (null != $value)
{
$ret = $value;
if (strlen($ret) == 0)
{
$ret = null;
}
}
return $ret;
}
/**
* 作用:产生随机字符串,不长于32位
*/
public function createNoncestr( $length = 32, $type = 'all')
{
if ($type == 'all') {
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
} else if($type == 'number') {
$chars = "0123456789";
}
$str ="";
for ( $i = 0; $i < $length; $i++ ) {
$str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
/**
* 作用:格式化参数,签名过程需要使用
*/
public function formatBizQueryParaMap($paraMap, $urlencode)
{
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v)
{
if($urlencode)
{
$v = urlencode($v);
}
//$buff .= strtolower($k) . "=" . $v . "&";
$buff .= $k . "=" . $v . "&";
}
$reqPar;
if (strlen($buff) > 0)
{
$reqPar = substr($buff, 0, strlen($buff)-1);
}
return $reqPar;
}
/**
* 作用:生成签名
*/
public function getSign($Obj)
{
foreach ($Obj as $k => $v)
{
$Parameters[$k] = $v;
}
//签名步骤一:按字典序排序参数
ksort($Parameters);
$String = $this->formatBizQueryParaMap($Parameters, false);
//echo '【string1】'.$String.'</br>';
//签名步骤二:在string后加入KEY
$String = $String . "&key=" . $this->config['key'];
//echo "【string2】".$String."</br>";
//签名步骤三:MD5加密
$String = md5($String);
//echo "【string3】 ".$String."</br>";
//签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
//echo "【result】 ".$result_."</br>";
return $result_;
}
/**
* 作用:array转xml
*/
public function arrayToXml($arr)
{
$xml = "<xml>";
foreach ($arr as $key=>$val)
{
if (is_numeric($val))
{
$xml.="<".$key.">".$val."</".$key.">";
}
else
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
$xml.="</xml>";
return $xml;
}
/**
* 作用:将xml转为array
*/
public function xmlToArray($xml)
{
//将XML转为array
$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $array_data;
}
/**
* 作用:以post方式提交xml到对应的接口url
*/
public function postXmlCurl($xml,$url,$second=30)
{
//初始化curl
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
curl_close($ch);
//返回结果
if($data)
{
return $data;
}
else
{
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
return false;
}
}
/**
* 作用:使用证书,以post方式提交xml到对应的接口url
*/
public function postXmlSSLCurl($xml,$url,$second=30)
{
$ch = curl_init();
//超时时间
curl_setopt($ch,CURLOPT_TIMEOUT,$second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch,CURLOPT_HEADER,FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT, $this->config['sslcert_path']);
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY, $this->config['sslkey_path']);
//post提交方式
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
$data = curl_exec($ch);
//返回结果
if($data){
curl_close($ch);
return $data;
}
else {
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
curl_close($ch);
return false;
}
}
}
/**
* 请求型接口的基类
*/
class WechatPayRequestBase extends WechatBase
{
var $parameters;//请求参数,类型为关联数组
public $response;//微信返回的响应
public $result;//返回参数,类型为关联数组
var $url;//接口链接
var $curl_timeout;//curl超时时间
/**
* 作用:设置请求参数
*/
function setParameter($parameter, $parameterValue)
{
$this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 作用:设置标配的请求参数,生成签名,生成接口参数xml
*/
function createXml()
{
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 作用:post请求xml
*/
function postXml()
{
$xml = $this->createXml();
$this->response = $this->postXmlCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:使用证书post请求xml
*/
function postXmlSSL()
{
$xml = $this->createXml();
$this->response = $this->postXmlSSLCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:获取结果,默认不使用证书
*/
function getResult()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
return $this->result;
}
}
/**
* 统一支付接口类
*/
class UnifiedOrderSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
//检测必填参数
if(empty($this->parameters["out_trade_no"]))
{
throw new SDKRuntimeException("缺少统一支付接口必填参数out_trade_no!"."<br>");
}elseif(empty($this->parameters["body"])){
throw new SDKRuntimeException("缺少统一支付接口必填参数body!"."<br>");
}elseif (empty($this->parameters["total_fee"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数total_fee!"."<br>");
}elseif (empty($this->parameters["notify_url"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数notify_url!"."<br>");
}elseif (empty($this->parameters["trade_type"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数trade_type!"."<br>");
}elseif ($this->parameters["trade_type"] == "JSAPI" &&
empty($this->parameters["openid"])){
throw new SDKRuntimeException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!"."<br>");
}
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["spbill_create_ip"] = $this->di->getRequest()->getClientAddress();//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 获取prepay_id
*/
function getPrepayId()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
$prepay_id = $this->result["prepay_id"];
return $prepay_id;
}
}
/**
* 响应型接口基类
*/
class WechatPayServerBase extends WechatBase
{
public $data;//接收到的数据,类型为关联数组
var $returnParameters;//返回参数,类型为关联数组
/**
* 将微信的请求xml转换成关联数组,以方便数据处理
*/
function saveData($xml)
{
$this->data = $this->xmlToArray($xml);
}
function checkSign()
{
$tmpData = $this->data;
unset($tmpData['sign']);
$sign = $this->getSign($tmpData);//本地签名
if (isLocal()) {
echo $sign;
}
if ($this->data['sign'] == $sign) {
return TRUE;
}
return FALSE;
}
/**
* 获取微信的请求数据
*/
function getData()
{
return $this->data;
}
/**
* 设置返回微信的xml数据
*/
function setReturnParameter($parameter, $parameterValue)
{
$this->returnParameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 生成接口参数xml
*/
function createXml()
{
return $this->arrayToXml($this->returnParameters);
}
/**
* 将xml数据返回微信
*/
function returnXml()
{
$returnXml = $this->createXml();
return $returnXml;
}
}
/**
* 红包发送接口类
*/
class RedPackSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
$requiredParams = [
'mch_billno',
'nick_name',
'send_name',
're_openid',
'total_amount',
'min_value',
'max_value',
'total_num',
'wishing',
'act_name',
'remark',
];
//检测必填参数
foreach ($requiredParams as $val) {
if (empty($this->parameters[$val])) {
throw new SDKRuntimeException("缺少红包接口必填参数{$val}!"."<br>");
}
}
$this->parameters["wxappid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["client_ip"] = gethostbyname(gethostname());//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 发送红包
*
* curl 返回例子
* <xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[发放成功.]]></return_msg>
<result_code><![CDATA[SUCCESS]]></result_code>
<err_code><![CDATA[0]]></err_code>
<err_code_des><![CDATA[发放成功.]]></err_code_des>
<mch_billno><![CDATA[1228563102201503261427336509]]></mch_billno>
<mch_id>1228563102</mch_id>
<wxappid><![CDATA[wxdceccb9098a978b8]]></wxappid>
<re_openid><![CDATA[o55e0t1NHPxdcRCjbIu_xnjHc2u8]]></re_openid>
<total_amount>100</total_amount>
</xml>
*/
public function send() {
$this->postXmlSSL();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
return $this->result;
}
#4 WechatSDK ->__construct ()/usr/local/www/liowang-shop-robotwar/shop/app/controllers/PageUserController.php (24)
<?php
use HomeSpace\Model;
class PageUserController extends PageBaseController {
/**
* 登录
*/
public function loginAction()
{
$data = [];
//echo '登录';
$act = $this->request->getQuery('act');
if ($act == 'not_active') {
if (empty($_SESSION['tmp_email'])) {
$act = '';
}
$data['email'] = $_SESSION['tmp_email'];
}
$data['act'] = $act;
$data['errmsg'] = $this->flash->getMessages('error');
$this->flash->clear();
$wechat = new WechatSDK;
//$url = $this->url->get('wechat/oauth2/callback');
$url = 'http://www.liowang.com/shop/wechat/oauth2/callback';
$state = ['env' => 'product'];
if (isLocal()) {
$state = ['env' => 'local'];
}
$data['wechat_login_url'] = $wechat->pcWebOauth2($url, $state);
echo $this->viewoutput('pc/login', $data);
}
/**
* 登录 POST
*/
public function postLoginAction()
{
global $user, $_LANG;
$errmsg = '';
$account = $this->request->getPost('account');
$password = $this->request->getPost('password');
$remember = ! empty($this->request->getQuery('remember'));
if (filter_var($account, FILTER_VALIDATE_EMAIL)) {
// 邮箱登录
$muser = Model\User::findFirst(['columns' => 'user_name', 'conditions' => "email = '$account'"]);
} else if(is_numeric($account) && strlen($account) == 11) {
// 手机登录
$muser = Model\User::findFirst(['columns' => 'user_name', 'conditions' => "mobile_phone = '$account'"]);
}
if (! empty($muser) && ! empty($password)) {
if ($user->login($muser['user_name'], $password, $remember)) {
$row = Model\User::findFirstByUserId($_SESSION['user_id']);
if ($row['is_validated'] == 1) {
update_user_info();
recalculate_price();
$this->auth->loginUsingId($_SESSION['user_id']);
$_SESSION['pc_login'] = true;
return $this->response->redirect('home/index');
} else {
//$_SESSION['login_fail'] ++ ;
//$errmsg = '该账号尚未激活请查阅该账号邮箱的激活邮件,或者联系客服';
$_SESSION['tmp_email'] = $row['email'];
return $this->response->redirect('login?act=not_active');
}
} else {
$_SESSION['login_fail'] ++ ;
$errmsg = $_LANG['login_failure'];
}
}
$errmsg = $errmsg ?: '账号或者密码错误';
$this->flash->error($errmsg);
return $this->response->redirect('login');
}
/**
* 退出登录
*/
public function logoutAction()
{
global $user;
$user->logout();
return $this->response->redirect($this->request->getHTTPReferer(), true);
}
/**
* 邮箱注册验证
*/
public function emailValidateAction()
{
include_once(ROOT_PATH . 'includes/lib_passport.php');
$data = [];
$data['status'] = 'user_not_exist';
$hash = $this->request->getQuery('hash');
if ($hash) {
$id = intval(register_hash('decode', $hash));
$user = Model\User::findFirstByUserId($id);
if ($user) {
if ($user['is_validated'] == 0) {
Model\User::update(['is_validated' => 1], ['user_id' => $id]);
$data['status'] = 'success';
} else {
$data['status'] = 'validated';
}
$data['user'] = $user;
}
}
echo $this->viewoutput('pc/email-validate', $data);
}
/**
* PC 微信回调 oauth2 函数
*/
public function wechatOauth2CallbackAction()
{
global $ecs, $db, $user;
include_once(ROOT_PATH . 'includes/lib_passport.php');
$code = $this->request->getQuery('code');
if (empty($code)) {
echo '授权失败';
exit;
}
$state = $this->request->getQuery('state');
parse_str($state, $params);
if (! islocal() && ! empty($params['env']) && $params['env'] == 'local') {
return $this->response->redirect('http://liowang.dev.seafarer.me/shop/wechat/oauth2/callback?' . http_build_query($this->request->getQuery()));
}
$wechat = new WechatSDK;
$config = $this->config->wechat;
$config->appid = $config->open_web_appid;
$config->appsecret = $config->open_web_secret;
$wechat->init($config);
$response = $wechat->getUserTokenByCode($code);
if (empty($response['errcode'])) {
// 授权成功
$ewxpcuser = Model\WxPcUser::findFirstByOpenid($response['openid']);
$data = [];
$isFirst = false;
$isNeedCreate = false;
$uuser = [];
$wxpcuser = [];
if ($ewxpcuser) {
// 用户已经存在
$euser = Model\User::findFirstByWxPcUserid($ewxpcuser['id']);
} else {
// 第一次访问
$isFirst = true;
$wxuser = Model\WxUser::findFirstByUnionid($response['unionid']);
if (empty($wxuser)) {
$isNeedCreate = true;
} else {
$euser = Model\User::findFirstByWxUserid($wxuser['id']);
}
}
$wxpcuser['openid'] = $response['openid'];
$wxpcuser['unionid'] = $response['unionid'];
$wxpcuser['access_token'] = $response['access_token'];
$wxpcuser['refresh_token'] = $response['refresh_token'];
$wxpcuser['expired_at'] = $response['expires_in'] + time() - 200;
$wxpcuser['scope'] = $response['scope'];
$wxpcuser['ip'] = $this->request->getClientAddress();
$info = $wechat->getUserInfoByOauth($response['openid'], $response['access_token']);
$wxpcuser['nickname'] = $info['nickname'];
$wxpcuser['gender'] = $info['sex'];
$wxpcuser['language'] = $info['language'];
$wxpcuser['city'] = $info['city'];
$wxpcuser['country'] = $info['country'];
$wxpcuser['province'] = $info['province'];
$wxpcuser['headimgurl'] = $info['headimgurl'];
if ($isFirst) {
// 首次PC扫码登录
$id = Model\WxPcUser::insert($wxpcuser);
$_SESSION['wx_pc_user_id'] = $id;
$uuser['wx_pc_userid'] = $id;
if (! $isNeedCreate) {
Model\User::update($uuser, "user_id = {$euser['user_id']}");
}
/**
if ($isNeedCreate) {
// 创建新用户
$uuser['avatar'] = 'shop/assets/pc/IMGS/defaultPhoto.png';
$name = '会员_' . time() . '_' . $id;
$uuser['nickname'] = $wxpcuser['nickname'];
$email = 'email_' . time() . '_' . $id . '@liowang.com';
if (register($name, '123123', $email) !== false) {;
$db->autoExecute($ecs->table('users'), $uuser, 'UPDATE', "user_id = {$_SESSION['user_id']}");
} else {
SystemException::error('授权失败');
}
} else {
// 绑定已在微信授权的用户
Model\User::update($uuser, "user_id = {$euser['user_id']}");
Model\WxPcUser::update($wxpcuser, "openid = '{$response['openid']}'");
$user->set_session($euser['user_name']);
$user->set_cookie($euser['user_name']);
update_user_info();
recalculate_price();
}**/
} else {
Model\WxPcUser::update($wxpcuser, "openid = '{$response['openid']}'");
$_SESSION['wx_pc_user_id'] = $ewxpcuser['id'];
}
if (empty($euser)) {
return $this->response->redirect('wechat/oauth2/register');
}
// 已注册直接登录
$user->set_session($euser['user_name']);
$user->set_cookie($euser['user_name']);
update_user_info();
recalculate_price();
$this->auth->loginUsingId($_SESSION['user_id']);
$_SESSION['pc_login'] = true;
} else {
SystemException::error($response['errmsg']);
}
$referer = $this->url->get('');
return $this->response->redirect($referer, true);
}
/**
* PC 微信账号登录绑定
*/
public function wechatOauth2RegisterAction()
{
global $ecs, $db, $user;
if (empty($_SESSION['wx_pc_user_id'])) {
return $this->response->redirect('login');
}
$data = [];
echo $this->viewoutput('pc/wechat-oauth2-register', $data);
}
/******** 需要登录授权 *********/
/**
* 个人信息
*/
public function infoAction()
{
$user = $this->auth->user();
list($user['birYear'], $user['birMonth'], $user['birDay']) = explode('-', $user['birthday']);
$user['birMonth'] = ltrim($user['birMonth'], 0);
$user['birDay'] = ltrim($user['birDay'], 0);
if (strpos($user['avatar'], 'http://') === false && strpos($user['avatar'], 'https://') === false) {
$user['avatar'] = 'http://' . $this->request->getHttpHost() . '/' . $user['avatar'];
}
$data = [];
$data['user'] = $user;
$data['navName'] = '';
$data['userSidebar'] = 'user-info';
echo $this->viewoutput('pc/user-info', $data);
}
/**
* 购物车
*/
public function cartAction()
{
require(ROOT_PATH . 'includes/lib_order.php');
$user = $this->auth->user();
$mcart = new Model\Cart;
$cartCount = $mcart->getCount();
$cartGoods = get_cart_goods();
$discount = compute_discount();
$goodsids = [];
foreach ($cartGoods['goods_list'] as $goods) {
$goodsids[] = $goods['goods_id'];
}
if ($goodsids) {
$collectGoodsList = Model\CollectGoods::find(['conditions' => "user_id='$_SESSION[user_id]' AND " . db_create_in($goodsids, 'goods_id')]);
foreach ($cartGoods['goods_list'] as &$goods) {
$goods['is_collect'] = false;
foreach ($collectGoodsList as $row) {
if ($row['goods_id'] == $goods['goods_id']) {
$goods['is_collect'] = true;
break;
}
}
}
unset($goods);
}
$data = [];
$data['user'] = $user;
$data['cartGoods'] = $cartGoods;
$data['navName'] = '';
echo $this->viewoutput('pc/user-cart', $data);
}
/**
* 购物车结算
*/
public function cartSettleAction()
{
global $db, $ecs, $_CFG, $_LANG, $err;
require(ROOT_PATH . 'includes/lib_order.php');
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'languages/' .$_CFG['lang']. '/shopping_flow.php');
$user = $this->auth->user();
$recids = $this->request->getQuery('rec_ids') ?: [];
$cartGoods = get_cart_goods($recids);
if (empty($cartGoods['goods_list'])) {
// 购物车为空
return $this->response->redirect('user/cart');
}
$discount = compute_discount();
$data = [];
$address_id = intval($this->request->getQuery('address_id'));
if ($address_id == 0) {
$address_id = $user['address_id'];
}
$address = Model\UserAddress::findFirst(['conditions' => "address_id = $address_id AND user_id = {$user['user_id']}"]);
$addressList = Model\UserAddress::find(['conditions' => "address_id != $address_id AND user_id = {$user['user_id']}", 'order' => 'address_id DESC']);
if ($address) {
array_unshift($addressList, $address);
}
$regions = Model\Region::getAll();
foreach ($addressList as &$row) {
$country = $regions[$row['country']];
$province = $country['list'][$row['province']];
$city = $province['list'][$row['city']];
$district = $city['list'][$row['district']];
$row['address_detail'] = $province['region_name'] . $city['region_name'] . $district['region_name'] . $row['address'];
}
unset($row);
$data['addressList'] = $addressList;
foreach ($cartGoods['goods_list'] as &$goods) {
//$goods['goods_name'] = mb_substr($goods['goods_name'], 0, 15, 'utf-8') . '......';
}
unset($goods);
$regions = Model\Region::getAll();
$data['user'] = $user;
$data['regions'] = $regions;
$data['cartGoods'] = $cartGoods;
$data['navName'] = '';
echo $this->viewoutput('pc/cart-settle', $data);
}
/**
* 已购买的商品
*/
public function boughtListAction()
{
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'includes/lib_order.php');
$user = $this->auth->user();
$mcart = new Model\Cart;
global $db, $ecs;
$page = intval($this->request->getQuery('page')) ?: 1;
$limit = 10;
$offset = ($page - 1) * $limit;
$userid = $_SESSION['user_id'];
$morder = new Model\OrderInfo;
$arr = array();
$osArr = [];
$ssArr = [];
$psArr = [];
// 需要评论的order_id
$result = $morder->needComment();
$needCommentOrderids = [];
foreach ($result as $row) {
$needCommentOrderids[$row['order_id']] = $row['order_id'];
}
$osArr[] = OS_CONFIRMED;
$osArr[] = OS_SPLITED;
$ssArr[] = SS_RECEIVED;
$psArr[] = PS_PAYED;
$ossql = $sssql = $pssql = '';
if ($osArr) {
$ossql = ' AND ' . db_create_in($osArr, 'order_status');
}
if ($ssArr) {
$sssql = ' AND ' . db_create_in($ssArr, 'shipping_status');
}
if ($psArr) {
$pssql = ' AND ' . db_create_in($psArr, 'pay_status');
}
$sql = "SELECT order_id, order_sn, order_status, shipping_status, pay_status, add_time, " .
"(goods_amount + shipping_fee + insure_fee + pay_fee + pack_fee + card_fee + tax - discount) AS total_fee ".
" FROM " .$GLOBALS['ecs']->table('order_info') .
' WHERE 1 = 1';
$sql .= " AND user_id = '$userid' $ossql $sssql $pssql ORDER BY add_time DESC";
//$res = $GLOBALS['db']->SelectLimit($sql, 20, ($page - 1) * 20);
$res = $GLOBALS['db']->query($sql);
$orderids = [];
while ($row = $GLOBALS['db']->fetchRow($res))
{
$orderids[] = $row['order_id'];
$row['shipping_status'] = ($row['shipping_status'] == SS_SHIPPED_ING) ? SS_PREPARING : $row['shipping_status'];
$arr[$row['order_id']] = array('order_id' => $row['order_id'],
'order_sn' => $row['order_sn'],
'order_time' => local_date($GLOBALS['_CFG']['time_format'], $row['add_time']),
'order_status' => $row['order_status'],
'shipping_status' => $row['shipping_status'],
'pay_status' => $row['pay_status'],
'total_fee' => price_format($row['total_fee'], false),
'needComment' => isset($needCommentOrderids[$row['order_id']]) ? true : false,
'goods' => [],
);
}
$orders = $arr;
$count = 0;
if ($orderids) {
$orderidsStr = implode(',', $orderids);
/* 订单商品 */
$params = [
'alias' => 'og',
'columns' => "og.rec_id, og.order_id, og.goods_id, og.goods_name, og.goods_sn, og.market_price, og.goods_number, " .
"og.goods_price, og.goods_attr, og.is_real, og.parent_id, og.is_gift, " .
"og.goods_price * og.goods_number AS subtotal, og.extension_code, g.goods_thumb, g.goods_img " .
',c.comment_id ',
'conditions' => "og.order_id IN ($orderidsStr)",
'offset' => $offset,
'limit' => $limit,
'join' => [
[
'type' => 'LEFT',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = og.goods_id'
],
[
'type' => 'LEFT',
'table' => 'comment',
'alias' => 'c',
'on' => 'c.id_value = og.goods_id AND c.order_id = og.order_id '
]
],
];
$colls = Model\OrderGoods::find($params);
unset($params['limit'], $params['offset']);
$count = Model\OrderGoods::count($params);
foreach ($colls as $row) {
if (isset($orders[$row['order_id']])) {
$row['market_price'] = price_format($row['market_price'], false);
$row['goods_price'] = price_format($row['goods_price'], false);
$row['subtotal'] = price_format($row['subtotal'], false);
$orders[$row['order_id']]['goods'][] = $row;
}
}
}
$totalPage = $count ? intval(($count - 1) / $limit) + 1 : 1;
$data = [];
$data['user'] = $user;
$data['page'] = $page;
$data['totalPage'] = $totalPage;
$data['count'] = $count;
$data['orders'] = $orders;
$data['navName'] = '';
$data['userSidebar'] = 'bought-list';
echo $this->viewoutput('pc/bought-list', $data);
}
/**
* 我的收藏
*/
public function collectListAction()
{
global $db, $ecs;
$user = $this->auth->user();
$page = intval($this->request->getQuery('page')) ?: 1;
$ctype = $this->request->getQuery('collect_type') == 'brand' ? 'brand' : 'goods';
$limit = 10;
$offset = ($page - 1) * $limit;
$userid = $user['user_id'];
$data = [];
if ($ctype == 'goods') {
$params = [
'alias' => 'c',
'columns' => 'g.goods_id, g.goods_name, g.market_price, g.shop_price AS org_price, '.
"IFNULL(mp.user_price, g.shop_price * '$_SESSION[discount]') AS shop_price, g.goods_img, ".
'g.promote_price, g.promote_start_date,g.promote_end_date, c.rec_id, c.is_attention',
'conditions' => "c.user_id = '$userid' ORDER BY c.rec_id DESC",
'offset' => $offset,
'limit' => $limit,
'join' => [
[
'type' => 'LEFT',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = c.goods_id'
],
[
'type' => 'LEFT',
'table' => 'member_price',
'alias' => 'mp',
'on' => "mp.goods_id = g.goods_id AND mp.user_rank = '$_SESSION[user_rank]' "
],
],
];
$goodsList = Model\CollectGoods::find($params);
foreach ($goodsList as &$row) {
if ($row['promote_price'] > 0) {
$promote_price = bargain_price($row['promote_price'], $row['promote_start_date'], $row['promote_end_date']);
$row['promote_price'] = $promote_price > 0 ? price_format($promote_price) : '';
} else {
$row['promote_price'] = '';
}
if ($row['promote_price'] > 0) {
$row['price'] = $row['promote_price'];
} else {
$row['price'] = $row['shop_price'];
}
}
unset($row);
unset($params['limit'], $params['offset']);
$count = Model\CollectGoods::count($params);
$data['list'] = $goodsList;
} else {
$params = [
'alias' => 'cb',
'columns' => 'b.*',
'conditions' => "cb.user_id = '$userid'",
'offset' => $offset,
'limit' => $limit,
'order' => 'cb.id DESC',
'join' => [
[
'type' => 'LEFT',
'table' => 'brand',
'alias' => 'b',
'on' => 'b.brand_id = cb.brand_id'
]
],
];
$brandList = Model\CollectBrand::find($params);
foreach ($brandList as &$row) {
}
unset($row);
unset($params['limit'], $params['offset']);
$count = Model\CollectBrand::count($params);
$data['list'] = $brandList;
}
$paginator = new \Juice\Paginator(['type' => 'emptyArray', 'config' => [
'count' => $count,
'limit' => $limit,
'page' => $page,
]]);
$paginate = $paginator->getPaginate();
$data['user'] = $user;
$data['ctype'] = $ctype;
$data['paginate'] = $paginate;
$data['navName'] = '';
$data['userSidebar'] = 'collect-list';
echo $this->viewoutput('pc/collect-list', $data);
}
/**
* 评论商品
*/
public function commentAction()
{
global $db, $ecs;
$orderid = intval($this->request->getQuery('order_id'));
$goodsid = intval($this->request->getQuery('goods_id'));
if (! $orderid || ! $goodsid) {
return $this->response->redirect('user/bought/list');
}
$user = $this->auth->user();
$goods = Model\OrderGoods::findFirst([
'alias' => 'og',
'columns' => "og.rec_id, og.goods_id, og.goods_name, og.goods_attr, og.goods_sn, og.goods_number, goods_price, g.goods_thumb, o.add_time, o.order_id",
'conditions' => "og.order_id = $orderid AND og.goods_id = $goodsid AND o.user_id = " . $user['user_id'] . ' AND o.shipping_status = 2 AND c.comment_id IS NULL',
'join' => [
[
'type' => 'INNER',
'table' => 'order_info',
'alias' => 'o',
'on' => 'o.order_id = og.order_id'
],
[
'type' => 'INNER',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = og.goods_id'
],
[
'type' => 'LEFT',
'table' => 'comment',
'alias' => 'c',
'on' => 'c.order_id = og.order_id AND c.id_value = og.goods_id'
]
],
]);
if (empty($goods)) {
return $this->response->redirect('user/bought/list');
}
$goods['add_time_format'] = local_date('Y-m-d', $goods['add_time']);
$data = [];
$data['user'] = $user;
$data['goods'] = $goods;
$data['navName'] = '';
$data['userSidebar'] = 'bought-list';
echo $this->viewoutput('pc/comment', $data);
}
/**
* 订单列表
*/
public function orderListAction()
{
global $db, $ecs;
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'includes/lib_order.php');
$data = [];
$user = $this->auth->user();
$mcart = new Model\Cart;
$page = intval($this->request->getQuery('page')) ?: 1;
$limit = 10;
$offset = ($page - 1) * $limit;
$status = $this->request->getQuery('status') ?: 0;
$userid = $_SESSION['user_id'];
$morder = new Model\OrderInfo;
// 需要评论的order_id
$result = $morder->needComment();
$needCommentOrderids = [];
foreach ($result as $row) {
$needCommentOrderids[$row['order_id']] = $row['order_id'];
}
if (! empty($status)) {
$orderAttr = Model\OrderInfo::$orderStatus[$status];
}
$ossql = $sssql = $pssql = '';
if (! empty($orderAttr['os'])) {
$ossql = ' AND ' . db_create_in($orderAttr['os'], 'o.order_status');
}
if (! empty($orderAttr['ss'])) {
$sssql = ' AND ' . db_create_in($orderAttr['ss'], 'o.shipping_status');
}
if (! empty($orderAttr['ps'])) {
$pssql = ' AND ' . db_create_in($orderAttr['ps'], 'o.pay_status');
}
$conditions = '1 = 1';
if ($status == 4) {
if (empty($needCommentOrderids)) {
$conditions .= ' AND 1 = 2';
} else {
$conditions .= ' AND o.order_id IN (' . implode(',', $needCommentOrderids) . ')';
}
}
$conditions .= " AND o.user_id = '$userid' $ossql $sssql $pssql ORDER BY o.add_time DESC LIMIT $offset, $limit";
$colls = Model\OrderInfo::find([
'alias' => 'o',
'columns' => "o.order_id, o.order_sn, o.order_status, o.shipping_status, o.pay_status, o.add_time, " .
"(o.goods_amount + o.shipping_fee + o.insure_fee + o.pay_fee + o.pack_fee + o.card_fee + o.tax - o.discount) AS total_fee, " .
"do.delivery_id, do.delivery_sn"
,
'conditions' => $conditions,
'join' => [
[
'type' => 'LEFT',
'table' => 'delivery_order',
'alias' => 'do',
'on' => 'o.order_id = do.order_id'
]
],
]);
$orderids = [];
$orders = [];
foreach ($colls as $row) {
$orderids[] = $row['order_id'];
$row['shipping_status'] = ($row['shipping_status'] == SS_SHIPPED_ING) ? SS_PREPARING : $row['shipping_status'];
$row['status'] = Model\OrderInfo::getOrderStatus($row);
$orders[$row['order_id']] = [
'order_id' => $row['order_id'],
'status' => $row['status'],
'order_sn' => $row['order_sn'],
'order_time' => local_date($GLOBALS['_CFG']['time_format'], $row['add_time']),
'order_status' => $row['order_status'],
'shipping_status' => $row['shipping_status'],
'pay_status' => $row['pay_status'],
'total_fee' => price_format($row['total_fee'], false),
'delivery_id' => $row['delivery_id'],
'delivery_sn' => $row['delivery_sn'],
'needComment' => isset($needCommentOrderids[$row['order_id']]) ? true : false,
'goods' => [],
];
}
$statistics = $morder->getAllCount();
$count = empty($status) ? $statistics['total'] : $statistics['count'][$status];
$paginator = new \Juice\Paginator(['type' => 'emptyArray', 'config' => [
'count' => $count,
'limit' => $limit,
'page' => $page,
]]);
$paginate = $paginator->getPaginate();
$data['paginate'] = $paginate;
if ($orderids) {
$orderidsStr = implode(',', $orderids);
$params = [
'alias' => 'og',
'columns' => "og.rec_id, og.order_id, og.goods_id, og.goods_name, og.goods_sn, og.market_price, og.goods_number, " .
"og.goods_price, og.goods_attr, og.is_real, og.parent_id, og.is_gift, " .
"og.goods_price * og.goods_number AS subtotal, og.extension_code, g.goods_thumb, g.goods_img " .
',c.comment_id ',
'conditions' => "og.order_id IN ($orderidsStr)",
'join' => [
[
'type' => 'LEFT',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = og.goods_id'
],
[
'type' => 'LEFT',
'table' => 'comment',
'alias' => 'c',
'on' => 'c.id_value = og.goods_id AND c.order_id = og.order_id '
],
],
];
$colls = Model\OrderGoods::find($params);
foreach ($colls as $row) {
if (isset($orders[$row['order_id']])) {
$row['market_price'] = price_format($row['market_price'], false);
$row['goods_price'] = price_format($row['goods_price'], false);
$row['subtotal'] = price_format($row['subtotal'], false);
$orders[$row['order_id']]['goods'][] = $row;
}
}
}
$data['user'] = $user;
$data['orders'] = $orders;
$data['status'] = $status;
$data['statistics'] = $statistics;
$data['navName'] = '';
$data['userSidebar'] = 'order-list';
echo $this->viewoutput('pc/order-list', $data);
}
/**
* 地址
*/
public function addressListAction()
{
global $db, $ecs, $_CFG, $_LANG, $err;
$user = $this->auth->user();
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'languages/' .$_CFG['lang']. '/shopping_flow.php');
$addressList = Model\UserAddress::findByUserId($user['user_id']);
$regionsids = [];
foreach ($addressList as $region_id => $row) {
$row['province'] = isset($row['province']) ? intval($row['province']) : 0;
$row['city'] = isset($row['city']) ? intval($row['city']) : 0;
$row['district'] = isset($row['district']) ? intval($row['district']) : 0;
$regionsids[] = $row['district'];
$regionsids[] = $row['province'];
$regionsids[] = $row['city'];
}
$regions = [];
if ($regionsids) {
$idsstr = implode(', ', $regionsids);
$sql = 'SELECT region_id, region_name FROM ' . $GLOBALS['ecs']->table('region') .
" WHERE region_id IN ($idsstr)";
$res = $GLOBALS['db']->query($sql);
while ($row = $GLOBALS['db']->fetchRow($res)) {
$regions[$row['region_id']] = $row;
}
}
foreach ($addressList as $key => &$row) {
$row['province_name'] = isset($regions[$row['province']]) ? $regions[$row['province']]['region_name'] : '';
$row['city_name'] = isset($regions[$row['city']]) ? $regions[$row['city']]['region_name'] : '';
$row['district_name'] = isset($regions[$row['district']]) ? $regions[$row['district']]['region_name'] : '';
}
unset($row);
$status = $this->request->getQuery('status');
$_SESSION['origin_from'] = $status;
$regions = Model\Region::getAll();
$data = [];
$data['user'] = $user;
$data['addressList'] = $addressList;
$data['regions'] = $regions;
$data['navName'] = '';
$data['userSidebar'] = 'address-list';
echo $this->viewoutput('pc/address-list', $data);
}
/**
* 代理信息
*/
public function agentInfoAction()
{
global $db, $ecs, $_CFG, $_LANG, $err;
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'languages/' .$_CFG['lang']. '/shopping_flow.php');
$user = $this->auth->user();
$data = [];
$agent = Model\Agent::findFirstByAgentId($user['agent_id']);
if (empty($agent)) {
$data['isEdit'] = false;
} else {
$data['isEdit'] = true;
}
$data['user'] = $user;
$data['agent'] = $agent;
$data['navName'] = '';
$data['userSidebar'] = 'agent';
echo $this->viewoutput('pc/agent-info', $data);
}
/**
* 代理订单信息
*/
public function agentOrderListAction()
{
global $db, $ecs, $_CFG, $_LANG, $err;
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'languages/' .$_CFG['lang']. '/shopping_flow.php');
$user = $this->auth->user();
$page = intval($this->request->getQuery('page')) ?: 1;
$limit = 10;
$offset = ($page - 1) * $limit;
$ossql = $sssql = $pssql = '';
if (! empty($orderAttr['os'])) {
$ossql = ' AND ' . db_create_in($orderAttr['os'], 'order_status');
}
if (! empty($orderAttr['ss'])) {
$sssql = ' AND ' . db_create_in($orderAttr['ss'], 'shipping_status');
}
if (! empty($orderAttr['ps'])) {
$pssql = ' AND ' . db_create_in($orderAttr['ps'], 'pay_status');
}
$orderids = [];
$arr = [];
$count = 0;
if ($user['agent_id']) {
$params = [
'alias' => 'o',
'columns' => "o.order_id, o.order_sn, o.order_status, o.shipping_status, o.pay_status, o.add_time, " .
"(o.goods_amount + o.shipping_fee + o.insure_fee + o.pay_fee + o.pack_fee + o.card_fee + o.tax - o.discount) AS total_fee , u.user_name",
'conditions' => "o.agent_id = '{$user['agent_id']}' $ossql $sssql $pssql",
'offset' => $offset,
'limit' => $limit,
'join' => [
[
'type' => 'LEFT',
'table' => 'users',
'alias' => 'u',
'on' => 'u.user_id = o.user_id'
]
],
'order' => 'add_time DESC',
];
$colls = Model\OrderInfo::find($params);
foreach ($colls as $row)
{
$orderids[] = $row['order_id'];
$row['shipping_status'] = ($row['shipping_status'] == SS_SHIPPED_ING) ? SS_PREPARING : $row['shipping_status'];
$row['status'] = Model\OrderInfo::getOrderStatus($row);
$orderTime = local_date($GLOBALS['_CFG']['time_format'], $row['add_time']);
$dateArr = explode(' ', $orderTime);
$arr[$row['order_id']] = [
'order_id' => $row['order_id'],
'user_name' => $row['user_name'],
'status' => $row['status'],
'order_sn' => $row['order_sn'],
'order_time' => $orderTime,
'order_time_date' => $orderTime,
'order_time_arr' => $dateArr,
'order_status' => $row['order_status'],
'shipping_status' => $row['shipping_status'],
'pay_status' => $row['pay_status'],
'total_fee' => price_format($row['total_fee'], false),
'goods' => [],
];
}
unset($params['limit'], $params['offset']);
$count = Model\OrderInfo::count($params);
}
$paginator = new \Juice\Paginator(['type' => 'emptyArray', 'config' => [
'count' => $count,
'limit' => $limit,
'page' => $page,
]]);
$paginate = $paginator->getPaginate();
$orders = $arr;
if ($orderids) {
$orderidsStr = implode(',', $orderids);
$params = [
'alias' => 'og',
'columns' => "og.rec_id, og.order_id, og.goods_id, og.goods_name, og.goods_sn, og.market_price, og.goods_number, " .
"og.goods_price, og.goods_attr, og.is_real, og.parent_id, og.is_gift, " .
"og.goods_price * og.goods_number AS subtotal, og.extension_code, g.goods_thumb, g.goods_img " .
',c.comment_id ',
'conditions' => "og.order_id IN ($orderidsStr)",
'join' => [
[
'type' => 'LEFT',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = og.goods_id'
],
[
'type' => 'LEFT',
'table' => 'comment',
'alias' => 'c',
'on' => 'c.id_value = og.goods_id AND c.order_id = og.order_id '
]
],
];
$colls = Model\OrderGoods::find($params);
foreach ($colls as $row) {
if (isset($orders[$row['order_id']])) {
$row['market_price'] = price_format($row['market_price'], false);
$row['goods_price'] = price_format($row['goods_price'], false);
$row['subtotal'] = price_format($row['subtotal'], false);
$orders[$row['order_id']]['goods'][] = $row;
}
}
}
$data = [];
$data['user'] = $user;
$data['orders'] = $orders;
$data['paginate'] = $paginate;
$data['navName'] = '';
$data['userSidebar'] = 'agent-order-list';
echo $this->viewoutput('pc/agent-order-list', $data);
}
/**
* 绑定微信号
*/
public function wechatBindAction()
{
$user = $this->auth->user();
$wechat = new WechatSDK;
$url = 'http://www.liowang.com/shop/wechat/oauth2/callback/bind';
$state = ['env' => 'product'];
if (isLocal()) {
$state = ['env' => 'local'];
}
return $this->response->redirect($wechat->pcWebOauth2($url, $state), true);
}
/**
* PC 微信回调 oauth2 绑定已登录用户
*/
public function wechatOauth2CallbackBindAction()
{
global $ecs, $db, $user;
include_once(ROOT_PATH . 'includes/lib_passport.php');
$code = $this->request->getQuery('code');
if (empty($code)) {
echo '授权失败';
exit;
}
$state = $this->request->getQuery('state');
parse_str($state, $params);
if (! islocal() && ! empty($params['env']) && $params['env'] == 'local') {
return $this->response->redirect('http://liowang.dev.seafarer.me/shop/wechat/oauth2/callback/bind?' . http_build_query($this->request->getQuery()));
}
$wechat = new WechatSDK;
$config = $this->config->wechat;
$config->appid = $config->open_web_appid;
$config->appsecret = $config->open_web_secret;
$wechat->init($config);
$response = $wechat->getUserTokenByCode($code);
if (empty($response['errcode'])) {
// 授权成功
$ewxpcuser = Model\WxPcUser::findFirstByOpenid($response['openid']);
$data = [];
$isFirst = false;
$isNeedCreate = false;
$uuser = [];
$wxpcuser = [];
if ($ewxpcuser) {
// 用户已经存在
$euser = Model\User::findFirstByWxPcUserid($ewxpcuser['id']);
} else {
// 第一次访问
$isFirst = true;
$wxuser = Model\WxUser::findFirstByUnionid($response['unionid']);
if (empty($wxuser)) {
$isNeedCreate = true;
} else {
$euser = Model\User::findFirstByWxUserid($wxuser['id']);
}
}
$wxpcuser['openid'] = $response['openid'];
$wxpcuser['unionid'] = $response['unionid'];
$wxpcuser['access_token'] = $response['access_token'];
$wxpcuser['refresh_token'] = $response['refresh_token'];
$wxpcuser['expired_at'] = $response['expires_in'] + time() - 200;
$wxpcuser['scope'] = $response['scope'];
$wxpcuser['ip'] = $this->request->getClientAddress();
$info = $wechat->getUserInfoByOauth($response['openid'], $response['access_token']);
$wxpcuser['nickname'] = $info['nickname'];
$wxpcuser['gender'] = $info['sex'];
$wxpcuser['language'] = $info['language'];
$wxpcuser['city'] = $info['city'];
$wxpcuser['country'] = $info['country'];
$wxpcuser['province'] = $info['province'];
$wxpcuser['headimgurl'] = $info['headimgurl'];
if (! empty($euser) && $euser['user_id'] != $_SESSION['user_id']) {
exit('该微信已被绑定');
}
if ($isFirst) {
// 首次PC扫码登录
$id = Model\WxPcUser::insert($wxpcuser);
$uuser['wx_pc_userid'] = $id;
Model\User::update($uuser, "user_id = {$_SESSION['user_id']}");
} else {
$uuser['wx_pc_userid'] = $ewxpcuser['id'];
Model\User::update($uuser, "user_id = {$_SESSION['user_id']}");
Model\WxPcUser::update($wxpcuser, "openid = '{$response['openid']}'");
}
} else {
SystemException::error($response['errmsg']);
}
$referer = $this->url->get('user/info');
return $this->response->redirect($referer, true);
}
}
<?php
error_reporting(E_ALL);
try {
define('BASE_PATH', realpath('..') . '/');
define('APP_PATH', BASE_PATH . 'app/');
define('STORAGE_PATH', BASE_PATH . 'storage/');
// 系统环境变量,参考laravel模式
$systemEnvs = [
'local' => [
'juice-pc'
],
'production' => [
'10-10-92-111'
],
'testing' => [
'10-10-13-185'
],
];
/**
* 项目引导文件
*/
$config = include __DIR__ . "/../app/start/start.php";
/**
* Handle the request
*/
$application = new \Phalcon\Mvc\Application($di);
$application->useImplicitView(false); // 禁用自动渲染
echo $application->handle()->getContent();
} catch (\Exception $e) {
//echo $e;
throw $e;
Warning : Cannot modify header information - headers already sent by (output started at /usr/local/www/liowang-shop-robotwar/shop/app/libraries/PrettyExceptions/Library/Phalcon/Utils/PrettyExceptions.php:339) in /usr/local/www/liowang-shop-robotwar/shop/app/libraries/PrettyExceptions/Library/Phalcon/Utils/PrettyExceptions.php on line 440
Exception - Undefined index: access_token
Undefined index: access_token
/usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php (260)
#1 {closure} (8 , Undefined index: access_token , /usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php , 260 , Array(9) )/usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php (260)
<?php
/**
* weixin library
* @author geekzhumail@gmail.com
* @since 2014-09-24
* @edit 2015-03-05
*/
class SDKRuntimeException extends Exception {
public function errorMessage() {
return $this->getMessage();
}
}
class WechatSDK extends WechatBase{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => 'wxdf0a039cf4ea94d1',
'appsecret' => '7100f0fe837a5287f075b6d2d9814ca0',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/';
/**
* @var access_token
*/
protected $accessToken = '';
/**
* 是否从远程服务器拿token
*/
protected $isRemote = false;
public function __construct() {
parent::__construct();
$this->isRemote = $this->config->is_remote;
$this->accessToken = $this->getToken();
}
/**
* 初始化数据
*/
public function init($config) {
$this->config = $config;
}
/**
* 获取Appid
*/
public function getAppid() {
return $this->config['appid'];
}
/**
* weixin signature
*/
public function checkSignature($signature, $timestamp, $nonce) {
$token = $this->config['token'];
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr);
$tmpStr = implode( $tmpArr );
$tmpStr = sha1( $tmpStr );
if( $tmpStr == $signature ){
return true;
}else{
return false;
}
}
/**
* web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function webOauth2($redirectUri, $state = [], $type = 'snsapi_base') {
$params = [];
$params['appid'] = $this->config['appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = $type;
if (empty($state['type'])) {
$state['type'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* pc web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function pcWebOauth2($redirectUri, $state = []) {
$params = [];
$params['appid'] = $this->config['open_web_appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = 'snsapi_login';
if (empty($state['env'])) {
$state['env'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/qrconnect?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* get access token by code
*/
public function getUserTokenByCode($code) {
$params = array();
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$params['code'] = $code;
$params['grant_type'] = 'authorization_code';
$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* refresh access token
* @param string $token 填写通过access_token获取到的refresh_token参数
*/
public function refreshUserToken($token) {
$params = [];
$params['appid'] = $this->config['appid'];
$params['grant_type'] = 'refresh_token';
$params['refresh_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 根据网页授权拉取用户信息(需scope为 snsapi_userinfo)
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function getUserInfoByOauth($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('sns/userinfo', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 验证授权凭证(access_token)是否有效
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function validUserToken($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/auth?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 获取公众号token
*/
public function getToken() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
if ($this->isRemote) {
return $this->getTokenFromRemote();
}
$params = [];
$params['grant_type'] = 'client_credential';
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$url = 'https://api.weixin.qq.com/cgi-bin/token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
// 写入缓存
$expiredAt = $response['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $response['access_token'], $expiredAt);
return $response['access_token'];
}
/**
* 获取access_token from remote
*/
public function getTokenFromRemote() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/token/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$cache->save('wechat_access_token_expired', $resp['expired_at'], $expiredAt);
$expiredAt = 120;
$cache->save($key, $resp['access_token'], $expiredAt);
return $resp['access_token'];
} else {
throw new Exception('wx admin get token error');
}
}
/**
* 获取用户基本信息(包括UnionID机制)
* @param string $openid 用户的唯一标识
* @param string $token 调用接口凭证,默认值为Wechat::getToken()
*/
public function getUserInfo($openid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('cgi-bin/user/info', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 下载已经上传到微信的资源文件
*/
public function downloadMedia($mediaid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['access_token'] = $token;
$params['media_id'] = $mediaid;
$url = 'http://file.api.weixin.qq.com/cgi-bin/media/get?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => false,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$content = curl_exec($ch);
$info = curl_getinfo($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == '200') {
// 正常则分割header和body
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($content, 0, $headerSize);
$body = substr($content, $headerSize);
}
curl_close($ch);
$resp['info'] = $info;
$resp['header'] = $header;
$resp['body'] = $body;
return $resp;
}
/**
* 获取 js api ticket
*/
public function getJsapiTicket()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
if ($this->isRemote) {
return $this->getJsapiTicketFromRemote();
}
$resp = json_decode($this->get('cgi-bin/ticket/getticket', ['access_token' => $this->accessToken, 'type' => 'jsapi']), true);
// 写入缓存
$expiredAt = $resp['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
}
/**
* 获取 js api ticket
*/
public function getJsapiTicketFromRemote()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/jsticket/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$expiredAt = 120;
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
} else {
throw new Exception('wx admin get ticket error');
}
}
/**
* 获取js api 签名
*/
public function getSignPackage($url = '')
{
$jsticket = $this->getJsapiTicket();
if (! $url) {
$url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}
$timestamp = time();
$nonceStr = $this->createNoncestr(16);
// 这里参数的顺序要按照 key 值 ASCII 码升序排序
$string = "jsapi_ticket={$jsticket}&noncestr={$nonceStr}×tamp={$timestamp}&url={$url}";
return [
"appId" => $this->config['appid'],
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => sha1($string),
"rawString" => $string
];
}
/**
* 新建一个类,由于PSR-4无法获取不以文件名命名的类;
*/
public function newClass($name)
{
return new $name();
}
// 微信支付
/**
* 获取微信付款码的prepayid
* @params array $params 参数
*/
public function getPrepayId($params) {
$sdk = new UnifiedOrderSDK;
$sdk->setParameter("trade_type","JSAPI");//交易类型
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$prepayid = $sdk->getPrepayId();
return $prepayid;
}
/**
* 获取支付js api 签名
*/
public function getPaySignPackage($prepayid)
{
$jsApiObj["appId"] = $this->config['appid'];
$timeStamp = time();
$jsApiObj["timeStamp"] = "$timeStamp";
$jsApiObj["nonceStr"] = $this->createNoncestr();
$jsApiObj["package"] = "prepay_id=$prepayid";
$jsApiObj["signType"] = "MD5";
$jsApiObj["paySign"] = $this->getSign($jsApiObj);
$signPackage = $jsApiObj;
return $signPackage;
}
/**
* 发送红包接口
* @params array $params 参数
*/
public function sendRedPack($params) {
$sdk = new RedPackSDK;
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$result = $sdk->send();
return $result;
}
}
/**
* 所有接口的基类
*/
class WechatBase
{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => '',
'appsecret' => '',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/cgi-bin/';
public function __construct() {
$this->di = Phalcon\DI::getDefault();
$this->config = $this->di->getConfig()['wechat'];
}
protected function get($url, $params = '')
{
if (strpos($url, 'https://') === false && strpos($url, 'http://') === false) {
$url = $this->apiPrefix.$url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl get error');
}
}
protected function post($url, $params = '', $data = '')
{
if (strpos($url, 'https://') === false) {
$url = $this->apiPrefix . $url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
if ($data) {
$options[CURLOPT_POSTFIELDS] = json_encode($data, JSON_UNESCAPED_UNICODE);
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl post error');
}
}
public function trimString($value)
{
$ret = null;
if (null != $value)
{
$ret = $value;
if (strlen($ret) == 0)
{
$ret = null;
}
}
return $ret;
}
/**
* 作用:产生随机字符串,不长于32位
*/
public function createNoncestr( $length = 32, $type = 'all')
{
if ($type == 'all') {
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
} else if($type == 'number') {
$chars = "0123456789";
}
$str ="";
for ( $i = 0; $i < $length; $i++ ) {
$str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
/**
* 作用:格式化参数,签名过程需要使用
*/
public function formatBizQueryParaMap($paraMap, $urlencode)
{
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v)
{
if($urlencode)
{
$v = urlencode($v);
}
//$buff .= strtolower($k) . "=" . $v . "&";
$buff .= $k . "=" . $v . "&";
}
$reqPar;
if (strlen($buff) > 0)
{
$reqPar = substr($buff, 0, strlen($buff)-1);
}
return $reqPar;
}
/**
* 作用:生成签名
*/
public function getSign($Obj)
{
foreach ($Obj as $k => $v)
{
$Parameters[$k] = $v;
}
//签名步骤一:按字典序排序参数
ksort($Parameters);
$String = $this->formatBizQueryParaMap($Parameters, false);
//echo '【string1】'.$String.'</br>';
//签名步骤二:在string后加入KEY
$String = $String . "&key=" . $this->config['key'];
//echo "【string2】".$String."</br>";
//签名步骤三:MD5加密
$String = md5($String);
//echo "【string3】 ".$String."</br>";
//签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
//echo "【result】 ".$result_."</br>";
return $result_;
}
/**
* 作用:array转xml
*/
public function arrayToXml($arr)
{
$xml = "<xml>";
foreach ($arr as $key=>$val)
{
if (is_numeric($val))
{
$xml.="<".$key.">".$val."</".$key.">";
}
else
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
$xml.="</xml>";
return $xml;
}
/**
* 作用:将xml转为array
*/
public function xmlToArray($xml)
{
//将XML转为array
$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $array_data;
}
/**
* 作用:以post方式提交xml到对应的接口url
*/
public function postXmlCurl($xml,$url,$second=30)
{
//初始化curl
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
curl_close($ch);
//返回结果
if($data)
{
return $data;
}
else
{
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
return false;
}
}
/**
* 作用:使用证书,以post方式提交xml到对应的接口url
*/
public function postXmlSSLCurl($xml,$url,$second=30)
{
$ch = curl_init();
//超时时间
curl_setopt($ch,CURLOPT_TIMEOUT,$second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch,CURLOPT_HEADER,FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT, $this->config['sslcert_path']);
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY, $this->config['sslkey_path']);
//post提交方式
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
$data = curl_exec($ch);
//返回结果
if($data){
curl_close($ch);
return $data;
}
else {
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
curl_close($ch);
return false;
}
}
}
/**
* 请求型接口的基类
*/
class WechatPayRequestBase extends WechatBase
{
var $parameters;//请求参数,类型为关联数组
public $response;//微信返回的响应
public $result;//返回参数,类型为关联数组
var $url;//接口链接
var $curl_timeout;//curl超时时间
/**
* 作用:设置请求参数
*/
function setParameter($parameter, $parameterValue)
{
$this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 作用:设置标配的请求参数,生成签名,生成接口参数xml
*/
function createXml()
{
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 作用:post请求xml
*/
function postXml()
{
$xml = $this->createXml();
$this->response = $this->postXmlCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:使用证书post请求xml
*/
function postXmlSSL()
{
$xml = $this->createXml();
$this->response = $this->postXmlSSLCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:获取结果,默认不使用证书
*/
function getResult()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
return $this->result;
}
}
/**
* 统一支付接口类
*/
class UnifiedOrderSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
//检测必填参数
if(empty($this->parameters["out_trade_no"]))
{
throw new SDKRuntimeException("缺少统一支付接口必填参数out_trade_no!"."<br>");
}elseif(empty($this->parameters["body"])){
throw new SDKRuntimeException("缺少统一支付接口必填参数body!"."<br>");
}elseif (empty($this->parameters["total_fee"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数total_fee!"."<br>");
}elseif (empty($this->parameters["notify_url"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数notify_url!"."<br>");
}elseif (empty($this->parameters["trade_type"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数trade_type!"."<br>");
}elseif ($this->parameters["trade_type"] == "JSAPI" &&
empty($this->parameters["openid"])){
throw new SDKRuntimeException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!"."<br>");
}
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["spbill_create_ip"] = $this->di->getRequest()->getClientAddress();//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 获取prepay_id
*/
function getPrepayId()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
$prepay_id = $this->result["prepay_id"];
return $prepay_id;
}
}
/**
* 响应型接口基类
*/
class WechatPayServerBase extends WechatBase
{
public $data;//接收到的数据,类型为关联数组
var $returnParameters;//返回参数,类型为关联数组
/**
* 将微信的请求xml转换成关联数组,以方便数据处理
*/
function saveData($xml)
{
$this->data = $this->xmlToArray($xml);
}
function checkSign()
{
$tmpData = $this->data;
unset($tmpData['sign']);
$sign = $this->getSign($tmpData);//本地签名
if (isLocal()) {
echo $sign;
}
if ($this->data['sign'] == $sign) {
return TRUE;
}
return FALSE;
}
/**
* 获取微信的请求数据
*/
function getData()
{
return $this->data;
}
/**
* 设置返回微信的xml数据
*/
function setReturnParameter($parameter, $parameterValue)
{
$this->returnParameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 生成接口参数xml
*/
function createXml()
{
return $this->arrayToXml($this->returnParameters);
}
/**
* 将xml数据返回微信
*/
function returnXml()
{
$returnXml = $this->createXml();
return $returnXml;
}
}
/**
* 红包发送接口类
*/
class RedPackSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
$requiredParams = [
'mch_billno',
'nick_name',
'send_name',
're_openid',
'total_amount',
'min_value',
'max_value',
'total_num',
'wishing',
'act_name',
'remark',
];
//检测必填参数
foreach ($requiredParams as $val) {
if (empty($this->parameters[$val])) {
throw new SDKRuntimeException("缺少红包接口必填参数{$val}!"."<br>");
}
}
$this->parameters["wxappid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["client_ip"] = gethostbyname(gethostname());//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 发送红包
*
* curl 返回例子
* <xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[发放成功.]]></return_msg>
<result_code><![CDATA[SUCCESS]]></result_code>
<err_code><![CDATA[0]]></err_code>
<err_code_des><![CDATA[发放成功.]]></err_code_des>
<mch_billno><![CDATA[1228563102201503261427336509]]></mch_billno>
<mch_id>1228563102</mch_id>
<wxappid><![CDATA[wxdceccb9098a978b8]]></wxappid>
<re_openid><![CDATA[o55e0t1NHPxdcRCjbIu_xnjHc2u8]]></re_openid>
<total_amount>100</total_amount>
</xml>
*/
public function send() {
$this->postXmlSSL();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
return $this->result;
}
#2 WechatSDK ->getTokenFromRemote ()/usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php (209)
<?php
/**
* weixin library
* @author geekzhumail@gmail.com
* @since 2014-09-24
* @edit 2015-03-05
*/
class SDKRuntimeException extends Exception {
public function errorMessage() {
return $this->getMessage();
}
}
class WechatSDK extends WechatBase{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => 'wxdf0a039cf4ea94d1',
'appsecret' => '7100f0fe837a5287f075b6d2d9814ca0',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/';
/**
* @var access_token
*/
protected $accessToken = '';
/**
* 是否从远程服务器拿token
*/
protected $isRemote = false;
public function __construct() {
parent::__construct();
$this->isRemote = $this->config->is_remote;
$this->accessToken = $this->getToken();
}
/**
* 初始化数据
*/
public function init($config) {
$this->config = $config;
}
/**
* 获取Appid
*/
public function getAppid() {
return $this->config['appid'];
}
/**
* weixin signature
*/
public function checkSignature($signature, $timestamp, $nonce) {
$token = $this->config['token'];
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr);
$tmpStr = implode( $tmpArr );
$tmpStr = sha1( $tmpStr );
if( $tmpStr == $signature ){
return true;
}else{
return false;
}
}
/**
* web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function webOauth2($redirectUri, $state = [], $type = 'snsapi_base') {
$params = [];
$params['appid'] = $this->config['appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = $type;
if (empty($state['type'])) {
$state['type'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* pc web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function pcWebOauth2($redirectUri, $state = []) {
$params = [];
$params['appid'] = $this->config['open_web_appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = 'snsapi_login';
if (empty($state['env'])) {
$state['env'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/qrconnect?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* get access token by code
*/
public function getUserTokenByCode($code) {
$params = array();
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$params['code'] = $code;
$params['grant_type'] = 'authorization_code';
$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* refresh access token
* @param string $token 填写通过access_token获取到的refresh_token参数
*/
public function refreshUserToken($token) {
$params = [];
$params['appid'] = $this->config['appid'];
$params['grant_type'] = 'refresh_token';
$params['refresh_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 根据网页授权拉取用户信息(需scope为 snsapi_userinfo)
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function getUserInfoByOauth($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('sns/userinfo', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 验证授权凭证(access_token)是否有效
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function validUserToken($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/auth?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 获取公众号token
*/
public function getToken() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
if ($this->isRemote) {
return $this->getTokenFromRemote();
}
$params = [];
$params['grant_type'] = 'client_credential';
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$url = 'https://api.weixin.qq.com/cgi-bin/token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
// 写入缓存
$expiredAt = $response['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $response['access_token'], $expiredAt);
return $response['access_token'];
}
/**
* 获取access_token from remote
*/
public function getTokenFromRemote() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/token/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$cache->save('wechat_access_token_expired', $resp['expired_at'], $expiredAt);
$expiredAt = 120;
$cache->save($key, $resp['access_token'], $expiredAt);
return $resp['access_token'];
} else {
throw new Exception('wx admin get token error');
}
}
/**
* 获取用户基本信息(包括UnionID机制)
* @param string $openid 用户的唯一标识
* @param string $token 调用接口凭证,默认值为Wechat::getToken()
*/
public function getUserInfo($openid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('cgi-bin/user/info', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 下载已经上传到微信的资源文件
*/
public function downloadMedia($mediaid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['access_token'] = $token;
$params['media_id'] = $mediaid;
$url = 'http://file.api.weixin.qq.com/cgi-bin/media/get?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => false,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$content = curl_exec($ch);
$info = curl_getinfo($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == '200') {
// 正常则分割header和body
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($content, 0, $headerSize);
$body = substr($content, $headerSize);
}
curl_close($ch);
$resp['info'] = $info;
$resp['header'] = $header;
$resp['body'] = $body;
return $resp;
}
/**
* 获取 js api ticket
*/
public function getJsapiTicket()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
if ($this->isRemote) {
return $this->getJsapiTicketFromRemote();
}
$resp = json_decode($this->get('cgi-bin/ticket/getticket', ['access_token' => $this->accessToken, 'type' => 'jsapi']), true);
// 写入缓存
$expiredAt = $resp['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
}
/**
* 获取 js api ticket
*/
public function getJsapiTicketFromRemote()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/jsticket/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$expiredAt = 120;
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
} else {
throw new Exception('wx admin get ticket error');
}
}
/**
* 获取js api 签名
*/
public function getSignPackage($url = '')
{
$jsticket = $this->getJsapiTicket();
if (! $url) {
$url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}
$timestamp = time();
$nonceStr = $this->createNoncestr(16);
// 这里参数的顺序要按照 key 值 ASCII 码升序排序
$string = "jsapi_ticket={$jsticket}&noncestr={$nonceStr}×tamp={$timestamp}&url={$url}";
return [
"appId" => $this->config['appid'],
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => sha1($string),
"rawString" => $string
];
}
/**
* 新建一个类,由于PSR-4无法获取不以文件名命名的类;
*/
public function newClass($name)
{
return new $name();
}
// 微信支付
/**
* 获取微信付款码的prepayid
* @params array $params 参数
*/
public function getPrepayId($params) {
$sdk = new UnifiedOrderSDK;
$sdk->setParameter("trade_type","JSAPI");//交易类型
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$prepayid = $sdk->getPrepayId();
return $prepayid;
}
/**
* 获取支付js api 签名
*/
public function getPaySignPackage($prepayid)
{
$jsApiObj["appId"] = $this->config['appid'];
$timeStamp = time();
$jsApiObj["timeStamp"] = "$timeStamp";
$jsApiObj["nonceStr"] = $this->createNoncestr();
$jsApiObj["package"] = "prepay_id=$prepayid";
$jsApiObj["signType"] = "MD5";
$jsApiObj["paySign"] = $this->getSign($jsApiObj);
$signPackage = $jsApiObj;
return $signPackage;
}
/**
* 发送红包接口
* @params array $params 参数
*/
public function sendRedPack($params) {
$sdk = new RedPackSDK;
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$result = $sdk->send();
return $result;
}
}
/**
* 所有接口的基类
*/
class WechatBase
{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => '',
'appsecret' => '',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/cgi-bin/';
public function __construct() {
$this->di = Phalcon\DI::getDefault();
$this->config = $this->di->getConfig()['wechat'];
}
protected function get($url, $params = '')
{
if (strpos($url, 'https://') === false && strpos($url, 'http://') === false) {
$url = $this->apiPrefix.$url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl get error');
}
}
protected function post($url, $params = '', $data = '')
{
if (strpos($url, 'https://') === false) {
$url = $this->apiPrefix . $url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
if ($data) {
$options[CURLOPT_POSTFIELDS] = json_encode($data, JSON_UNESCAPED_UNICODE);
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl post error');
}
}
public function trimString($value)
{
$ret = null;
if (null != $value)
{
$ret = $value;
if (strlen($ret) == 0)
{
$ret = null;
}
}
return $ret;
}
/**
* 作用:产生随机字符串,不长于32位
*/
public function createNoncestr( $length = 32, $type = 'all')
{
if ($type == 'all') {
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
} else if($type == 'number') {
$chars = "0123456789";
}
$str ="";
for ( $i = 0; $i < $length; $i++ ) {
$str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
/**
* 作用:格式化参数,签名过程需要使用
*/
public function formatBizQueryParaMap($paraMap, $urlencode)
{
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v)
{
if($urlencode)
{
$v = urlencode($v);
}
//$buff .= strtolower($k) . "=" . $v . "&";
$buff .= $k . "=" . $v . "&";
}
$reqPar;
if (strlen($buff) > 0)
{
$reqPar = substr($buff, 0, strlen($buff)-1);
}
return $reqPar;
}
/**
* 作用:生成签名
*/
public function getSign($Obj)
{
foreach ($Obj as $k => $v)
{
$Parameters[$k] = $v;
}
//签名步骤一:按字典序排序参数
ksort($Parameters);
$String = $this->formatBizQueryParaMap($Parameters, false);
//echo '【string1】'.$String.'</br>';
//签名步骤二:在string后加入KEY
$String = $String . "&key=" . $this->config['key'];
//echo "【string2】".$String."</br>";
//签名步骤三:MD5加密
$String = md5($String);
//echo "【string3】 ".$String."</br>";
//签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
//echo "【result】 ".$result_."</br>";
return $result_;
}
/**
* 作用:array转xml
*/
public function arrayToXml($arr)
{
$xml = "<xml>";
foreach ($arr as $key=>$val)
{
if (is_numeric($val))
{
$xml.="<".$key.">".$val."</".$key.">";
}
else
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
$xml.="</xml>";
return $xml;
}
/**
* 作用:将xml转为array
*/
public function xmlToArray($xml)
{
//将XML转为array
$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $array_data;
}
/**
* 作用:以post方式提交xml到对应的接口url
*/
public function postXmlCurl($xml,$url,$second=30)
{
//初始化curl
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
curl_close($ch);
//返回结果
if($data)
{
return $data;
}
else
{
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
return false;
}
}
/**
* 作用:使用证书,以post方式提交xml到对应的接口url
*/
public function postXmlSSLCurl($xml,$url,$second=30)
{
$ch = curl_init();
//超时时间
curl_setopt($ch,CURLOPT_TIMEOUT,$second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch,CURLOPT_HEADER,FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT, $this->config['sslcert_path']);
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY, $this->config['sslkey_path']);
//post提交方式
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
$data = curl_exec($ch);
//返回结果
if($data){
curl_close($ch);
return $data;
}
else {
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
curl_close($ch);
return false;
}
}
}
/**
* 请求型接口的基类
*/
class WechatPayRequestBase extends WechatBase
{
var $parameters;//请求参数,类型为关联数组
public $response;//微信返回的响应
public $result;//返回参数,类型为关联数组
var $url;//接口链接
var $curl_timeout;//curl超时时间
/**
* 作用:设置请求参数
*/
function setParameter($parameter, $parameterValue)
{
$this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 作用:设置标配的请求参数,生成签名,生成接口参数xml
*/
function createXml()
{
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 作用:post请求xml
*/
function postXml()
{
$xml = $this->createXml();
$this->response = $this->postXmlCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:使用证书post请求xml
*/
function postXmlSSL()
{
$xml = $this->createXml();
$this->response = $this->postXmlSSLCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:获取结果,默认不使用证书
*/
function getResult()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
return $this->result;
}
}
/**
* 统一支付接口类
*/
class UnifiedOrderSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
//检测必填参数
if(empty($this->parameters["out_trade_no"]))
{
throw new SDKRuntimeException("缺少统一支付接口必填参数out_trade_no!"."<br>");
}elseif(empty($this->parameters["body"])){
throw new SDKRuntimeException("缺少统一支付接口必填参数body!"."<br>");
}elseif (empty($this->parameters["total_fee"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数total_fee!"."<br>");
}elseif (empty($this->parameters["notify_url"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数notify_url!"."<br>");
}elseif (empty($this->parameters["trade_type"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数trade_type!"."<br>");
}elseif ($this->parameters["trade_type"] == "JSAPI" &&
empty($this->parameters["openid"])){
throw new SDKRuntimeException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!"."<br>");
}
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["spbill_create_ip"] = $this->di->getRequest()->getClientAddress();//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 获取prepay_id
*/
function getPrepayId()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
$prepay_id = $this->result["prepay_id"];
return $prepay_id;
}
}
/**
* 响应型接口基类
*/
class WechatPayServerBase extends WechatBase
{
public $data;//接收到的数据,类型为关联数组
var $returnParameters;//返回参数,类型为关联数组
/**
* 将微信的请求xml转换成关联数组,以方便数据处理
*/
function saveData($xml)
{
$this->data = $this->xmlToArray($xml);
}
function checkSign()
{
$tmpData = $this->data;
unset($tmpData['sign']);
$sign = $this->getSign($tmpData);//本地签名
if (isLocal()) {
echo $sign;
}
if ($this->data['sign'] == $sign) {
return TRUE;
}
return FALSE;
}
/**
* 获取微信的请求数据
*/
function getData()
{
return $this->data;
}
/**
* 设置返回微信的xml数据
*/
function setReturnParameter($parameter, $parameterValue)
{
$this->returnParameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 生成接口参数xml
*/
function createXml()
{
return $this->arrayToXml($this->returnParameters);
}
/**
* 将xml数据返回微信
*/
function returnXml()
{
$returnXml = $this->createXml();
return $returnXml;
}
}
/**
* 红包发送接口类
*/
class RedPackSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
$requiredParams = [
'mch_billno',
'nick_name',
'send_name',
're_openid',
'total_amount',
'min_value',
'max_value',
'total_num',
'wishing',
'act_name',
'remark',
];
//检测必填参数
foreach ($requiredParams as $val) {
if (empty($this->parameters[$val])) {
throw new SDKRuntimeException("缺少红包接口必填参数{$val}!"."<br>");
}
}
$this->parameters["wxappid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["client_ip"] = gethostbyname(gethostname());//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 发送红包
*
* curl 返回例子
* <xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[发放成功.]]></return_msg>
<result_code><![CDATA[SUCCESS]]></result_code>
<err_code><![CDATA[0]]></err_code>
<err_code_des><![CDATA[发放成功.]]></err_code_des>
<mch_billno><![CDATA[1228563102201503261427336509]]></mch_billno>
<mch_id>1228563102</mch_id>
<wxappid><![CDATA[wxdceccb9098a978b8]]></wxappid>
<re_openid><![CDATA[o55e0t1NHPxdcRCjbIu_xnjHc2u8]]></re_openid>
<total_amount>100</total_amount>
</xml>
*/
public function send() {
$this->postXmlSSL();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
return $this->result;
}
#3 WechatSDK ->getToken ()/usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php (45)
<?php
/**
* weixin library
* @author geekzhumail@gmail.com
* @since 2014-09-24
* @edit 2015-03-05
*/
class SDKRuntimeException extends Exception {
public function errorMessage() {
return $this->getMessage();
}
}
class WechatSDK extends WechatBase{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => 'wxdf0a039cf4ea94d1',
'appsecret' => '7100f0fe837a5287f075b6d2d9814ca0',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/';
/**
* @var access_token
*/
protected $accessToken = '';
/**
* 是否从远程服务器拿token
*/
protected $isRemote = false;
public function __construct() {
parent::__construct();
$this->isRemote = $this->config->is_remote;
$this->accessToken = $this->getToken();
}
/**
* 初始化数据
*/
public function init($config) {
$this->config = $config;
}
/**
* 获取Appid
*/
public function getAppid() {
return $this->config['appid'];
}
/**
* weixin signature
*/
public function checkSignature($signature, $timestamp, $nonce) {
$token = $this->config['token'];
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr);
$tmpStr = implode( $tmpArr );
$tmpStr = sha1( $tmpStr );
if( $tmpStr == $signature ){
return true;
}else{
return false;
}
}
/**
* web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function webOauth2($redirectUri, $state = [], $type = 'snsapi_base') {
$params = [];
$params['appid'] = $this->config['appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = $type;
if (empty($state['type'])) {
$state['type'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* pc web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function pcWebOauth2($redirectUri, $state = []) {
$params = [];
$params['appid'] = $this->config['open_web_appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = 'snsapi_login';
if (empty($state['env'])) {
$state['env'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/qrconnect?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* get access token by code
*/
public function getUserTokenByCode($code) {
$params = array();
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$params['code'] = $code;
$params['grant_type'] = 'authorization_code';
$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* refresh access token
* @param string $token 填写通过access_token获取到的refresh_token参数
*/
public function refreshUserToken($token) {
$params = [];
$params['appid'] = $this->config['appid'];
$params['grant_type'] = 'refresh_token';
$params['refresh_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 根据网页授权拉取用户信息(需scope为 snsapi_userinfo)
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function getUserInfoByOauth($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('sns/userinfo', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 验证授权凭证(access_token)是否有效
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function validUserToken($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/auth?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 获取公众号token
*/
public function getToken() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
if ($this->isRemote) {
return $this->getTokenFromRemote();
}
$params = [];
$params['grant_type'] = 'client_credential';
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$url = 'https://api.weixin.qq.com/cgi-bin/token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
// 写入缓存
$expiredAt = $response['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $response['access_token'], $expiredAt);
return $response['access_token'];
}
/**
* 获取access_token from remote
*/
public function getTokenFromRemote() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/token/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$cache->save('wechat_access_token_expired', $resp['expired_at'], $expiredAt);
$expiredAt = 120;
$cache->save($key, $resp['access_token'], $expiredAt);
return $resp['access_token'];
} else {
throw new Exception('wx admin get token error');
}
}
/**
* 获取用户基本信息(包括UnionID机制)
* @param string $openid 用户的唯一标识
* @param string $token 调用接口凭证,默认值为Wechat::getToken()
*/
public function getUserInfo($openid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('cgi-bin/user/info', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 下载已经上传到微信的资源文件
*/
public function downloadMedia($mediaid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['access_token'] = $token;
$params['media_id'] = $mediaid;
$url = 'http://file.api.weixin.qq.com/cgi-bin/media/get?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => false,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$content = curl_exec($ch);
$info = curl_getinfo($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == '200') {
// 正常则分割header和body
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($content, 0, $headerSize);
$body = substr($content, $headerSize);
}
curl_close($ch);
$resp['info'] = $info;
$resp['header'] = $header;
$resp['body'] = $body;
return $resp;
}
/**
* 获取 js api ticket
*/
public function getJsapiTicket()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
if ($this->isRemote) {
return $this->getJsapiTicketFromRemote();
}
$resp = json_decode($this->get('cgi-bin/ticket/getticket', ['access_token' => $this->accessToken, 'type' => 'jsapi']), true);
// 写入缓存
$expiredAt = $resp['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
}
/**
* 获取 js api ticket
*/
public function getJsapiTicketFromRemote()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/jsticket/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$expiredAt = 120;
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
} else {
throw new Exception('wx admin get ticket error');
}
}
/**
* 获取js api 签名
*/
public function getSignPackage($url = '')
{
$jsticket = $this->getJsapiTicket();
if (! $url) {
$url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}
$timestamp = time();
$nonceStr = $this->createNoncestr(16);
// 这里参数的顺序要按照 key 值 ASCII 码升序排序
$string = "jsapi_ticket={$jsticket}&noncestr={$nonceStr}×tamp={$timestamp}&url={$url}";
return [
"appId" => $this->config['appid'],
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => sha1($string),
"rawString" => $string
];
}
/**
* 新建一个类,由于PSR-4无法获取不以文件名命名的类;
*/
public function newClass($name)
{
return new $name();
}
// 微信支付
/**
* 获取微信付款码的prepayid
* @params array $params 参数
*/
public function getPrepayId($params) {
$sdk = new UnifiedOrderSDK;
$sdk->setParameter("trade_type","JSAPI");//交易类型
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$prepayid = $sdk->getPrepayId();
return $prepayid;
}
/**
* 获取支付js api 签名
*/
public function getPaySignPackage($prepayid)
{
$jsApiObj["appId"] = $this->config['appid'];
$timeStamp = time();
$jsApiObj["timeStamp"] = "$timeStamp";
$jsApiObj["nonceStr"] = $this->createNoncestr();
$jsApiObj["package"] = "prepay_id=$prepayid";
$jsApiObj["signType"] = "MD5";
$jsApiObj["paySign"] = $this->getSign($jsApiObj);
$signPackage = $jsApiObj;
return $signPackage;
}
/**
* 发送红包接口
* @params array $params 参数
*/
public function sendRedPack($params) {
$sdk = new RedPackSDK;
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$result = $sdk->send();
return $result;
}
}
/**
* 所有接口的基类
*/
class WechatBase
{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => '',
'appsecret' => '',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/cgi-bin/';
public function __construct() {
$this->di = Phalcon\DI::getDefault();
$this->config = $this->di->getConfig()['wechat'];
}
protected function get($url, $params = '')
{
if (strpos($url, 'https://') === false && strpos($url, 'http://') === false) {
$url = $this->apiPrefix.$url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl get error');
}
}
protected function post($url, $params = '', $data = '')
{
if (strpos($url, 'https://') === false) {
$url = $this->apiPrefix . $url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
if ($data) {
$options[CURLOPT_POSTFIELDS] = json_encode($data, JSON_UNESCAPED_UNICODE);
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl post error');
}
}
public function trimString($value)
{
$ret = null;
if (null != $value)
{
$ret = $value;
if (strlen($ret) == 0)
{
$ret = null;
}
}
return $ret;
}
/**
* 作用:产生随机字符串,不长于32位
*/
public function createNoncestr( $length = 32, $type = 'all')
{
if ($type == 'all') {
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
} else if($type == 'number') {
$chars = "0123456789";
}
$str ="";
for ( $i = 0; $i < $length; $i++ ) {
$str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
/**
* 作用:格式化参数,签名过程需要使用
*/
public function formatBizQueryParaMap($paraMap, $urlencode)
{
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v)
{
if($urlencode)
{
$v = urlencode($v);
}
//$buff .= strtolower($k) . "=" . $v . "&";
$buff .= $k . "=" . $v . "&";
}
$reqPar;
if (strlen($buff) > 0)
{
$reqPar = substr($buff, 0, strlen($buff)-1);
}
return $reqPar;
}
/**
* 作用:生成签名
*/
public function getSign($Obj)
{
foreach ($Obj as $k => $v)
{
$Parameters[$k] = $v;
}
//签名步骤一:按字典序排序参数
ksort($Parameters);
$String = $this->formatBizQueryParaMap($Parameters, false);
//echo '【string1】'.$String.'</br>';
//签名步骤二:在string后加入KEY
$String = $String . "&key=" . $this->config['key'];
//echo "【string2】".$String."</br>";
//签名步骤三:MD5加密
$String = md5($String);
//echo "【string3】 ".$String."</br>";
//签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
//echo "【result】 ".$result_."</br>";
return $result_;
}
/**
* 作用:array转xml
*/
public function arrayToXml($arr)
{
$xml = "<xml>";
foreach ($arr as $key=>$val)
{
if (is_numeric($val))
{
$xml.="<".$key.">".$val."</".$key.">";
}
else
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
$xml.="</xml>";
return $xml;
}
/**
* 作用:将xml转为array
*/
public function xmlToArray($xml)
{
//将XML转为array
$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $array_data;
}
/**
* 作用:以post方式提交xml到对应的接口url
*/
public function postXmlCurl($xml,$url,$second=30)
{
//初始化curl
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
curl_close($ch);
//返回结果
if($data)
{
return $data;
}
else
{
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
return false;
}
}
/**
* 作用:使用证书,以post方式提交xml到对应的接口url
*/
public function postXmlSSLCurl($xml,$url,$second=30)
{
$ch = curl_init();
//超时时间
curl_setopt($ch,CURLOPT_TIMEOUT,$second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch,CURLOPT_HEADER,FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT, $this->config['sslcert_path']);
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY, $this->config['sslkey_path']);
//post提交方式
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
$data = curl_exec($ch);
//返回结果
if($data){
curl_close($ch);
return $data;
}
else {
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
curl_close($ch);
return false;
}
}
}
/**
* 请求型接口的基类
*/
class WechatPayRequestBase extends WechatBase
{
var $parameters;//请求参数,类型为关联数组
public $response;//微信返回的响应
public $result;//返回参数,类型为关联数组
var $url;//接口链接
var $curl_timeout;//curl超时时间
/**
* 作用:设置请求参数
*/
function setParameter($parameter, $parameterValue)
{
$this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 作用:设置标配的请求参数,生成签名,生成接口参数xml
*/
function createXml()
{
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 作用:post请求xml
*/
function postXml()
{
$xml = $this->createXml();
$this->response = $this->postXmlCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:使用证书post请求xml
*/
function postXmlSSL()
{
$xml = $this->createXml();
$this->response = $this->postXmlSSLCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:获取结果,默认不使用证书
*/
function getResult()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
return $this->result;
}
}
/**
* 统一支付接口类
*/
class UnifiedOrderSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
//检测必填参数
if(empty($this->parameters["out_trade_no"]))
{
throw new SDKRuntimeException("缺少统一支付接口必填参数out_trade_no!"."<br>");
}elseif(empty($this->parameters["body"])){
throw new SDKRuntimeException("缺少统一支付接口必填参数body!"."<br>");
}elseif (empty($this->parameters["total_fee"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数total_fee!"."<br>");
}elseif (empty($this->parameters["notify_url"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数notify_url!"."<br>");
}elseif (empty($this->parameters["trade_type"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数trade_type!"."<br>");
}elseif ($this->parameters["trade_type"] == "JSAPI" &&
empty($this->parameters["openid"])){
throw new SDKRuntimeException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!"."<br>");
}
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["spbill_create_ip"] = $this->di->getRequest()->getClientAddress();//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 获取prepay_id
*/
function getPrepayId()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
$prepay_id = $this->result["prepay_id"];
return $prepay_id;
}
}
/**
* 响应型接口基类
*/
class WechatPayServerBase extends WechatBase
{
public $data;//接收到的数据,类型为关联数组
var $returnParameters;//返回参数,类型为关联数组
/**
* 将微信的请求xml转换成关联数组,以方便数据处理
*/
function saveData($xml)
{
$this->data = $this->xmlToArray($xml);
}
function checkSign()
{
$tmpData = $this->data;
unset($tmpData['sign']);
$sign = $this->getSign($tmpData);//本地签名
if (isLocal()) {
echo $sign;
}
if ($this->data['sign'] == $sign) {
return TRUE;
}
return FALSE;
}
/**
* 获取微信的请求数据
*/
function getData()
{
return $this->data;
}
/**
* 设置返回微信的xml数据
*/
function setReturnParameter($parameter, $parameterValue)
{
$this->returnParameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 生成接口参数xml
*/
function createXml()
{
return $this->arrayToXml($this->returnParameters);
}
/**
* 将xml数据返回微信
*/
function returnXml()
{
$returnXml = $this->createXml();
return $returnXml;
}
}
/**
* 红包发送接口类
*/
class RedPackSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
$requiredParams = [
'mch_billno',
'nick_name',
'send_name',
're_openid',
'total_amount',
'min_value',
'max_value',
'total_num',
'wishing',
'act_name',
'remark',
];
//检测必填参数
foreach ($requiredParams as $val) {
if (empty($this->parameters[$val])) {
throw new SDKRuntimeException("缺少红包接口必填参数{$val}!"."<br>");
}
}
$this->parameters["wxappid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["client_ip"] = gethostbyname(gethostname());//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 发送红包
*
* curl 返回例子
* <xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[发放成功.]]></return_msg>
<result_code><![CDATA[SUCCESS]]></result_code>
<err_code><![CDATA[0]]></err_code>
<err_code_des><![CDATA[发放成功.]]></err_code_des>
<mch_billno><![CDATA[1228563102201503261427336509]]></mch_billno>
<mch_id>1228563102</mch_id>
<wxappid><![CDATA[wxdceccb9098a978b8]]></wxappid>
<re_openid><![CDATA[o55e0t1NHPxdcRCjbIu_xnjHc2u8]]></re_openid>
<total_amount>100</total_amount>
</xml>
*/
public function send() {
$this->postXmlSSL();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
return $this->result;
}
#4 WechatSDK ->__construct ()/usr/local/www/liowang-shop-robotwar/shop/app/controllers/PageUserController.php (24)
<?php
use HomeSpace\Model;
class PageUserController extends PageBaseController {
/**
* 登录
*/
public function loginAction()
{
$data = [];
//echo '登录';
$act = $this->request->getQuery('act');
if ($act == 'not_active') {
if (empty($_SESSION['tmp_email'])) {
$act = '';
}
$data['email'] = $_SESSION['tmp_email'];
}
$data['act'] = $act;
$data['errmsg'] = $this->flash->getMessages('error');
$this->flash->clear();
$wechat = new WechatSDK;
//$url = $this->url->get('wechat/oauth2/callback');
$url = 'http://www.liowang.com/shop/wechat/oauth2/callback';
$state = ['env' => 'product'];
if (isLocal()) {
$state = ['env' => 'local'];
}
$data['wechat_login_url'] = $wechat->pcWebOauth2($url, $state);
echo $this->viewoutput('pc/login', $data);
}
/**
* 登录 POST
*/
public function postLoginAction()
{
global $user, $_LANG;
$errmsg = '';
$account = $this->request->getPost('account');
$password = $this->request->getPost('password');
$remember = ! empty($this->request->getQuery('remember'));
if (filter_var($account, FILTER_VALIDATE_EMAIL)) {
// 邮箱登录
$muser = Model\User::findFirst(['columns' => 'user_name', 'conditions' => "email = '$account'"]);
} else if(is_numeric($account) && strlen($account) == 11) {
// 手机登录
$muser = Model\User::findFirst(['columns' => 'user_name', 'conditions' => "mobile_phone = '$account'"]);
}
if (! empty($muser) && ! empty($password)) {
if ($user->login($muser['user_name'], $password, $remember)) {
$row = Model\User::findFirstByUserId($_SESSION['user_id']);
if ($row['is_validated'] == 1) {
update_user_info();
recalculate_price();
$this->auth->loginUsingId($_SESSION['user_id']);
$_SESSION['pc_login'] = true;
return $this->response->redirect('home/index');
} else {
//$_SESSION['login_fail'] ++ ;
//$errmsg = '该账号尚未激活请查阅该账号邮箱的激活邮件,或者联系客服';
$_SESSION['tmp_email'] = $row['email'];
return $this->response->redirect('login?act=not_active');
}
} else {
$_SESSION['login_fail'] ++ ;
$errmsg = $_LANG['login_failure'];
}
}
$errmsg = $errmsg ?: '账号或者密码错误';
$this->flash->error($errmsg);
return $this->response->redirect('login');
}
/**
* 退出登录
*/
public function logoutAction()
{
global $user;
$user->logout();
return $this->response->redirect($this->request->getHTTPReferer(), true);
}
/**
* 邮箱注册验证
*/
public function emailValidateAction()
{
include_once(ROOT_PATH . 'includes/lib_passport.php');
$data = [];
$data['status'] = 'user_not_exist';
$hash = $this->request->getQuery('hash');
if ($hash) {
$id = intval(register_hash('decode', $hash));
$user = Model\User::findFirstByUserId($id);
if ($user) {
if ($user['is_validated'] == 0) {
Model\User::update(['is_validated' => 1], ['user_id' => $id]);
$data['status'] = 'success';
} else {
$data['status'] = 'validated';
}
$data['user'] = $user;
}
}
echo $this->viewoutput('pc/email-validate', $data);
}
/**
* PC 微信回调 oauth2 函数
*/
public function wechatOauth2CallbackAction()
{
global $ecs, $db, $user;
include_once(ROOT_PATH . 'includes/lib_passport.php');
$code = $this->request->getQuery('code');
if (empty($code)) {
echo '授权失败';
exit;
}
$state = $this->request->getQuery('state');
parse_str($state, $params);
if (! islocal() && ! empty($params['env']) && $params['env'] == 'local') {
return $this->response->redirect('http://liowang.dev.seafarer.me/shop/wechat/oauth2/callback?' . http_build_query($this->request->getQuery()));
}
$wechat = new WechatSDK;
$config = $this->config->wechat;
$config->appid = $config->open_web_appid;
$config->appsecret = $config->open_web_secret;
$wechat->init($config);
$response = $wechat->getUserTokenByCode($code);
if (empty($response['errcode'])) {
// 授权成功
$ewxpcuser = Model\WxPcUser::findFirstByOpenid($response['openid']);
$data = [];
$isFirst = false;
$isNeedCreate = false;
$uuser = [];
$wxpcuser = [];
if ($ewxpcuser) {
// 用户已经存在
$euser = Model\User::findFirstByWxPcUserid($ewxpcuser['id']);
} else {
// 第一次访问
$isFirst = true;
$wxuser = Model\WxUser::findFirstByUnionid($response['unionid']);
if (empty($wxuser)) {
$isNeedCreate = true;
} else {
$euser = Model\User::findFirstByWxUserid($wxuser['id']);
}
}
$wxpcuser['openid'] = $response['openid'];
$wxpcuser['unionid'] = $response['unionid'];
$wxpcuser['access_token'] = $response['access_token'];
$wxpcuser['refresh_token'] = $response['refresh_token'];
$wxpcuser['expired_at'] = $response['expires_in'] + time() - 200;
$wxpcuser['scope'] = $response['scope'];
$wxpcuser['ip'] = $this->request->getClientAddress();
$info = $wechat->getUserInfoByOauth($response['openid'], $response['access_token']);
$wxpcuser['nickname'] = $info['nickname'];
$wxpcuser['gender'] = $info['sex'];
$wxpcuser['language'] = $info['language'];
$wxpcuser['city'] = $info['city'];
$wxpcuser['country'] = $info['country'];
$wxpcuser['province'] = $info['province'];
$wxpcuser['headimgurl'] = $info['headimgurl'];
if ($isFirst) {
// 首次PC扫码登录
$id = Model\WxPcUser::insert($wxpcuser);
$_SESSION['wx_pc_user_id'] = $id;
$uuser['wx_pc_userid'] = $id;
if (! $isNeedCreate) {
Model\User::update($uuser, "user_id = {$euser['user_id']}");
}
/**
if ($isNeedCreate) {
// 创建新用户
$uuser['avatar'] = 'shop/assets/pc/IMGS/defaultPhoto.png';
$name = '会员_' . time() . '_' . $id;
$uuser['nickname'] = $wxpcuser['nickname'];
$email = 'email_' . time() . '_' . $id . '@liowang.com';
if (register($name, '123123', $email) !== false) {;
$db->autoExecute($ecs->table('users'), $uuser, 'UPDATE', "user_id = {$_SESSION['user_id']}");
} else {
SystemException::error('授权失败');
}
} else {
// 绑定已在微信授权的用户
Model\User::update($uuser, "user_id = {$euser['user_id']}");
Model\WxPcUser::update($wxpcuser, "openid = '{$response['openid']}'");
$user->set_session($euser['user_name']);
$user->set_cookie($euser['user_name']);
update_user_info();
recalculate_price();
}**/
} else {
Model\WxPcUser::update($wxpcuser, "openid = '{$response['openid']}'");
$_SESSION['wx_pc_user_id'] = $ewxpcuser['id'];
}
if (empty($euser)) {
return $this->response->redirect('wechat/oauth2/register');
}
// 已注册直接登录
$user->set_session($euser['user_name']);
$user->set_cookie($euser['user_name']);
update_user_info();
recalculate_price();
$this->auth->loginUsingId($_SESSION['user_id']);
$_SESSION['pc_login'] = true;
} else {
SystemException::error($response['errmsg']);
}
$referer = $this->url->get('');
return $this->response->redirect($referer, true);
}
/**
* PC 微信账号登录绑定
*/
public function wechatOauth2RegisterAction()
{
global $ecs, $db, $user;
if (empty($_SESSION['wx_pc_user_id'])) {
return $this->response->redirect('login');
}
$data = [];
echo $this->viewoutput('pc/wechat-oauth2-register', $data);
}
/******** 需要登录授权 *********/
/**
* 个人信息
*/
public function infoAction()
{
$user = $this->auth->user();
list($user['birYear'], $user['birMonth'], $user['birDay']) = explode('-', $user['birthday']);
$user['birMonth'] = ltrim($user['birMonth'], 0);
$user['birDay'] = ltrim($user['birDay'], 0);
if (strpos($user['avatar'], 'http://') === false && strpos($user['avatar'], 'https://') === false) {
$user['avatar'] = 'http://' . $this->request->getHttpHost() . '/' . $user['avatar'];
}
$data = [];
$data['user'] = $user;
$data['navName'] = '';
$data['userSidebar'] = 'user-info';
echo $this->viewoutput('pc/user-info', $data);
}
/**
* 购物车
*/
public function cartAction()
{
require(ROOT_PATH . 'includes/lib_order.php');
$user = $this->auth->user();
$mcart = new Model\Cart;
$cartCount = $mcart->getCount();
$cartGoods = get_cart_goods();
$discount = compute_discount();
$goodsids = [];
foreach ($cartGoods['goods_list'] as $goods) {
$goodsids[] = $goods['goods_id'];
}
if ($goodsids) {
$collectGoodsList = Model\CollectGoods::find(['conditions' => "user_id='$_SESSION[user_id]' AND " . db_create_in($goodsids, 'goods_id')]);
foreach ($cartGoods['goods_list'] as &$goods) {
$goods['is_collect'] = false;
foreach ($collectGoodsList as $row) {
if ($row['goods_id'] == $goods['goods_id']) {
$goods['is_collect'] = true;
break;
}
}
}
unset($goods);
}
$data = [];
$data['user'] = $user;
$data['cartGoods'] = $cartGoods;
$data['navName'] = '';
echo $this->viewoutput('pc/user-cart', $data);
}
/**
* 购物车结算
*/
public function cartSettleAction()
{
global $db, $ecs, $_CFG, $_LANG, $err;
require(ROOT_PATH . 'includes/lib_order.php');
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'languages/' .$_CFG['lang']. '/shopping_flow.php');
$user = $this->auth->user();
$recids = $this->request->getQuery('rec_ids') ?: [];
$cartGoods = get_cart_goods($recids);
if (empty($cartGoods['goods_list'])) {
// 购物车为空
return $this->response->redirect('user/cart');
}
$discount = compute_discount();
$data = [];
$address_id = intval($this->request->getQuery('address_id'));
if ($address_id == 0) {
$address_id = $user['address_id'];
}
$address = Model\UserAddress::findFirst(['conditions' => "address_id = $address_id AND user_id = {$user['user_id']}"]);
$addressList = Model\UserAddress::find(['conditions' => "address_id != $address_id AND user_id = {$user['user_id']}", 'order' => 'address_id DESC']);
if ($address) {
array_unshift($addressList, $address);
}
$regions = Model\Region::getAll();
foreach ($addressList as &$row) {
$country = $regions[$row['country']];
$province = $country['list'][$row['province']];
$city = $province['list'][$row['city']];
$district = $city['list'][$row['district']];
$row['address_detail'] = $province['region_name'] . $city['region_name'] . $district['region_name'] . $row['address'];
}
unset($row);
$data['addressList'] = $addressList;
foreach ($cartGoods['goods_list'] as &$goods) {
//$goods['goods_name'] = mb_substr($goods['goods_name'], 0, 15, 'utf-8') . '......';
}
unset($goods);
$regions = Model\Region::getAll();
$data['user'] = $user;
$data['regions'] = $regions;
$data['cartGoods'] = $cartGoods;
$data['navName'] = '';
echo $this->viewoutput('pc/cart-settle', $data);
}
/**
* 已购买的商品
*/
public function boughtListAction()
{
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'includes/lib_order.php');
$user = $this->auth->user();
$mcart = new Model\Cart;
global $db, $ecs;
$page = intval($this->request->getQuery('page')) ?: 1;
$limit = 10;
$offset = ($page - 1) * $limit;
$userid = $_SESSION['user_id'];
$morder = new Model\OrderInfo;
$arr = array();
$osArr = [];
$ssArr = [];
$psArr = [];
// 需要评论的order_id
$result = $morder->needComment();
$needCommentOrderids = [];
foreach ($result as $row) {
$needCommentOrderids[$row['order_id']] = $row['order_id'];
}
$osArr[] = OS_CONFIRMED;
$osArr[] = OS_SPLITED;
$ssArr[] = SS_RECEIVED;
$psArr[] = PS_PAYED;
$ossql = $sssql = $pssql = '';
if ($osArr) {
$ossql = ' AND ' . db_create_in($osArr, 'order_status');
}
if ($ssArr) {
$sssql = ' AND ' . db_create_in($ssArr, 'shipping_status');
}
if ($psArr) {
$pssql = ' AND ' . db_create_in($psArr, 'pay_status');
}
$sql = "SELECT order_id, order_sn, order_status, shipping_status, pay_status, add_time, " .
"(goods_amount + shipping_fee + insure_fee + pay_fee + pack_fee + card_fee + tax - discount) AS total_fee ".
" FROM " .$GLOBALS['ecs']->table('order_info') .
' WHERE 1 = 1';
$sql .= " AND user_id = '$userid' $ossql $sssql $pssql ORDER BY add_time DESC";
//$res = $GLOBALS['db']->SelectLimit($sql, 20, ($page - 1) * 20);
$res = $GLOBALS['db']->query($sql);
$orderids = [];
while ($row = $GLOBALS['db']->fetchRow($res))
{
$orderids[] = $row['order_id'];
$row['shipping_status'] = ($row['shipping_status'] == SS_SHIPPED_ING) ? SS_PREPARING : $row['shipping_status'];
$arr[$row['order_id']] = array('order_id' => $row['order_id'],
'order_sn' => $row['order_sn'],
'order_time' => local_date($GLOBALS['_CFG']['time_format'], $row['add_time']),
'order_status' => $row['order_status'],
'shipping_status' => $row['shipping_status'],
'pay_status' => $row['pay_status'],
'total_fee' => price_format($row['total_fee'], false),
'needComment' => isset($needCommentOrderids[$row['order_id']]) ? true : false,
'goods' => [],
);
}
$orders = $arr;
$count = 0;
if ($orderids) {
$orderidsStr = implode(',', $orderids);
/* 订单商品 */
$params = [
'alias' => 'og',
'columns' => "og.rec_id, og.order_id, og.goods_id, og.goods_name, og.goods_sn, og.market_price, og.goods_number, " .
"og.goods_price, og.goods_attr, og.is_real, og.parent_id, og.is_gift, " .
"og.goods_price * og.goods_number AS subtotal, og.extension_code, g.goods_thumb, g.goods_img " .
',c.comment_id ',
'conditions' => "og.order_id IN ($orderidsStr)",
'offset' => $offset,
'limit' => $limit,
'join' => [
[
'type' => 'LEFT',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = og.goods_id'
],
[
'type' => 'LEFT',
'table' => 'comment',
'alias' => 'c',
'on' => 'c.id_value = og.goods_id AND c.order_id = og.order_id '
]
],
];
$colls = Model\OrderGoods::find($params);
unset($params['limit'], $params['offset']);
$count = Model\OrderGoods::count($params);
foreach ($colls as $row) {
if (isset($orders[$row['order_id']])) {
$row['market_price'] = price_format($row['market_price'], false);
$row['goods_price'] = price_format($row['goods_price'], false);
$row['subtotal'] = price_format($row['subtotal'], false);
$orders[$row['order_id']]['goods'][] = $row;
}
}
}
$totalPage = $count ? intval(($count - 1) / $limit) + 1 : 1;
$data = [];
$data['user'] = $user;
$data['page'] = $page;
$data['totalPage'] = $totalPage;
$data['count'] = $count;
$data['orders'] = $orders;
$data['navName'] = '';
$data['userSidebar'] = 'bought-list';
echo $this->viewoutput('pc/bought-list', $data);
}
/**
* 我的收藏
*/
public function collectListAction()
{
global $db, $ecs;
$user = $this->auth->user();
$page = intval($this->request->getQuery('page')) ?: 1;
$ctype = $this->request->getQuery('collect_type') == 'brand' ? 'brand' : 'goods';
$limit = 10;
$offset = ($page - 1) * $limit;
$userid = $user['user_id'];
$data = [];
if ($ctype == 'goods') {
$params = [
'alias' => 'c',
'columns' => 'g.goods_id, g.goods_name, g.market_price, g.shop_price AS org_price, '.
"IFNULL(mp.user_price, g.shop_price * '$_SESSION[discount]') AS shop_price, g.goods_img, ".
'g.promote_price, g.promote_start_date,g.promote_end_date, c.rec_id, c.is_attention',
'conditions' => "c.user_id = '$userid' ORDER BY c.rec_id DESC",
'offset' => $offset,
'limit' => $limit,
'join' => [
[
'type' => 'LEFT',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = c.goods_id'
],
[
'type' => 'LEFT',
'table' => 'member_price',
'alias' => 'mp',
'on' => "mp.goods_id = g.goods_id AND mp.user_rank = '$_SESSION[user_rank]' "
],
],
];
$goodsList = Model\CollectGoods::find($params);
foreach ($goodsList as &$row) {
if ($row['promote_price'] > 0) {
$promote_price = bargain_price($row['promote_price'], $row['promote_start_date'], $row['promote_end_date']);
$row['promote_price'] = $promote_price > 0 ? price_format($promote_price) : '';
} else {
$row['promote_price'] = '';
}
if ($row['promote_price'] > 0) {
$row['price'] = $row['promote_price'];
} else {
$row['price'] = $row['shop_price'];
}
}
unset($row);
unset($params['limit'], $params['offset']);
$count = Model\CollectGoods::count($params);
$data['list'] = $goodsList;
} else {
$params = [
'alias' => 'cb',
'columns' => 'b.*',
'conditions' => "cb.user_id = '$userid'",
'offset' => $offset,
'limit' => $limit,
'order' => 'cb.id DESC',
'join' => [
[
'type' => 'LEFT',
'table' => 'brand',
'alias' => 'b',
'on' => 'b.brand_id = cb.brand_id'
]
],
];
$brandList = Model\CollectBrand::find($params);
foreach ($brandList as &$row) {
}
unset($row);
unset($params['limit'], $params['offset']);
$count = Model\CollectBrand::count($params);
$data['list'] = $brandList;
}
$paginator = new \Juice\Paginator(['type' => 'emptyArray', 'config' => [
'count' => $count,
'limit' => $limit,
'page' => $page,
]]);
$paginate = $paginator->getPaginate();
$data['user'] = $user;
$data['ctype'] = $ctype;
$data['paginate'] = $paginate;
$data['navName'] = '';
$data['userSidebar'] = 'collect-list';
echo $this->viewoutput('pc/collect-list', $data);
}
/**
* 评论商品
*/
public function commentAction()
{
global $db, $ecs;
$orderid = intval($this->request->getQuery('order_id'));
$goodsid = intval($this->request->getQuery('goods_id'));
if (! $orderid || ! $goodsid) {
return $this->response->redirect('user/bought/list');
}
$user = $this->auth->user();
$goods = Model\OrderGoods::findFirst([
'alias' => 'og',
'columns' => "og.rec_id, og.goods_id, og.goods_name, og.goods_attr, og.goods_sn, og.goods_number, goods_price, g.goods_thumb, o.add_time, o.order_id",
'conditions' => "og.order_id = $orderid AND og.goods_id = $goodsid AND o.user_id = " . $user['user_id'] . ' AND o.shipping_status = 2 AND c.comment_id IS NULL',
'join' => [
[
'type' => 'INNER',
'table' => 'order_info',
'alias' => 'o',
'on' => 'o.order_id = og.order_id'
],
[
'type' => 'INNER',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = og.goods_id'
],
[
'type' => 'LEFT',
'table' => 'comment',
'alias' => 'c',
'on' => 'c.order_id = og.order_id AND c.id_value = og.goods_id'
]
],
]);
if (empty($goods)) {
return $this->response->redirect('user/bought/list');
}
$goods['add_time_format'] = local_date('Y-m-d', $goods['add_time']);
$data = [];
$data['user'] = $user;
$data['goods'] = $goods;
$data['navName'] = '';
$data['userSidebar'] = 'bought-list';
echo $this->viewoutput('pc/comment', $data);
}
/**
* 订单列表
*/
public function orderListAction()
{
global $db, $ecs;
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'includes/lib_order.php');
$data = [];
$user = $this->auth->user();
$mcart = new Model\Cart;
$page = intval($this->request->getQuery('page')) ?: 1;
$limit = 10;
$offset = ($page - 1) * $limit;
$status = $this->request->getQuery('status') ?: 0;
$userid = $_SESSION['user_id'];
$morder = new Model\OrderInfo;
// 需要评论的order_id
$result = $morder->needComment();
$needCommentOrderids = [];
foreach ($result as $row) {
$needCommentOrderids[$row['order_id']] = $row['order_id'];
}
if (! empty($status)) {
$orderAttr = Model\OrderInfo::$orderStatus[$status];
}
$ossql = $sssql = $pssql = '';
if (! empty($orderAttr['os'])) {
$ossql = ' AND ' . db_create_in($orderAttr['os'], 'o.order_status');
}
if (! empty($orderAttr['ss'])) {
$sssql = ' AND ' . db_create_in($orderAttr['ss'], 'o.shipping_status');
}
if (! empty($orderAttr['ps'])) {
$pssql = ' AND ' . db_create_in($orderAttr['ps'], 'o.pay_status');
}
$conditions = '1 = 1';
if ($status == 4) {
if (empty($needCommentOrderids)) {
$conditions .= ' AND 1 = 2';
} else {
$conditions .= ' AND o.order_id IN (' . implode(',', $needCommentOrderids) . ')';
}
}
$conditions .= " AND o.user_id = '$userid' $ossql $sssql $pssql ORDER BY o.add_time DESC LIMIT $offset, $limit";
$colls = Model\OrderInfo::find([
'alias' => 'o',
'columns' => "o.order_id, o.order_sn, o.order_status, o.shipping_status, o.pay_status, o.add_time, " .
"(o.goods_amount + o.shipping_fee + o.insure_fee + o.pay_fee + o.pack_fee + o.card_fee + o.tax - o.discount) AS total_fee, " .
"do.delivery_id, do.delivery_sn"
,
'conditions' => $conditions,
'join' => [
[
'type' => 'LEFT',
'table' => 'delivery_order',
'alias' => 'do',
'on' => 'o.order_id = do.order_id'
]
],
]);
$orderids = [];
$orders = [];
foreach ($colls as $row) {
$orderids[] = $row['order_id'];
$row['shipping_status'] = ($row['shipping_status'] == SS_SHIPPED_ING) ? SS_PREPARING : $row['shipping_status'];
$row['status'] = Model\OrderInfo::getOrderStatus($row);
$orders[$row['order_id']] = [
'order_id' => $row['order_id'],
'status' => $row['status'],
'order_sn' => $row['order_sn'],
'order_time' => local_date($GLOBALS['_CFG']['time_format'], $row['add_time']),
'order_status' => $row['order_status'],
'shipping_status' => $row['shipping_status'],
'pay_status' => $row['pay_status'],
'total_fee' => price_format($row['total_fee'], false),
'delivery_id' => $row['delivery_id'],
'delivery_sn' => $row['delivery_sn'],
'needComment' => isset($needCommentOrderids[$row['order_id']]) ? true : false,
'goods' => [],
];
}
$statistics = $morder->getAllCount();
$count = empty($status) ? $statistics['total'] : $statistics['count'][$status];
$paginator = new \Juice\Paginator(['type' => 'emptyArray', 'config' => [
'count' => $count,
'limit' => $limit,
'page' => $page,
]]);
$paginate = $paginator->getPaginate();
$data['paginate'] = $paginate;
if ($orderids) {
$orderidsStr = implode(',', $orderids);
$params = [
'alias' => 'og',
'columns' => "og.rec_id, og.order_id, og.goods_id, og.goods_name, og.goods_sn, og.market_price, og.goods_number, " .
"og.goods_price, og.goods_attr, og.is_real, og.parent_id, og.is_gift, " .
"og.goods_price * og.goods_number AS subtotal, og.extension_code, g.goods_thumb, g.goods_img " .
',c.comment_id ',
'conditions' => "og.order_id IN ($orderidsStr)",
'join' => [
[
'type' => 'LEFT',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = og.goods_id'
],
[
'type' => 'LEFT',
'table' => 'comment',
'alias' => 'c',
'on' => 'c.id_value = og.goods_id AND c.order_id = og.order_id '
],
],
];
$colls = Model\OrderGoods::find($params);
foreach ($colls as $row) {
if (isset($orders[$row['order_id']])) {
$row['market_price'] = price_format($row['market_price'], false);
$row['goods_price'] = price_format($row['goods_price'], false);
$row['subtotal'] = price_format($row['subtotal'], false);
$orders[$row['order_id']]['goods'][] = $row;
}
}
}
$data['user'] = $user;
$data['orders'] = $orders;
$data['status'] = $status;
$data['statistics'] = $statistics;
$data['navName'] = '';
$data['userSidebar'] = 'order-list';
echo $this->viewoutput('pc/order-list', $data);
}
/**
* 地址
*/
public function addressListAction()
{
global $db, $ecs, $_CFG, $_LANG, $err;
$user = $this->auth->user();
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'languages/' .$_CFG['lang']. '/shopping_flow.php');
$addressList = Model\UserAddress::findByUserId($user['user_id']);
$regionsids = [];
foreach ($addressList as $region_id => $row) {
$row['province'] = isset($row['province']) ? intval($row['province']) : 0;
$row['city'] = isset($row['city']) ? intval($row['city']) : 0;
$row['district'] = isset($row['district']) ? intval($row['district']) : 0;
$regionsids[] = $row['district'];
$regionsids[] = $row['province'];
$regionsids[] = $row['city'];
}
$regions = [];
if ($regionsids) {
$idsstr = implode(', ', $regionsids);
$sql = 'SELECT region_id, region_name FROM ' . $GLOBALS['ecs']->table('region') .
" WHERE region_id IN ($idsstr)";
$res = $GLOBALS['db']->query($sql);
while ($row = $GLOBALS['db']->fetchRow($res)) {
$regions[$row['region_id']] = $row;
}
}
foreach ($addressList as $key => &$row) {
$row['province_name'] = isset($regions[$row['province']]) ? $regions[$row['province']]['region_name'] : '';
$row['city_name'] = isset($regions[$row['city']]) ? $regions[$row['city']]['region_name'] : '';
$row['district_name'] = isset($regions[$row['district']]) ? $regions[$row['district']]['region_name'] : '';
}
unset($row);
$status = $this->request->getQuery('status');
$_SESSION['origin_from'] = $status;
$regions = Model\Region::getAll();
$data = [];
$data['user'] = $user;
$data['addressList'] = $addressList;
$data['regions'] = $regions;
$data['navName'] = '';
$data['userSidebar'] = 'address-list';
echo $this->viewoutput('pc/address-list', $data);
}
/**
* 代理信息
*/
public function agentInfoAction()
{
global $db, $ecs, $_CFG, $_LANG, $err;
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'languages/' .$_CFG['lang']. '/shopping_flow.php');
$user = $this->auth->user();
$data = [];
$agent = Model\Agent::findFirstByAgentId($user['agent_id']);
if (empty($agent)) {
$data['isEdit'] = false;
} else {
$data['isEdit'] = true;
}
$data['user'] = $user;
$data['agent'] = $agent;
$data['navName'] = '';
$data['userSidebar'] = 'agent';
echo $this->viewoutput('pc/agent-info', $data);
}
/**
* 代理订单信息
*/
public function agentOrderListAction()
{
global $db, $ecs, $_CFG, $_LANG, $err;
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'languages/' .$_CFG['lang']. '/shopping_flow.php');
$user = $this->auth->user();
$page = intval($this->request->getQuery('page')) ?: 1;
$limit = 10;
$offset = ($page - 1) * $limit;
$ossql = $sssql = $pssql = '';
if (! empty($orderAttr['os'])) {
$ossql = ' AND ' . db_create_in($orderAttr['os'], 'order_status');
}
if (! empty($orderAttr['ss'])) {
$sssql = ' AND ' . db_create_in($orderAttr['ss'], 'shipping_status');
}
if (! empty($orderAttr['ps'])) {
$pssql = ' AND ' . db_create_in($orderAttr['ps'], 'pay_status');
}
$orderids = [];
$arr = [];
$count = 0;
if ($user['agent_id']) {
$params = [
'alias' => 'o',
'columns' => "o.order_id, o.order_sn, o.order_status, o.shipping_status, o.pay_status, o.add_time, " .
"(o.goods_amount + o.shipping_fee + o.insure_fee + o.pay_fee + o.pack_fee + o.card_fee + o.tax - o.discount) AS total_fee , u.user_name",
'conditions' => "o.agent_id = '{$user['agent_id']}' $ossql $sssql $pssql",
'offset' => $offset,
'limit' => $limit,
'join' => [
[
'type' => 'LEFT',
'table' => 'users',
'alias' => 'u',
'on' => 'u.user_id = o.user_id'
]
],
'order' => 'add_time DESC',
];
$colls = Model\OrderInfo::find($params);
foreach ($colls as $row)
{
$orderids[] = $row['order_id'];
$row['shipping_status'] = ($row['shipping_status'] == SS_SHIPPED_ING) ? SS_PREPARING : $row['shipping_status'];
$row['status'] = Model\OrderInfo::getOrderStatus($row);
$orderTime = local_date($GLOBALS['_CFG']['time_format'], $row['add_time']);
$dateArr = explode(' ', $orderTime);
$arr[$row['order_id']] = [
'order_id' => $row['order_id'],
'user_name' => $row['user_name'],
'status' => $row['status'],
'order_sn' => $row['order_sn'],
'order_time' => $orderTime,
'order_time_date' => $orderTime,
'order_time_arr' => $dateArr,
'order_status' => $row['order_status'],
'shipping_status' => $row['shipping_status'],
'pay_status' => $row['pay_status'],
'total_fee' => price_format($row['total_fee'], false),
'goods' => [],
];
}
unset($params['limit'], $params['offset']);
$count = Model\OrderInfo::count($params);
}
$paginator = new \Juice\Paginator(['type' => 'emptyArray', 'config' => [
'count' => $count,
'limit' => $limit,
'page' => $page,
]]);
$paginate = $paginator->getPaginate();
$orders = $arr;
if ($orderids) {
$orderidsStr = implode(',', $orderids);
$params = [
'alias' => 'og',
'columns' => "og.rec_id, og.order_id, og.goods_id, og.goods_name, og.goods_sn, og.market_price, og.goods_number, " .
"og.goods_price, og.goods_attr, og.is_real, og.parent_id, og.is_gift, " .
"og.goods_price * og.goods_number AS subtotal, og.extension_code, g.goods_thumb, g.goods_img " .
',c.comment_id ',
'conditions' => "og.order_id IN ($orderidsStr)",
'join' => [
[
'type' => 'LEFT',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = og.goods_id'
],
[
'type' => 'LEFT',
'table' => 'comment',
'alias' => 'c',
'on' => 'c.id_value = og.goods_id AND c.order_id = og.order_id '
]
],
];
$colls = Model\OrderGoods::find($params);
foreach ($colls as $row) {
if (isset($orders[$row['order_id']])) {
$row['market_price'] = price_format($row['market_price'], false);
$row['goods_price'] = price_format($row['goods_price'], false);
$row['subtotal'] = price_format($row['subtotal'], false);
$orders[$row['order_id']]['goods'][] = $row;
}
}
}
$data = [];
$data['user'] = $user;
$data['orders'] = $orders;
$data['paginate'] = $paginate;
$data['navName'] = '';
$data['userSidebar'] = 'agent-order-list';
echo $this->viewoutput('pc/agent-order-list', $data);
}
/**
* 绑定微信号
*/
public function wechatBindAction()
{
$user = $this->auth->user();
$wechat = new WechatSDK;
$url = 'http://www.liowang.com/shop/wechat/oauth2/callback/bind';
$state = ['env' => 'product'];
if (isLocal()) {
$state = ['env' => 'local'];
}
return $this->response->redirect($wechat->pcWebOauth2($url, $state), true);
}
/**
* PC 微信回调 oauth2 绑定已登录用户
*/
public function wechatOauth2CallbackBindAction()
{
global $ecs, $db, $user;
include_once(ROOT_PATH . 'includes/lib_passport.php');
$code = $this->request->getQuery('code');
if (empty($code)) {
echo '授权失败';
exit;
}
$state = $this->request->getQuery('state');
parse_str($state, $params);
if (! islocal() && ! empty($params['env']) && $params['env'] == 'local') {
return $this->response->redirect('http://liowang.dev.seafarer.me/shop/wechat/oauth2/callback/bind?' . http_build_query($this->request->getQuery()));
}
$wechat = new WechatSDK;
$config = $this->config->wechat;
$config->appid = $config->open_web_appid;
$config->appsecret = $config->open_web_secret;
$wechat->init($config);
$response = $wechat->getUserTokenByCode($code);
if (empty($response['errcode'])) {
// 授权成功
$ewxpcuser = Model\WxPcUser::findFirstByOpenid($response['openid']);
$data = [];
$isFirst = false;
$isNeedCreate = false;
$uuser = [];
$wxpcuser = [];
if ($ewxpcuser) {
// 用户已经存在
$euser = Model\User::findFirstByWxPcUserid($ewxpcuser['id']);
} else {
// 第一次访问
$isFirst = true;
$wxuser = Model\WxUser::findFirstByUnionid($response['unionid']);
if (empty($wxuser)) {
$isNeedCreate = true;
} else {
$euser = Model\User::findFirstByWxUserid($wxuser['id']);
}
}
$wxpcuser['openid'] = $response['openid'];
$wxpcuser['unionid'] = $response['unionid'];
$wxpcuser['access_token'] = $response['access_token'];
$wxpcuser['refresh_token'] = $response['refresh_token'];
$wxpcuser['expired_at'] = $response['expires_in'] + time() - 200;
$wxpcuser['scope'] = $response['scope'];
$wxpcuser['ip'] = $this->request->getClientAddress();
$info = $wechat->getUserInfoByOauth($response['openid'], $response['access_token']);
$wxpcuser['nickname'] = $info['nickname'];
$wxpcuser['gender'] = $info['sex'];
$wxpcuser['language'] = $info['language'];
$wxpcuser['city'] = $info['city'];
$wxpcuser['country'] = $info['country'];
$wxpcuser['province'] = $info['province'];
$wxpcuser['headimgurl'] = $info['headimgurl'];
if (! empty($euser) && $euser['user_id'] != $_SESSION['user_id']) {
exit('该微信已被绑定');
}
if ($isFirst) {
// 首次PC扫码登录
$id = Model\WxPcUser::insert($wxpcuser);
$uuser['wx_pc_userid'] = $id;
Model\User::update($uuser, "user_id = {$_SESSION['user_id']}");
} else {
$uuser['wx_pc_userid'] = $ewxpcuser['id'];
Model\User::update($uuser, "user_id = {$_SESSION['user_id']}");
Model\WxPcUser::update($wxpcuser, "openid = '{$response['openid']}'");
}
} else {
SystemException::error($response['errmsg']);
}
$referer = $this->url->get('user/info');
return $this->response->redirect($referer, true);
}
}
<?php
error_reporting(E_ALL);
try {
define('BASE_PATH', realpath('..') . '/');
define('APP_PATH', BASE_PATH . 'app/');
define('STORAGE_PATH', BASE_PATH . 'storage/');
// 系统环境变量,参考laravel模式
$systemEnvs = [
'local' => [
'juice-pc'
],
'production' => [
'10-10-92-111'
],
'testing' => [
'10-10-13-185'
],
];
/**
* 项目引导文件
*/
$config = include __DIR__ . "/../app/start/start.php";
/**
* Handle the request
*/
$application = new \Phalcon\Mvc\Application($di);
$application->useImplicitView(false); // 禁用自动渲染
echo $application->handle()->getContent();
} catch (\Exception $e) {
//echo $e;
throw $e;
Warning : Cannot modify header information - headers already sent by (output started at /usr/local/www/liowang-shop-robotwar/shop/app/libraries/PrettyExceptions/Library/Phalcon/Utils/PrettyExceptions.php:339) in /usr/local/www/liowang-shop-robotwar/shop/app/libraries/PrettyExceptions/Library/Phalcon/Utils/PrettyExceptions.php on line 440
Exception - Undefined index: access_token
Undefined index: access_token
/usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php (261)
#1 {closure} (8 , Undefined index: access_token , /usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php , 261 , Array(9) )/usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php (261)
<?php
/**
* weixin library
* @author geekzhumail@gmail.com
* @since 2014-09-24
* @edit 2015-03-05
*/
class SDKRuntimeException extends Exception {
public function errorMessage() {
return $this->getMessage();
}
}
class WechatSDK extends WechatBase{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => 'wxdf0a039cf4ea94d1',
'appsecret' => '7100f0fe837a5287f075b6d2d9814ca0',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/';
/**
* @var access_token
*/
protected $accessToken = '';
/**
* 是否从远程服务器拿token
*/
protected $isRemote = false;
public function __construct() {
parent::__construct();
$this->isRemote = $this->config->is_remote;
$this->accessToken = $this->getToken();
}
/**
* 初始化数据
*/
public function init($config) {
$this->config = $config;
}
/**
* 获取Appid
*/
public function getAppid() {
return $this->config['appid'];
}
/**
* weixin signature
*/
public function checkSignature($signature, $timestamp, $nonce) {
$token = $this->config['token'];
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr);
$tmpStr = implode( $tmpArr );
$tmpStr = sha1( $tmpStr );
if( $tmpStr == $signature ){
return true;
}else{
return false;
}
}
/**
* web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function webOauth2($redirectUri, $state = [], $type = 'snsapi_base') {
$params = [];
$params['appid'] = $this->config['appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = $type;
if (empty($state['type'])) {
$state['type'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* pc web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function pcWebOauth2($redirectUri, $state = []) {
$params = [];
$params['appid'] = $this->config['open_web_appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = 'snsapi_login';
if (empty($state['env'])) {
$state['env'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/qrconnect?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* get access token by code
*/
public function getUserTokenByCode($code) {
$params = array();
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$params['code'] = $code;
$params['grant_type'] = 'authorization_code';
$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* refresh access token
* @param string $token 填写通过access_token获取到的refresh_token参数
*/
public function refreshUserToken($token) {
$params = [];
$params['appid'] = $this->config['appid'];
$params['grant_type'] = 'refresh_token';
$params['refresh_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 根据网页授权拉取用户信息(需scope为 snsapi_userinfo)
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function getUserInfoByOauth($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('sns/userinfo', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 验证授权凭证(access_token)是否有效
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function validUserToken($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/auth?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 获取公众号token
*/
public function getToken() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
if ($this->isRemote) {
return $this->getTokenFromRemote();
}
$params = [];
$params['grant_type'] = 'client_credential';
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$url = 'https://api.weixin.qq.com/cgi-bin/token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
// 写入缓存
$expiredAt = $response['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $response['access_token'], $expiredAt);
return $response['access_token'];
}
/**
* 获取access_token from remote
*/
public function getTokenFromRemote() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/token/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$cache->save('wechat_access_token_expired', $resp['expired_at'], $expiredAt);
$expiredAt = 120;
$cache->save($key, $resp['access_token'], $expiredAt);
return $resp['access_token'];
} else {
throw new Exception('wx admin get token error');
}
}
/**
* 获取用户基本信息(包括UnionID机制)
* @param string $openid 用户的唯一标识
* @param string $token 调用接口凭证,默认值为Wechat::getToken()
*/
public function getUserInfo($openid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('cgi-bin/user/info', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 下载已经上传到微信的资源文件
*/
public function downloadMedia($mediaid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['access_token'] = $token;
$params['media_id'] = $mediaid;
$url = 'http://file.api.weixin.qq.com/cgi-bin/media/get?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => false,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$content = curl_exec($ch);
$info = curl_getinfo($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == '200') {
// 正常则分割header和body
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($content, 0, $headerSize);
$body = substr($content, $headerSize);
}
curl_close($ch);
$resp['info'] = $info;
$resp['header'] = $header;
$resp['body'] = $body;
return $resp;
}
/**
* 获取 js api ticket
*/
public function getJsapiTicket()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
if ($this->isRemote) {
return $this->getJsapiTicketFromRemote();
}
$resp = json_decode($this->get('cgi-bin/ticket/getticket', ['access_token' => $this->accessToken, 'type' => 'jsapi']), true);
// 写入缓存
$expiredAt = $resp['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
}
/**
* 获取 js api ticket
*/
public function getJsapiTicketFromRemote()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/jsticket/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$expiredAt = 120;
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
} else {
throw new Exception('wx admin get ticket error');
}
}
/**
* 获取js api 签名
*/
public function getSignPackage($url = '')
{
$jsticket = $this->getJsapiTicket();
if (! $url) {
$url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}
$timestamp = time();
$nonceStr = $this->createNoncestr(16);
// 这里参数的顺序要按照 key 值 ASCII 码升序排序
$string = "jsapi_ticket={$jsticket}&noncestr={$nonceStr}×tamp={$timestamp}&url={$url}";
return [
"appId" => $this->config['appid'],
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => sha1($string),
"rawString" => $string
];
}
/**
* 新建一个类,由于PSR-4无法获取不以文件名命名的类;
*/
public function newClass($name)
{
return new $name();
}
// 微信支付
/**
* 获取微信付款码的prepayid
* @params array $params 参数
*/
public function getPrepayId($params) {
$sdk = new UnifiedOrderSDK;
$sdk->setParameter("trade_type","JSAPI");//交易类型
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$prepayid = $sdk->getPrepayId();
return $prepayid;
}
/**
* 获取支付js api 签名
*/
public function getPaySignPackage($prepayid)
{
$jsApiObj["appId"] = $this->config['appid'];
$timeStamp = time();
$jsApiObj["timeStamp"] = "$timeStamp";
$jsApiObj["nonceStr"] = $this->createNoncestr();
$jsApiObj["package"] = "prepay_id=$prepayid";
$jsApiObj["signType"] = "MD5";
$jsApiObj["paySign"] = $this->getSign($jsApiObj);
$signPackage = $jsApiObj;
return $signPackage;
}
/**
* 发送红包接口
* @params array $params 参数
*/
public function sendRedPack($params) {
$sdk = new RedPackSDK;
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$result = $sdk->send();
return $result;
}
}
/**
* 所有接口的基类
*/
class WechatBase
{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => '',
'appsecret' => '',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/cgi-bin/';
public function __construct() {
$this->di = Phalcon\DI::getDefault();
$this->config = $this->di->getConfig()['wechat'];
}
protected function get($url, $params = '')
{
if (strpos($url, 'https://') === false && strpos($url, 'http://') === false) {
$url = $this->apiPrefix.$url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl get error');
}
}
protected function post($url, $params = '', $data = '')
{
if (strpos($url, 'https://') === false) {
$url = $this->apiPrefix . $url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
if ($data) {
$options[CURLOPT_POSTFIELDS] = json_encode($data, JSON_UNESCAPED_UNICODE);
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl post error');
}
}
public function trimString($value)
{
$ret = null;
if (null != $value)
{
$ret = $value;
if (strlen($ret) == 0)
{
$ret = null;
}
}
return $ret;
}
/**
* 作用:产生随机字符串,不长于32位
*/
public function createNoncestr( $length = 32, $type = 'all')
{
if ($type == 'all') {
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
} else if($type == 'number') {
$chars = "0123456789";
}
$str ="";
for ( $i = 0; $i < $length; $i++ ) {
$str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
/**
* 作用:格式化参数,签名过程需要使用
*/
public function formatBizQueryParaMap($paraMap, $urlencode)
{
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v)
{
if($urlencode)
{
$v = urlencode($v);
}
//$buff .= strtolower($k) . "=" . $v . "&";
$buff .= $k . "=" . $v . "&";
}
$reqPar;
if (strlen($buff) > 0)
{
$reqPar = substr($buff, 0, strlen($buff)-1);
}
return $reqPar;
}
/**
* 作用:生成签名
*/
public function getSign($Obj)
{
foreach ($Obj as $k => $v)
{
$Parameters[$k] = $v;
}
//签名步骤一:按字典序排序参数
ksort($Parameters);
$String = $this->formatBizQueryParaMap($Parameters, false);
//echo '【string1】'.$String.'</br>';
//签名步骤二:在string后加入KEY
$String = $String . "&key=" . $this->config['key'];
//echo "【string2】".$String."</br>";
//签名步骤三:MD5加密
$String = md5($String);
//echo "【string3】 ".$String."</br>";
//签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
//echo "【result】 ".$result_."</br>";
return $result_;
}
/**
* 作用:array转xml
*/
public function arrayToXml($arr)
{
$xml = "<xml>";
foreach ($arr as $key=>$val)
{
if (is_numeric($val))
{
$xml.="<".$key.">".$val."</".$key.">";
}
else
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
$xml.="</xml>";
return $xml;
}
/**
* 作用:将xml转为array
*/
public function xmlToArray($xml)
{
//将XML转为array
$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $array_data;
}
/**
* 作用:以post方式提交xml到对应的接口url
*/
public function postXmlCurl($xml,$url,$second=30)
{
//初始化curl
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
curl_close($ch);
//返回结果
if($data)
{
return $data;
}
else
{
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
return false;
}
}
/**
* 作用:使用证书,以post方式提交xml到对应的接口url
*/
public function postXmlSSLCurl($xml,$url,$second=30)
{
$ch = curl_init();
//超时时间
curl_setopt($ch,CURLOPT_TIMEOUT,$second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch,CURLOPT_HEADER,FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT, $this->config['sslcert_path']);
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY, $this->config['sslkey_path']);
//post提交方式
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
$data = curl_exec($ch);
//返回结果
if($data){
curl_close($ch);
return $data;
}
else {
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
curl_close($ch);
return false;
}
}
}
/**
* 请求型接口的基类
*/
class WechatPayRequestBase extends WechatBase
{
var $parameters;//请求参数,类型为关联数组
public $response;//微信返回的响应
public $result;//返回参数,类型为关联数组
var $url;//接口链接
var $curl_timeout;//curl超时时间
/**
* 作用:设置请求参数
*/
function setParameter($parameter, $parameterValue)
{
$this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 作用:设置标配的请求参数,生成签名,生成接口参数xml
*/
function createXml()
{
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 作用:post请求xml
*/
function postXml()
{
$xml = $this->createXml();
$this->response = $this->postXmlCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:使用证书post请求xml
*/
function postXmlSSL()
{
$xml = $this->createXml();
$this->response = $this->postXmlSSLCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:获取结果,默认不使用证书
*/
function getResult()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
return $this->result;
}
}
/**
* 统一支付接口类
*/
class UnifiedOrderSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
//检测必填参数
if(empty($this->parameters["out_trade_no"]))
{
throw new SDKRuntimeException("缺少统一支付接口必填参数out_trade_no!"."<br>");
}elseif(empty($this->parameters["body"])){
throw new SDKRuntimeException("缺少统一支付接口必填参数body!"."<br>");
}elseif (empty($this->parameters["total_fee"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数total_fee!"."<br>");
}elseif (empty($this->parameters["notify_url"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数notify_url!"."<br>");
}elseif (empty($this->parameters["trade_type"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数trade_type!"."<br>");
}elseif ($this->parameters["trade_type"] == "JSAPI" &&
empty($this->parameters["openid"])){
throw new SDKRuntimeException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!"."<br>");
}
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["spbill_create_ip"] = $this->di->getRequest()->getClientAddress();//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 获取prepay_id
*/
function getPrepayId()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
$prepay_id = $this->result["prepay_id"];
return $prepay_id;
}
}
/**
* 响应型接口基类
*/
class WechatPayServerBase extends WechatBase
{
public $data;//接收到的数据,类型为关联数组
var $returnParameters;//返回参数,类型为关联数组
/**
* 将微信的请求xml转换成关联数组,以方便数据处理
*/
function saveData($xml)
{
$this->data = $this->xmlToArray($xml);
}
function checkSign()
{
$tmpData = $this->data;
unset($tmpData['sign']);
$sign = $this->getSign($tmpData);//本地签名
if (isLocal()) {
echo $sign;
}
if ($this->data['sign'] == $sign) {
return TRUE;
}
return FALSE;
}
/**
* 获取微信的请求数据
*/
function getData()
{
return $this->data;
}
/**
* 设置返回微信的xml数据
*/
function setReturnParameter($parameter, $parameterValue)
{
$this->returnParameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 生成接口参数xml
*/
function createXml()
{
return $this->arrayToXml($this->returnParameters);
}
/**
* 将xml数据返回微信
*/
function returnXml()
{
$returnXml = $this->createXml();
return $returnXml;
}
}
/**
* 红包发送接口类
*/
class RedPackSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
$requiredParams = [
'mch_billno',
'nick_name',
'send_name',
're_openid',
'total_amount',
'min_value',
'max_value',
'total_num',
'wishing',
'act_name',
'remark',
];
//检测必填参数
foreach ($requiredParams as $val) {
if (empty($this->parameters[$val])) {
throw new SDKRuntimeException("缺少红包接口必填参数{$val}!"."<br>");
}
}
$this->parameters["wxappid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["client_ip"] = gethostbyname(gethostname());//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 发送红包
*
* curl 返回例子
* <xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[发放成功.]]></return_msg>
<result_code><![CDATA[SUCCESS]]></result_code>
<err_code><![CDATA[0]]></err_code>
<err_code_des><![CDATA[发放成功.]]></err_code_des>
<mch_billno><![CDATA[1228563102201503261427336509]]></mch_billno>
<mch_id>1228563102</mch_id>
<wxappid><![CDATA[wxdceccb9098a978b8]]></wxappid>
<re_openid><![CDATA[o55e0t1NHPxdcRCjbIu_xnjHc2u8]]></re_openid>
<total_amount>100</total_amount>
</xml>
*/
public function send() {
$this->postXmlSSL();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
return $this->result;
}
#2 WechatSDK ->getTokenFromRemote ()/usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php (209)
<?php
/**
* weixin library
* @author geekzhumail@gmail.com
* @since 2014-09-24
* @edit 2015-03-05
*/
class SDKRuntimeException extends Exception {
public function errorMessage() {
return $this->getMessage();
}
}
class WechatSDK extends WechatBase{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => 'wxdf0a039cf4ea94d1',
'appsecret' => '7100f0fe837a5287f075b6d2d9814ca0',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/';
/**
* @var access_token
*/
protected $accessToken = '';
/**
* 是否从远程服务器拿token
*/
protected $isRemote = false;
public function __construct() {
parent::__construct();
$this->isRemote = $this->config->is_remote;
$this->accessToken = $this->getToken();
}
/**
* 初始化数据
*/
public function init($config) {
$this->config = $config;
}
/**
* 获取Appid
*/
public function getAppid() {
return $this->config['appid'];
}
/**
* weixin signature
*/
public function checkSignature($signature, $timestamp, $nonce) {
$token = $this->config['token'];
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr);
$tmpStr = implode( $tmpArr );
$tmpStr = sha1( $tmpStr );
if( $tmpStr == $signature ){
return true;
}else{
return false;
}
}
/**
* web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function webOauth2($redirectUri, $state = [], $type = 'snsapi_base') {
$params = [];
$params['appid'] = $this->config['appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = $type;
if (empty($state['type'])) {
$state['type'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* pc web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function pcWebOauth2($redirectUri, $state = []) {
$params = [];
$params['appid'] = $this->config['open_web_appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = 'snsapi_login';
if (empty($state['env'])) {
$state['env'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/qrconnect?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* get access token by code
*/
public function getUserTokenByCode($code) {
$params = array();
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$params['code'] = $code;
$params['grant_type'] = 'authorization_code';
$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* refresh access token
* @param string $token 填写通过access_token获取到的refresh_token参数
*/
public function refreshUserToken($token) {
$params = [];
$params['appid'] = $this->config['appid'];
$params['grant_type'] = 'refresh_token';
$params['refresh_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 根据网页授权拉取用户信息(需scope为 snsapi_userinfo)
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function getUserInfoByOauth($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('sns/userinfo', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 验证授权凭证(access_token)是否有效
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function validUserToken($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/auth?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 获取公众号token
*/
public function getToken() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
if ($this->isRemote) {
return $this->getTokenFromRemote();
}
$params = [];
$params['grant_type'] = 'client_credential';
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$url = 'https://api.weixin.qq.com/cgi-bin/token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
// 写入缓存
$expiredAt = $response['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $response['access_token'], $expiredAt);
return $response['access_token'];
}
/**
* 获取access_token from remote
*/
public function getTokenFromRemote() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/token/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$cache->save('wechat_access_token_expired', $resp['expired_at'], $expiredAt);
$expiredAt = 120;
$cache->save($key, $resp['access_token'], $expiredAt);
return $resp['access_token'];
} else {
throw new Exception('wx admin get token error');
}
}
/**
* 获取用户基本信息(包括UnionID机制)
* @param string $openid 用户的唯一标识
* @param string $token 调用接口凭证,默认值为Wechat::getToken()
*/
public function getUserInfo($openid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('cgi-bin/user/info', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 下载已经上传到微信的资源文件
*/
public function downloadMedia($mediaid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['access_token'] = $token;
$params['media_id'] = $mediaid;
$url = 'http://file.api.weixin.qq.com/cgi-bin/media/get?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => false,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$content = curl_exec($ch);
$info = curl_getinfo($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == '200') {
// 正常则分割header和body
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($content, 0, $headerSize);
$body = substr($content, $headerSize);
}
curl_close($ch);
$resp['info'] = $info;
$resp['header'] = $header;
$resp['body'] = $body;
return $resp;
}
/**
* 获取 js api ticket
*/
public function getJsapiTicket()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
if ($this->isRemote) {
return $this->getJsapiTicketFromRemote();
}
$resp = json_decode($this->get('cgi-bin/ticket/getticket', ['access_token' => $this->accessToken, 'type' => 'jsapi']), true);
// 写入缓存
$expiredAt = $resp['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
}
/**
* 获取 js api ticket
*/
public function getJsapiTicketFromRemote()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/jsticket/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$expiredAt = 120;
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
} else {
throw new Exception('wx admin get ticket error');
}
}
/**
* 获取js api 签名
*/
public function getSignPackage($url = '')
{
$jsticket = $this->getJsapiTicket();
if (! $url) {
$url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}
$timestamp = time();
$nonceStr = $this->createNoncestr(16);
// 这里参数的顺序要按照 key 值 ASCII 码升序排序
$string = "jsapi_ticket={$jsticket}&noncestr={$nonceStr}×tamp={$timestamp}&url={$url}";
return [
"appId" => $this->config['appid'],
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => sha1($string),
"rawString" => $string
];
}
/**
* 新建一个类,由于PSR-4无法获取不以文件名命名的类;
*/
public function newClass($name)
{
return new $name();
}
// 微信支付
/**
* 获取微信付款码的prepayid
* @params array $params 参数
*/
public function getPrepayId($params) {
$sdk = new UnifiedOrderSDK;
$sdk->setParameter("trade_type","JSAPI");//交易类型
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$prepayid = $sdk->getPrepayId();
return $prepayid;
}
/**
* 获取支付js api 签名
*/
public function getPaySignPackage($prepayid)
{
$jsApiObj["appId"] = $this->config['appid'];
$timeStamp = time();
$jsApiObj["timeStamp"] = "$timeStamp";
$jsApiObj["nonceStr"] = $this->createNoncestr();
$jsApiObj["package"] = "prepay_id=$prepayid";
$jsApiObj["signType"] = "MD5";
$jsApiObj["paySign"] = $this->getSign($jsApiObj);
$signPackage = $jsApiObj;
return $signPackage;
}
/**
* 发送红包接口
* @params array $params 参数
*/
public function sendRedPack($params) {
$sdk = new RedPackSDK;
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$result = $sdk->send();
return $result;
}
}
/**
* 所有接口的基类
*/
class WechatBase
{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => '',
'appsecret' => '',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/cgi-bin/';
public function __construct() {
$this->di = Phalcon\DI::getDefault();
$this->config = $this->di->getConfig()['wechat'];
}
protected function get($url, $params = '')
{
if (strpos($url, 'https://') === false && strpos($url, 'http://') === false) {
$url = $this->apiPrefix.$url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl get error');
}
}
protected function post($url, $params = '', $data = '')
{
if (strpos($url, 'https://') === false) {
$url = $this->apiPrefix . $url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
if ($data) {
$options[CURLOPT_POSTFIELDS] = json_encode($data, JSON_UNESCAPED_UNICODE);
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl post error');
}
}
public function trimString($value)
{
$ret = null;
if (null != $value)
{
$ret = $value;
if (strlen($ret) == 0)
{
$ret = null;
}
}
return $ret;
}
/**
* 作用:产生随机字符串,不长于32位
*/
public function createNoncestr( $length = 32, $type = 'all')
{
if ($type == 'all') {
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
} else if($type == 'number') {
$chars = "0123456789";
}
$str ="";
for ( $i = 0; $i < $length; $i++ ) {
$str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
/**
* 作用:格式化参数,签名过程需要使用
*/
public function formatBizQueryParaMap($paraMap, $urlencode)
{
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v)
{
if($urlencode)
{
$v = urlencode($v);
}
//$buff .= strtolower($k) . "=" . $v . "&";
$buff .= $k . "=" . $v . "&";
}
$reqPar;
if (strlen($buff) > 0)
{
$reqPar = substr($buff, 0, strlen($buff)-1);
}
return $reqPar;
}
/**
* 作用:生成签名
*/
public function getSign($Obj)
{
foreach ($Obj as $k => $v)
{
$Parameters[$k] = $v;
}
//签名步骤一:按字典序排序参数
ksort($Parameters);
$String = $this->formatBizQueryParaMap($Parameters, false);
//echo '【string1】'.$String.'</br>';
//签名步骤二:在string后加入KEY
$String = $String . "&key=" . $this->config['key'];
//echo "【string2】".$String."</br>";
//签名步骤三:MD5加密
$String = md5($String);
//echo "【string3】 ".$String."</br>";
//签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
//echo "【result】 ".$result_."</br>";
return $result_;
}
/**
* 作用:array转xml
*/
public function arrayToXml($arr)
{
$xml = "<xml>";
foreach ($arr as $key=>$val)
{
if (is_numeric($val))
{
$xml.="<".$key.">".$val."</".$key.">";
}
else
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
$xml.="</xml>";
return $xml;
}
/**
* 作用:将xml转为array
*/
public function xmlToArray($xml)
{
//将XML转为array
$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $array_data;
}
/**
* 作用:以post方式提交xml到对应的接口url
*/
public function postXmlCurl($xml,$url,$second=30)
{
//初始化curl
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
curl_close($ch);
//返回结果
if($data)
{
return $data;
}
else
{
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
return false;
}
}
/**
* 作用:使用证书,以post方式提交xml到对应的接口url
*/
public function postXmlSSLCurl($xml,$url,$second=30)
{
$ch = curl_init();
//超时时间
curl_setopt($ch,CURLOPT_TIMEOUT,$second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch,CURLOPT_HEADER,FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT, $this->config['sslcert_path']);
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY, $this->config['sslkey_path']);
//post提交方式
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
$data = curl_exec($ch);
//返回结果
if($data){
curl_close($ch);
return $data;
}
else {
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
curl_close($ch);
return false;
}
}
}
/**
* 请求型接口的基类
*/
class WechatPayRequestBase extends WechatBase
{
var $parameters;//请求参数,类型为关联数组
public $response;//微信返回的响应
public $result;//返回参数,类型为关联数组
var $url;//接口链接
var $curl_timeout;//curl超时时间
/**
* 作用:设置请求参数
*/
function setParameter($parameter, $parameterValue)
{
$this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 作用:设置标配的请求参数,生成签名,生成接口参数xml
*/
function createXml()
{
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 作用:post请求xml
*/
function postXml()
{
$xml = $this->createXml();
$this->response = $this->postXmlCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:使用证书post请求xml
*/
function postXmlSSL()
{
$xml = $this->createXml();
$this->response = $this->postXmlSSLCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:获取结果,默认不使用证书
*/
function getResult()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
return $this->result;
}
}
/**
* 统一支付接口类
*/
class UnifiedOrderSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
//检测必填参数
if(empty($this->parameters["out_trade_no"]))
{
throw new SDKRuntimeException("缺少统一支付接口必填参数out_trade_no!"."<br>");
}elseif(empty($this->parameters["body"])){
throw new SDKRuntimeException("缺少统一支付接口必填参数body!"."<br>");
}elseif (empty($this->parameters["total_fee"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数total_fee!"."<br>");
}elseif (empty($this->parameters["notify_url"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数notify_url!"."<br>");
}elseif (empty($this->parameters["trade_type"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数trade_type!"."<br>");
}elseif ($this->parameters["trade_type"] == "JSAPI" &&
empty($this->parameters["openid"])){
throw new SDKRuntimeException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!"."<br>");
}
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["spbill_create_ip"] = $this->di->getRequest()->getClientAddress();//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 获取prepay_id
*/
function getPrepayId()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
$prepay_id = $this->result["prepay_id"];
return $prepay_id;
}
}
/**
* 响应型接口基类
*/
class WechatPayServerBase extends WechatBase
{
public $data;//接收到的数据,类型为关联数组
var $returnParameters;//返回参数,类型为关联数组
/**
* 将微信的请求xml转换成关联数组,以方便数据处理
*/
function saveData($xml)
{
$this->data = $this->xmlToArray($xml);
}
function checkSign()
{
$tmpData = $this->data;
unset($tmpData['sign']);
$sign = $this->getSign($tmpData);//本地签名
if (isLocal()) {
echo $sign;
}
if ($this->data['sign'] == $sign) {
return TRUE;
}
return FALSE;
}
/**
* 获取微信的请求数据
*/
function getData()
{
return $this->data;
}
/**
* 设置返回微信的xml数据
*/
function setReturnParameter($parameter, $parameterValue)
{
$this->returnParameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 生成接口参数xml
*/
function createXml()
{
return $this->arrayToXml($this->returnParameters);
}
/**
* 将xml数据返回微信
*/
function returnXml()
{
$returnXml = $this->createXml();
return $returnXml;
}
}
/**
* 红包发送接口类
*/
class RedPackSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
$requiredParams = [
'mch_billno',
'nick_name',
'send_name',
're_openid',
'total_amount',
'min_value',
'max_value',
'total_num',
'wishing',
'act_name',
'remark',
];
//检测必填参数
foreach ($requiredParams as $val) {
if (empty($this->parameters[$val])) {
throw new SDKRuntimeException("缺少红包接口必填参数{$val}!"."<br>");
}
}
$this->parameters["wxappid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["client_ip"] = gethostbyname(gethostname());//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 发送红包
*
* curl 返回例子
* <xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[发放成功.]]></return_msg>
<result_code><![CDATA[SUCCESS]]></result_code>
<err_code><![CDATA[0]]></err_code>
<err_code_des><![CDATA[发放成功.]]></err_code_des>
<mch_billno><![CDATA[1228563102201503261427336509]]></mch_billno>
<mch_id>1228563102</mch_id>
<wxappid><![CDATA[wxdceccb9098a978b8]]></wxappid>
<re_openid><![CDATA[o55e0t1NHPxdcRCjbIu_xnjHc2u8]]></re_openid>
<total_amount>100</total_amount>
</xml>
*/
public function send() {
$this->postXmlSSL();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
return $this->result;
}
#3 WechatSDK ->getToken ()/usr/local/www/liowang-shop-robotwar/shop/app/libraries/WechatSDK.php (45)
<?php
/**
* weixin library
* @author geekzhumail@gmail.com
* @since 2014-09-24
* @edit 2015-03-05
*/
class SDKRuntimeException extends Exception {
public function errorMessage() {
return $this->getMessage();
}
}
class WechatSDK extends WechatBase{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => 'wxdf0a039cf4ea94d1',
'appsecret' => '7100f0fe837a5287f075b6d2d9814ca0',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/';
/**
* @var access_token
*/
protected $accessToken = '';
/**
* 是否从远程服务器拿token
*/
protected $isRemote = false;
public function __construct() {
parent::__construct();
$this->isRemote = $this->config->is_remote;
$this->accessToken = $this->getToken();
}
/**
* 初始化数据
*/
public function init($config) {
$this->config = $config;
}
/**
* 获取Appid
*/
public function getAppid() {
return $this->config['appid'];
}
/**
* weixin signature
*/
public function checkSignature($signature, $timestamp, $nonce) {
$token = $this->config['token'];
$tmpArr = array($token, $timestamp, $nonce);
sort($tmpArr);
$tmpStr = implode( $tmpArr );
$tmpStr = sha1( $tmpStr );
if( $tmpStr == $signature ){
return true;
}else{
return false;
}
}
/**
* web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function webOauth2($redirectUri, $state = [], $type = 'snsapi_base') {
$params = [];
$params['appid'] = $this->config['appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = $type;
if (empty($state['type'])) {
$state['type'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/oauth2/authorize?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* pc web oauth2
* @params string $redirectUri 回调地址
* @params array $state 额外的参数
* @params string $type 授权模式
* @return string 授权地址
*/
public function pcWebOauth2($redirectUri, $state = []) {
$params = [];
$params['appid'] = $this->config['open_web_appid'];
$params['redirect_uri'] = $redirectUri;
$params['response_type'] = 'code';
$params['scope'] = 'snsapi_login';
if (empty($state['env'])) {
$state['env'] = 'product';
}
$params['state'] = urldecode(http_build_query($state));
$url = 'http://open.weixin.qq.com/connect/qrconnect?' . http_build_query($params) . '#wechat_redirect';
return $url;
}
/**
* get access token by code
*/
public function getUserTokenByCode($code) {
$params = array();
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$params['code'] = $code;
$params['grant_type'] = 'authorization_code';
$url = 'https://api.weixin.qq.com/sns/oauth2/access_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* refresh access token
* @param string $token 填写通过access_token获取到的refresh_token参数
*/
public function refreshUserToken($token) {
$params = [];
$params['appid'] = $this->config['appid'];
$params['grant_type'] = 'refresh_token';
$params['refresh_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/oauth2/refresh_token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 根据网页授权拉取用户信息(需scope为 snsapi_userinfo)
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function getUserInfoByOauth($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('sns/userinfo', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 验证授权凭证(access_token)是否有效
* @param string $openid 用户的唯一标识
* @param string $token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
*/
public function validUserToken($openid, $token) {
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$url = 'https://api.weixin.qq.com/sns/auth?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
return $response;
}
/**
* 获取公众号token
*/
public function getToken() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
if ($this->isRemote) {
return $this->getTokenFromRemote();
}
$params = [];
$params['grant_type'] = 'client_credential';
$params['appid'] = $this->config['appid'];
$params['secret'] = $this->config['appsecret'];
$url = 'https://api.weixin.qq.com/cgi-bin/token?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$response = json_decode($response, true);
// 写入缓存
$expiredAt = $response['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $response['access_token'], $expiredAt);
return $response['access_token'];
}
/**
* 获取access_token from remote
*/
public function getTokenFromRemote() {
$cache = $this->di->getCache();
$key = 'wechat_access_token_' . $this->config['appid'];
if ($cache->exists($key) && $token = $cache->get($key)) {
return $token;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/token/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$cache->save('wechat_access_token_expired', $resp['expired_at'], $expiredAt);
$expiredAt = 120;
$cache->save($key, $resp['access_token'], $expiredAt);
return $resp['access_token'];
} else {
throw new Exception('wx admin get token error');
}
}
/**
* 获取用户基本信息(包括UnionID机制)
* @param string $openid 用户的唯一标识
* @param string $token 调用接口凭证,默认值为Wechat::getToken()
*/
public function getUserInfo($openid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['openid'] = $openid;
$params['access_token'] = $token;
$params['lang'] = 'zh_CN';
$response = $this->get('cgi-bin/user/info', $params);
$response = json_decode($response, true);
return $response;
}
/**
* 下载已经上传到微信的资源文件
*/
public function downloadMedia($mediaid, $token = '') {
if (empty($token)) {
$token = $this->getToken();
}
$params = [];
$params['access_token'] = $token;
$params['media_id'] = $mediaid;
$url = 'http://file.api.weixin.qq.com/cgi-bin/media/get?' . http_build_query($params);
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => false,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$content = curl_exec($ch);
$info = curl_getinfo($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == '200') {
// 正常则分割header和body
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($content, 0, $headerSize);
$body = substr($content, $headerSize);
}
curl_close($ch);
$resp['info'] = $info;
$resp['header'] = $header;
$resp['body'] = $body;
return $resp;
}
/**
* 获取 js api ticket
*/
public function getJsapiTicket()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
if ($this->isRemote) {
return $this->getJsapiTicketFromRemote();
}
$resp = json_decode($this->get('cgi-bin/ticket/getticket', ['access_token' => $this->accessToken, 'type' => 'jsapi']), true);
// 写入缓存
$expiredAt = $resp['expires_in'] - 5 * 60;// 设置缓存时间(分钟)
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
}
/**
* 获取 js api ticket
*/
public function getJsapiTicketFromRemote()
{
$cache = $this->di->getCache();
$key = 'wechat_js_ticket' . $this->config['appid'];
if ($cache->exists($key) && $ticket = $cache->get($key)) {
return $ticket;
}
$params = [];
$params['appid'] = $this->config['appid'];
$url = 'http://wx.seafarer.me/openapi/v1/jsticket/get';
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
$options[CURLOPT_POSTFIELDS] = $params;
curl_setopt_array($ch, $options);
$resp = curl_exec($ch);
$resp = json_decode($resp, true);
$status = curl_getinfo($ch);
curl_close($ch);
if (intval($status["http_code"]) == 200) {
// 写入缓存
$expiredAt = $resp['expired_at'] - time();// 设置缓存时间(秒)
$expiredAt = 120;
$cache->save($key, $resp['ticket'], $expiredAt);
return $resp['ticket'];
} else {
throw new Exception('wx admin get ticket error');
}
}
/**
* 获取js api 签名
*/
public function getSignPackage($url = '')
{
$jsticket = $this->getJsapiTicket();
if (! $url) {
$url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}
$timestamp = time();
$nonceStr = $this->createNoncestr(16);
// 这里参数的顺序要按照 key 值 ASCII 码升序排序
$string = "jsapi_ticket={$jsticket}&noncestr={$nonceStr}×tamp={$timestamp}&url={$url}";
return [
"appId" => $this->config['appid'],
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => sha1($string),
"rawString" => $string
];
}
/**
* 新建一个类,由于PSR-4无法获取不以文件名命名的类;
*/
public function newClass($name)
{
return new $name();
}
// 微信支付
/**
* 获取微信付款码的prepayid
* @params array $params 参数
*/
public function getPrepayId($params) {
$sdk = new UnifiedOrderSDK;
$sdk->setParameter("trade_type","JSAPI");//交易类型
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$prepayid = $sdk->getPrepayId();
return $prepayid;
}
/**
* 获取支付js api 签名
*/
public function getPaySignPackage($prepayid)
{
$jsApiObj["appId"] = $this->config['appid'];
$timeStamp = time();
$jsApiObj["timeStamp"] = "$timeStamp";
$jsApiObj["nonceStr"] = $this->createNoncestr();
$jsApiObj["package"] = "prepay_id=$prepayid";
$jsApiObj["signType"] = "MD5";
$jsApiObj["paySign"] = $this->getSign($jsApiObj);
$signPackage = $jsApiObj;
return $signPackage;
}
/**
* 发送红包接口
* @params array $params 参数
*/
public function sendRedPack($params) {
$sdk = new RedPackSDK;
foreach ($params as $key => $val) {
$sdk->setParameter($key, $val);
}
$result = $sdk->send();
return $result;
}
}
/**
* 所有接口的基类
*/
class WechatBase
{
/**
* 配置参数
* @var
*/
protected $config = [
'appid' => '',
'appsecret' => '',
];
/**
* api地址
* @var
*/
protected $apiPrefix = 'https://api.weixin.qq.com/cgi-bin/';
public function __construct() {
$this->di = Phalcon\DI::getDefault();
$this->config = $this->di->getConfig()['wechat'];
}
protected function get($url, $params = '')
{
if (strpos($url, 'https://') === false && strpos($url, 'http://') === false) {
$url = $this->apiPrefix.$url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
];
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl get error');
}
}
protected function post($url, $params = '', $data = '')
{
if (strpos($url, 'https://') === false) {
$url = $this->apiPrefix . $url;
}
if ($params) {
$url .= '?'.http_build_query($params);
}
$ch = curl_init();
$options = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
];
if ($data) {
$options[CURLOPT_POSTFIELDS] = json_encode($data, JSON_UNESCAPED_UNICODE);
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$resp_status = curl_getinfo($ch);
curl_close($ch);
if (intval($resp_status["http_code"]) == 200) {
return $response;
} else {
throw new Exception('WechatSDK curl post error');
}
}
public function trimString($value)
{
$ret = null;
if (null != $value)
{
$ret = $value;
if (strlen($ret) == 0)
{
$ret = null;
}
}
return $ret;
}
/**
* 作用:产生随机字符串,不长于32位
*/
public function createNoncestr( $length = 32, $type = 'all')
{
if ($type == 'all') {
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
} else if($type == 'number') {
$chars = "0123456789";
}
$str ="";
for ( $i = 0; $i < $length; $i++ ) {
$str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
/**
* 作用:格式化参数,签名过程需要使用
*/
public function formatBizQueryParaMap($paraMap, $urlencode)
{
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v)
{
if($urlencode)
{
$v = urlencode($v);
}
//$buff .= strtolower($k) . "=" . $v . "&";
$buff .= $k . "=" . $v . "&";
}
$reqPar;
if (strlen($buff) > 0)
{
$reqPar = substr($buff, 0, strlen($buff)-1);
}
return $reqPar;
}
/**
* 作用:生成签名
*/
public function getSign($Obj)
{
foreach ($Obj as $k => $v)
{
$Parameters[$k] = $v;
}
//签名步骤一:按字典序排序参数
ksort($Parameters);
$String = $this->formatBizQueryParaMap($Parameters, false);
//echo '【string1】'.$String.'</br>';
//签名步骤二:在string后加入KEY
$String = $String . "&key=" . $this->config['key'];
//echo "【string2】".$String."</br>";
//签名步骤三:MD5加密
$String = md5($String);
//echo "【string3】 ".$String."</br>";
//签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
//echo "【result】 ".$result_."</br>";
return $result_;
}
/**
* 作用:array转xml
*/
public function arrayToXml($arr)
{
$xml = "<xml>";
foreach ($arr as $key=>$val)
{
if (is_numeric($val))
{
$xml.="<".$key.">".$val."</".$key.">";
}
else
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
$xml.="</xml>";
return $xml;
}
/**
* 作用:将xml转为array
*/
public function xmlToArray($xml)
{
//将XML转为array
$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $array_data;
}
/**
* 作用:以post方式提交xml到对应的接口url
*/
public function postXmlCurl($xml,$url,$second=30)
{
//初始化curl
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
curl_close($ch);
//返回结果
if($data)
{
return $data;
}
else
{
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
return false;
}
}
/**
* 作用:使用证书,以post方式提交xml到对应的接口url
*/
public function postXmlSSLCurl($xml,$url,$second=30)
{
$ch = curl_init();
//超时时间
curl_setopt($ch,CURLOPT_TIMEOUT,$second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
//设置header
curl_setopt($ch,CURLOPT_HEADER,FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT, $this->config['sslcert_path']);
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY, $this->config['sslkey_path']);
//post提交方式
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
$data = curl_exec($ch);
//返回结果
if($data){
curl_close($ch);
return $data;
}
else {
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
curl_close($ch);
return false;
}
}
}
/**
* 请求型接口的基类
*/
class WechatPayRequestBase extends WechatBase
{
var $parameters;//请求参数,类型为关联数组
public $response;//微信返回的响应
public $result;//返回参数,类型为关联数组
var $url;//接口链接
var $curl_timeout;//curl超时时间
/**
* 作用:设置请求参数
*/
function setParameter($parameter, $parameterValue)
{
$this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 作用:设置标配的请求参数,生成签名,生成接口参数xml
*/
function createXml()
{
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 作用:post请求xml
*/
function postXml()
{
$xml = $this->createXml();
$this->response = $this->postXmlCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:使用证书post请求xml
*/
function postXmlSSL()
{
$xml = $this->createXml();
$this->response = $this->postXmlSSLCurl($xml,$this->url,$this->curl_timeout);
return $this->response;
}
/**
* 作用:获取结果,默认不使用证书
*/
function getResult()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
return $this->result;
}
}
/**
* 统一支付接口类
*/
class UnifiedOrderSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
//检测必填参数
if(empty($this->parameters["out_trade_no"]))
{
throw new SDKRuntimeException("缺少统一支付接口必填参数out_trade_no!"."<br>");
}elseif(empty($this->parameters["body"])){
throw new SDKRuntimeException("缺少统一支付接口必填参数body!"."<br>");
}elseif (empty($this->parameters["total_fee"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数total_fee!"."<br>");
}elseif (empty($this->parameters["notify_url"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数notify_url!"."<br>");
}elseif (empty($this->parameters["trade_type"])) {
throw new SDKRuntimeException("缺少统一支付接口必填参数trade_type!"."<br>");
}elseif ($this->parameters["trade_type"] == "JSAPI" &&
empty($this->parameters["openid"])){
throw new SDKRuntimeException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!"."<br>");
}
$this->parameters["appid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["spbill_create_ip"] = $this->di->getRequest()->getClientAddress();//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 获取prepay_id
*/
function getPrepayId()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
$prepay_id = $this->result["prepay_id"];
return $prepay_id;
}
}
/**
* 响应型接口基类
*/
class WechatPayServerBase extends WechatBase
{
public $data;//接收到的数据,类型为关联数组
var $returnParameters;//返回参数,类型为关联数组
/**
* 将微信的请求xml转换成关联数组,以方便数据处理
*/
function saveData($xml)
{
$this->data = $this->xmlToArray($xml);
}
function checkSign()
{
$tmpData = $this->data;
unset($tmpData['sign']);
$sign = $this->getSign($tmpData);//本地签名
if (isLocal()) {
echo $sign;
}
if ($this->data['sign'] == $sign) {
return TRUE;
}
return FALSE;
}
/**
* 获取微信的请求数据
*/
function getData()
{
return $this->data;
}
/**
* 设置返回微信的xml数据
*/
function setReturnParameter($parameter, $parameterValue)
{
$this->returnParameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 生成接口参数xml
*/
function createXml()
{
return $this->arrayToXml($this->returnParameters);
}
/**
* 将xml数据返回微信
*/
function returnXml()
{
$returnXml = $this->createXml();
return $returnXml;
}
}
/**
* 红包发送接口类
*/
class RedPackSDK extends WechatPayRequestBase
{
function __construct()
{
parent::__construct();
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/sendredpack";
//设置curl超时时间
$this->curl_timeout = $this->config['curl_timeout'];
}
/**
* 生成接口参数xml
*/
function createXml()
{
$requiredParams = [
'mch_billno',
'nick_name',
'send_name',
're_openid',
'total_amount',
'min_value',
'max_value',
'total_num',
'wishing',
'act_name',
'remark',
];
//检测必填参数
foreach ($requiredParams as $val) {
if (empty($this->parameters[$val])) {
throw new SDKRuntimeException("缺少红包接口必填参数{$val}!"."<br>");
}
}
$this->parameters["wxappid"] = $this->config['appid'];//公众账号ID
$this->parameters["mch_id"] = $this->config['mchid'];//商户号
$this->parameters["client_ip"] = gethostbyname(gethostname());//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 发送红包
*
* curl 返回例子
* <xml>
<return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[发放成功.]]></return_msg>
<result_code><![CDATA[SUCCESS]]></result_code>
<err_code><![CDATA[0]]></err_code>
<err_code_des><![CDATA[发放成功.]]></err_code_des>
<mch_billno><![CDATA[1228563102201503261427336509]]></mch_billno>
<mch_id>1228563102</mch_id>
<wxappid><![CDATA[wxdceccb9098a978b8]]></wxappid>
<re_openid><![CDATA[o55e0t1NHPxdcRCjbIu_xnjHc2u8]]></re_openid>
<total_amount>100</total_amount>
</xml>
*/
public function send() {
$this->postXmlSSL();
$this->result = $this->xmlToArray($this->response);
if ($this->result['return_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['return_msg']);
}
if ($this->result['result_code'] == 'FAIL') {
throw new SDKRuntimeException($this->result['err_code_des']);
}
return $this->result;
}
#4 WechatSDK ->__construct ()/usr/local/www/liowang-shop-robotwar/shop/app/controllers/PageUserController.php (24)
<?php
use HomeSpace\Model;
class PageUserController extends PageBaseController {
/**
* 登录
*/
public function loginAction()
{
$data = [];
//echo '登录';
$act = $this->request->getQuery('act');
if ($act == 'not_active') {
if (empty($_SESSION['tmp_email'])) {
$act = '';
}
$data['email'] = $_SESSION['tmp_email'];
}
$data['act'] = $act;
$data['errmsg'] = $this->flash->getMessages('error');
$this->flash->clear();
$wechat = new WechatSDK;
//$url = $this->url->get('wechat/oauth2/callback');
$url = 'http://www.liowang.com/shop/wechat/oauth2/callback';
$state = ['env' => 'product'];
if (isLocal()) {
$state = ['env' => 'local'];
}
$data['wechat_login_url'] = $wechat->pcWebOauth2($url, $state);
echo $this->viewoutput('pc/login', $data);
}
/**
* 登录 POST
*/
public function postLoginAction()
{
global $user, $_LANG;
$errmsg = '';
$account = $this->request->getPost('account');
$password = $this->request->getPost('password');
$remember = ! empty($this->request->getQuery('remember'));
if (filter_var($account, FILTER_VALIDATE_EMAIL)) {
// 邮箱登录
$muser = Model\User::findFirst(['columns' => 'user_name', 'conditions' => "email = '$account'"]);
} else if(is_numeric($account) && strlen($account) == 11) {
// 手机登录
$muser = Model\User::findFirst(['columns' => 'user_name', 'conditions' => "mobile_phone = '$account'"]);
}
if (! empty($muser) && ! empty($password)) {
if ($user->login($muser['user_name'], $password, $remember)) {
$row = Model\User::findFirstByUserId($_SESSION['user_id']);
if ($row['is_validated'] == 1) {
update_user_info();
recalculate_price();
$this->auth->loginUsingId($_SESSION['user_id']);
$_SESSION['pc_login'] = true;
return $this->response->redirect('home/index');
} else {
//$_SESSION['login_fail'] ++ ;
//$errmsg = '该账号尚未激活请查阅该账号邮箱的激活邮件,或者联系客服';
$_SESSION['tmp_email'] = $row['email'];
return $this->response->redirect('login?act=not_active');
}
} else {
$_SESSION['login_fail'] ++ ;
$errmsg = $_LANG['login_failure'];
}
}
$errmsg = $errmsg ?: '账号或者密码错误';
$this->flash->error($errmsg);
return $this->response->redirect('login');
}
/**
* 退出登录
*/
public function logoutAction()
{
global $user;
$user->logout();
return $this->response->redirect($this->request->getHTTPReferer(), true);
}
/**
* 邮箱注册验证
*/
public function emailValidateAction()
{
include_once(ROOT_PATH . 'includes/lib_passport.php');
$data = [];
$data['status'] = 'user_not_exist';
$hash = $this->request->getQuery('hash');
if ($hash) {
$id = intval(register_hash('decode', $hash));
$user = Model\User::findFirstByUserId($id);
if ($user) {
if ($user['is_validated'] == 0) {
Model\User::update(['is_validated' => 1], ['user_id' => $id]);
$data['status'] = 'success';
} else {
$data['status'] = 'validated';
}
$data['user'] = $user;
}
}
echo $this->viewoutput('pc/email-validate', $data);
}
/**
* PC 微信回调 oauth2 函数
*/
public function wechatOauth2CallbackAction()
{
global $ecs, $db, $user;
include_once(ROOT_PATH . 'includes/lib_passport.php');
$code = $this->request->getQuery('code');
if (empty($code)) {
echo '授权失败';
exit;
}
$state = $this->request->getQuery('state');
parse_str($state, $params);
if (! islocal() && ! empty($params['env']) && $params['env'] == 'local') {
return $this->response->redirect('http://liowang.dev.seafarer.me/shop/wechat/oauth2/callback?' . http_build_query($this->request->getQuery()));
}
$wechat = new WechatSDK;
$config = $this->config->wechat;
$config->appid = $config->open_web_appid;
$config->appsecret = $config->open_web_secret;
$wechat->init($config);
$response = $wechat->getUserTokenByCode($code);
if (empty($response['errcode'])) {
// 授权成功
$ewxpcuser = Model\WxPcUser::findFirstByOpenid($response['openid']);
$data = [];
$isFirst = false;
$isNeedCreate = false;
$uuser = [];
$wxpcuser = [];
if ($ewxpcuser) {
// 用户已经存在
$euser = Model\User::findFirstByWxPcUserid($ewxpcuser['id']);
} else {
// 第一次访问
$isFirst = true;
$wxuser = Model\WxUser::findFirstByUnionid($response['unionid']);
if (empty($wxuser)) {
$isNeedCreate = true;
} else {
$euser = Model\User::findFirstByWxUserid($wxuser['id']);
}
}
$wxpcuser['openid'] = $response['openid'];
$wxpcuser['unionid'] = $response['unionid'];
$wxpcuser['access_token'] = $response['access_token'];
$wxpcuser['refresh_token'] = $response['refresh_token'];
$wxpcuser['expired_at'] = $response['expires_in'] + time() - 200;
$wxpcuser['scope'] = $response['scope'];
$wxpcuser['ip'] = $this->request->getClientAddress();
$info = $wechat->getUserInfoByOauth($response['openid'], $response['access_token']);
$wxpcuser['nickname'] = $info['nickname'];
$wxpcuser['gender'] = $info['sex'];
$wxpcuser['language'] = $info['language'];
$wxpcuser['city'] = $info['city'];
$wxpcuser['country'] = $info['country'];
$wxpcuser['province'] = $info['province'];
$wxpcuser['headimgurl'] = $info['headimgurl'];
if ($isFirst) {
// 首次PC扫码登录
$id = Model\WxPcUser::insert($wxpcuser);
$_SESSION['wx_pc_user_id'] = $id;
$uuser['wx_pc_userid'] = $id;
if (! $isNeedCreate) {
Model\User::update($uuser, "user_id = {$euser['user_id']}");
}
/**
if ($isNeedCreate) {
// 创建新用户
$uuser['avatar'] = 'shop/assets/pc/IMGS/defaultPhoto.png';
$name = '会员_' . time() . '_' . $id;
$uuser['nickname'] = $wxpcuser['nickname'];
$email = 'email_' . time() . '_' . $id . '@liowang.com';
if (register($name, '123123', $email) !== false) {;
$db->autoExecute($ecs->table('users'), $uuser, 'UPDATE', "user_id = {$_SESSION['user_id']}");
} else {
SystemException::error('授权失败');
}
} else {
// 绑定已在微信授权的用户
Model\User::update($uuser, "user_id = {$euser['user_id']}");
Model\WxPcUser::update($wxpcuser, "openid = '{$response['openid']}'");
$user->set_session($euser['user_name']);
$user->set_cookie($euser['user_name']);
update_user_info();
recalculate_price();
}**/
} else {
Model\WxPcUser::update($wxpcuser, "openid = '{$response['openid']}'");
$_SESSION['wx_pc_user_id'] = $ewxpcuser['id'];
}
if (empty($euser)) {
return $this->response->redirect('wechat/oauth2/register');
}
// 已注册直接登录
$user->set_session($euser['user_name']);
$user->set_cookie($euser['user_name']);
update_user_info();
recalculate_price();
$this->auth->loginUsingId($_SESSION['user_id']);
$_SESSION['pc_login'] = true;
} else {
SystemException::error($response['errmsg']);
}
$referer = $this->url->get('');
return $this->response->redirect($referer, true);
}
/**
* PC 微信账号登录绑定
*/
public function wechatOauth2RegisterAction()
{
global $ecs, $db, $user;
if (empty($_SESSION['wx_pc_user_id'])) {
return $this->response->redirect('login');
}
$data = [];
echo $this->viewoutput('pc/wechat-oauth2-register', $data);
}
/******** 需要登录授权 *********/
/**
* 个人信息
*/
public function infoAction()
{
$user = $this->auth->user();
list($user['birYear'], $user['birMonth'], $user['birDay']) = explode('-', $user['birthday']);
$user['birMonth'] = ltrim($user['birMonth'], 0);
$user['birDay'] = ltrim($user['birDay'], 0);
if (strpos($user['avatar'], 'http://') === false && strpos($user['avatar'], 'https://') === false) {
$user['avatar'] = 'http://' . $this->request->getHttpHost() . '/' . $user['avatar'];
}
$data = [];
$data['user'] = $user;
$data['navName'] = '';
$data['userSidebar'] = 'user-info';
echo $this->viewoutput('pc/user-info', $data);
}
/**
* 购物车
*/
public function cartAction()
{
require(ROOT_PATH . 'includes/lib_order.php');
$user = $this->auth->user();
$mcart = new Model\Cart;
$cartCount = $mcart->getCount();
$cartGoods = get_cart_goods();
$discount = compute_discount();
$goodsids = [];
foreach ($cartGoods['goods_list'] as $goods) {
$goodsids[] = $goods['goods_id'];
}
if ($goodsids) {
$collectGoodsList = Model\CollectGoods::find(['conditions' => "user_id='$_SESSION[user_id]' AND " . db_create_in($goodsids, 'goods_id')]);
foreach ($cartGoods['goods_list'] as &$goods) {
$goods['is_collect'] = false;
foreach ($collectGoodsList as $row) {
if ($row['goods_id'] == $goods['goods_id']) {
$goods['is_collect'] = true;
break;
}
}
}
unset($goods);
}
$data = [];
$data['user'] = $user;
$data['cartGoods'] = $cartGoods;
$data['navName'] = '';
echo $this->viewoutput('pc/user-cart', $data);
}
/**
* 购物车结算
*/
public function cartSettleAction()
{
global $db, $ecs, $_CFG, $_LANG, $err;
require(ROOT_PATH . 'includes/lib_order.php');
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'languages/' .$_CFG['lang']. '/shopping_flow.php');
$user = $this->auth->user();
$recids = $this->request->getQuery('rec_ids') ?: [];
$cartGoods = get_cart_goods($recids);
if (empty($cartGoods['goods_list'])) {
// 购物车为空
return $this->response->redirect('user/cart');
}
$discount = compute_discount();
$data = [];
$address_id = intval($this->request->getQuery('address_id'));
if ($address_id == 0) {
$address_id = $user['address_id'];
}
$address = Model\UserAddress::findFirst(['conditions' => "address_id = $address_id AND user_id = {$user['user_id']}"]);
$addressList = Model\UserAddress::find(['conditions' => "address_id != $address_id AND user_id = {$user['user_id']}", 'order' => 'address_id DESC']);
if ($address) {
array_unshift($addressList, $address);
}
$regions = Model\Region::getAll();
foreach ($addressList as &$row) {
$country = $regions[$row['country']];
$province = $country['list'][$row['province']];
$city = $province['list'][$row['city']];
$district = $city['list'][$row['district']];
$row['address_detail'] = $province['region_name'] . $city['region_name'] . $district['region_name'] . $row['address'];
}
unset($row);
$data['addressList'] = $addressList;
foreach ($cartGoods['goods_list'] as &$goods) {
//$goods['goods_name'] = mb_substr($goods['goods_name'], 0, 15, 'utf-8') . '......';
}
unset($goods);
$regions = Model\Region::getAll();
$data['user'] = $user;
$data['regions'] = $regions;
$data['cartGoods'] = $cartGoods;
$data['navName'] = '';
echo $this->viewoutput('pc/cart-settle', $data);
}
/**
* 已购买的商品
*/
public function boughtListAction()
{
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'includes/lib_order.php');
$user = $this->auth->user();
$mcart = new Model\Cart;
global $db, $ecs;
$page = intval($this->request->getQuery('page')) ?: 1;
$limit = 10;
$offset = ($page - 1) * $limit;
$userid = $_SESSION['user_id'];
$morder = new Model\OrderInfo;
$arr = array();
$osArr = [];
$ssArr = [];
$psArr = [];
// 需要评论的order_id
$result = $morder->needComment();
$needCommentOrderids = [];
foreach ($result as $row) {
$needCommentOrderids[$row['order_id']] = $row['order_id'];
}
$osArr[] = OS_CONFIRMED;
$osArr[] = OS_SPLITED;
$ssArr[] = SS_RECEIVED;
$psArr[] = PS_PAYED;
$ossql = $sssql = $pssql = '';
if ($osArr) {
$ossql = ' AND ' . db_create_in($osArr, 'order_status');
}
if ($ssArr) {
$sssql = ' AND ' . db_create_in($ssArr, 'shipping_status');
}
if ($psArr) {
$pssql = ' AND ' . db_create_in($psArr, 'pay_status');
}
$sql = "SELECT order_id, order_sn, order_status, shipping_status, pay_status, add_time, " .
"(goods_amount + shipping_fee + insure_fee + pay_fee + pack_fee + card_fee + tax - discount) AS total_fee ".
" FROM " .$GLOBALS['ecs']->table('order_info') .
' WHERE 1 = 1';
$sql .= " AND user_id = '$userid' $ossql $sssql $pssql ORDER BY add_time DESC";
//$res = $GLOBALS['db']->SelectLimit($sql, 20, ($page - 1) * 20);
$res = $GLOBALS['db']->query($sql);
$orderids = [];
while ($row = $GLOBALS['db']->fetchRow($res))
{
$orderids[] = $row['order_id'];
$row['shipping_status'] = ($row['shipping_status'] == SS_SHIPPED_ING) ? SS_PREPARING : $row['shipping_status'];
$arr[$row['order_id']] = array('order_id' => $row['order_id'],
'order_sn' => $row['order_sn'],
'order_time' => local_date($GLOBALS['_CFG']['time_format'], $row['add_time']),
'order_status' => $row['order_status'],
'shipping_status' => $row['shipping_status'],
'pay_status' => $row['pay_status'],
'total_fee' => price_format($row['total_fee'], false),
'needComment' => isset($needCommentOrderids[$row['order_id']]) ? true : false,
'goods' => [],
);
}
$orders = $arr;
$count = 0;
if ($orderids) {
$orderidsStr = implode(',', $orderids);
/* 订单商品 */
$params = [
'alias' => 'og',
'columns' => "og.rec_id, og.order_id, og.goods_id, og.goods_name, og.goods_sn, og.market_price, og.goods_number, " .
"og.goods_price, og.goods_attr, og.is_real, og.parent_id, og.is_gift, " .
"og.goods_price * og.goods_number AS subtotal, og.extension_code, g.goods_thumb, g.goods_img " .
',c.comment_id ',
'conditions' => "og.order_id IN ($orderidsStr)",
'offset' => $offset,
'limit' => $limit,
'join' => [
[
'type' => 'LEFT',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = og.goods_id'
],
[
'type' => 'LEFT',
'table' => 'comment',
'alias' => 'c',
'on' => 'c.id_value = og.goods_id AND c.order_id = og.order_id '
]
],
];
$colls = Model\OrderGoods::find($params);
unset($params['limit'], $params['offset']);
$count = Model\OrderGoods::count($params);
foreach ($colls as $row) {
if (isset($orders[$row['order_id']])) {
$row['market_price'] = price_format($row['market_price'], false);
$row['goods_price'] = price_format($row['goods_price'], false);
$row['subtotal'] = price_format($row['subtotal'], false);
$orders[$row['order_id']]['goods'][] = $row;
}
}
}
$totalPage = $count ? intval(($count - 1) / $limit) + 1 : 1;
$data = [];
$data['user'] = $user;
$data['page'] = $page;
$data['totalPage'] = $totalPage;
$data['count'] = $count;
$data['orders'] = $orders;
$data['navName'] = '';
$data['userSidebar'] = 'bought-list';
echo $this->viewoutput('pc/bought-list', $data);
}
/**
* 我的收藏
*/
public function collectListAction()
{
global $db, $ecs;
$user = $this->auth->user();
$page = intval($this->request->getQuery('page')) ?: 1;
$ctype = $this->request->getQuery('collect_type') == 'brand' ? 'brand' : 'goods';
$limit = 10;
$offset = ($page - 1) * $limit;
$userid = $user['user_id'];
$data = [];
if ($ctype == 'goods') {
$params = [
'alias' => 'c',
'columns' => 'g.goods_id, g.goods_name, g.market_price, g.shop_price AS org_price, '.
"IFNULL(mp.user_price, g.shop_price * '$_SESSION[discount]') AS shop_price, g.goods_img, ".
'g.promote_price, g.promote_start_date,g.promote_end_date, c.rec_id, c.is_attention',
'conditions' => "c.user_id = '$userid' ORDER BY c.rec_id DESC",
'offset' => $offset,
'limit' => $limit,
'join' => [
[
'type' => 'LEFT',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = c.goods_id'
],
[
'type' => 'LEFT',
'table' => 'member_price',
'alias' => 'mp',
'on' => "mp.goods_id = g.goods_id AND mp.user_rank = '$_SESSION[user_rank]' "
],
],
];
$goodsList = Model\CollectGoods::find($params);
foreach ($goodsList as &$row) {
if ($row['promote_price'] > 0) {
$promote_price = bargain_price($row['promote_price'], $row['promote_start_date'], $row['promote_end_date']);
$row['promote_price'] = $promote_price > 0 ? price_format($promote_price) : '';
} else {
$row['promote_price'] = '';
}
if ($row['promote_price'] > 0) {
$row['price'] = $row['promote_price'];
} else {
$row['price'] = $row['shop_price'];
}
}
unset($row);
unset($params['limit'], $params['offset']);
$count = Model\CollectGoods::count($params);
$data['list'] = $goodsList;
} else {
$params = [
'alias' => 'cb',
'columns' => 'b.*',
'conditions' => "cb.user_id = '$userid'",
'offset' => $offset,
'limit' => $limit,
'order' => 'cb.id DESC',
'join' => [
[
'type' => 'LEFT',
'table' => 'brand',
'alias' => 'b',
'on' => 'b.brand_id = cb.brand_id'
]
],
];
$brandList = Model\CollectBrand::find($params);
foreach ($brandList as &$row) {
}
unset($row);
unset($params['limit'], $params['offset']);
$count = Model\CollectBrand::count($params);
$data['list'] = $brandList;
}
$paginator = new \Juice\Paginator(['type' => 'emptyArray', 'config' => [
'count' => $count,
'limit' => $limit,
'page' => $page,
]]);
$paginate = $paginator->getPaginate();
$data['user'] = $user;
$data['ctype'] = $ctype;
$data['paginate'] = $paginate;
$data['navName'] = '';
$data['userSidebar'] = 'collect-list';
echo $this->viewoutput('pc/collect-list', $data);
}
/**
* 评论商品
*/
public function commentAction()
{
global $db, $ecs;
$orderid = intval($this->request->getQuery('order_id'));
$goodsid = intval($this->request->getQuery('goods_id'));
if (! $orderid || ! $goodsid) {
return $this->response->redirect('user/bought/list');
}
$user = $this->auth->user();
$goods = Model\OrderGoods::findFirst([
'alias' => 'og',
'columns' => "og.rec_id, og.goods_id, og.goods_name, og.goods_attr, og.goods_sn, og.goods_number, goods_price, g.goods_thumb, o.add_time, o.order_id",
'conditions' => "og.order_id = $orderid AND og.goods_id = $goodsid AND o.user_id = " . $user['user_id'] . ' AND o.shipping_status = 2 AND c.comment_id IS NULL',
'join' => [
[
'type' => 'INNER',
'table' => 'order_info',
'alias' => 'o',
'on' => 'o.order_id = og.order_id'
],
[
'type' => 'INNER',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = og.goods_id'
],
[
'type' => 'LEFT',
'table' => 'comment',
'alias' => 'c',
'on' => 'c.order_id = og.order_id AND c.id_value = og.goods_id'
]
],
]);
if (empty($goods)) {
return $this->response->redirect('user/bought/list');
}
$goods['add_time_format'] = local_date('Y-m-d', $goods['add_time']);
$data = [];
$data['user'] = $user;
$data['goods'] = $goods;
$data['navName'] = '';
$data['userSidebar'] = 'bought-list';
echo $this->viewoutput('pc/comment', $data);
}
/**
* 订单列表
*/
public function orderListAction()
{
global $db, $ecs;
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'includes/lib_order.php');
$data = [];
$user = $this->auth->user();
$mcart = new Model\Cart;
$page = intval($this->request->getQuery('page')) ?: 1;
$limit = 10;
$offset = ($page - 1) * $limit;
$status = $this->request->getQuery('status') ?: 0;
$userid = $_SESSION['user_id'];
$morder = new Model\OrderInfo;
// 需要评论的order_id
$result = $morder->needComment();
$needCommentOrderids = [];
foreach ($result as $row) {
$needCommentOrderids[$row['order_id']] = $row['order_id'];
}
if (! empty($status)) {
$orderAttr = Model\OrderInfo::$orderStatus[$status];
}
$ossql = $sssql = $pssql = '';
if (! empty($orderAttr['os'])) {
$ossql = ' AND ' . db_create_in($orderAttr['os'], 'o.order_status');
}
if (! empty($orderAttr['ss'])) {
$sssql = ' AND ' . db_create_in($orderAttr['ss'], 'o.shipping_status');
}
if (! empty($orderAttr['ps'])) {
$pssql = ' AND ' . db_create_in($orderAttr['ps'], 'o.pay_status');
}
$conditions = '1 = 1';
if ($status == 4) {
if (empty($needCommentOrderids)) {
$conditions .= ' AND 1 = 2';
} else {
$conditions .= ' AND o.order_id IN (' . implode(',', $needCommentOrderids) . ')';
}
}
$conditions .= " AND o.user_id = '$userid' $ossql $sssql $pssql ORDER BY o.add_time DESC LIMIT $offset, $limit";
$colls = Model\OrderInfo::find([
'alias' => 'o',
'columns' => "o.order_id, o.order_sn, o.order_status, o.shipping_status, o.pay_status, o.add_time, " .
"(o.goods_amount + o.shipping_fee + o.insure_fee + o.pay_fee + o.pack_fee + o.card_fee + o.tax - o.discount) AS total_fee, " .
"do.delivery_id, do.delivery_sn"
,
'conditions' => $conditions,
'join' => [
[
'type' => 'LEFT',
'table' => 'delivery_order',
'alias' => 'do',
'on' => 'o.order_id = do.order_id'
]
],
]);
$orderids = [];
$orders = [];
foreach ($colls as $row) {
$orderids[] = $row['order_id'];
$row['shipping_status'] = ($row['shipping_status'] == SS_SHIPPED_ING) ? SS_PREPARING : $row['shipping_status'];
$row['status'] = Model\OrderInfo::getOrderStatus($row);
$orders[$row['order_id']] = [
'order_id' => $row['order_id'],
'status' => $row['status'],
'order_sn' => $row['order_sn'],
'order_time' => local_date($GLOBALS['_CFG']['time_format'], $row['add_time']),
'order_status' => $row['order_status'],
'shipping_status' => $row['shipping_status'],
'pay_status' => $row['pay_status'],
'total_fee' => price_format($row['total_fee'], false),
'delivery_id' => $row['delivery_id'],
'delivery_sn' => $row['delivery_sn'],
'needComment' => isset($needCommentOrderids[$row['order_id']]) ? true : false,
'goods' => [],
];
}
$statistics = $morder->getAllCount();
$count = empty($status) ? $statistics['total'] : $statistics['count'][$status];
$paginator = new \Juice\Paginator(['type' => 'emptyArray', 'config' => [
'count' => $count,
'limit' => $limit,
'page' => $page,
]]);
$paginate = $paginator->getPaginate();
$data['paginate'] = $paginate;
if ($orderids) {
$orderidsStr = implode(',', $orderids);
$params = [
'alias' => 'og',
'columns' => "og.rec_id, og.order_id, og.goods_id, og.goods_name, og.goods_sn, og.market_price, og.goods_number, " .
"og.goods_price, og.goods_attr, og.is_real, og.parent_id, og.is_gift, " .
"og.goods_price * og.goods_number AS subtotal, og.extension_code, g.goods_thumb, g.goods_img " .
',c.comment_id ',
'conditions' => "og.order_id IN ($orderidsStr)",
'join' => [
[
'type' => 'LEFT',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = og.goods_id'
],
[
'type' => 'LEFT',
'table' => 'comment',
'alias' => 'c',
'on' => 'c.id_value = og.goods_id AND c.order_id = og.order_id '
],
],
];
$colls = Model\OrderGoods::find($params);
foreach ($colls as $row) {
if (isset($orders[$row['order_id']])) {
$row['market_price'] = price_format($row['market_price'], false);
$row['goods_price'] = price_format($row['goods_price'], false);
$row['subtotal'] = price_format($row['subtotal'], false);
$orders[$row['order_id']]['goods'][] = $row;
}
}
}
$data['user'] = $user;
$data['orders'] = $orders;
$data['status'] = $status;
$data['statistics'] = $statistics;
$data['navName'] = '';
$data['userSidebar'] = 'order-list';
echo $this->viewoutput('pc/order-list', $data);
}
/**
* 地址
*/
public function addressListAction()
{
global $db, $ecs, $_CFG, $_LANG, $err;
$user = $this->auth->user();
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'languages/' .$_CFG['lang']. '/shopping_flow.php');
$addressList = Model\UserAddress::findByUserId($user['user_id']);
$regionsids = [];
foreach ($addressList as $region_id => $row) {
$row['province'] = isset($row['province']) ? intval($row['province']) : 0;
$row['city'] = isset($row['city']) ? intval($row['city']) : 0;
$row['district'] = isset($row['district']) ? intval($row['district']) : 0;
$regionsids[] = $row['district'];
$regionsids[] = $row['province'];
$regionsids[] = $row['city'];
}
$regions = [];
if ($regionsids) {
$idsstr = implode(', ', $regionsids);
$sql = 'SELECT region_id, region_name FROM ' . $GLOBALS['ecs']->table('region') .
" WHERE region_id IN ($idsstr)";
$res = $GLOBALS['db']->query($sql);
while ($row = $GLOBALS['db']->fetchRow($res)) {
$regions[$row['region_id']] = $row;
}
}
foreach ($addressList as $key => &$row) {
$row['province_name'] = isset($regions[$row['province']]) ? $regions[$row['province']]['region_name'] : '';
$row['city_name'] = isset($regions[$row['city']]) ? $regions[$row['city']]['region_name'] : '';
$row['district_name'] = isset($regions[$row['district']]) ? $regions[$row['district']]['region_name'] : '';
}
unset($row);
$status = $this->request->getQuery('status');
$_SESSION['origin_from'] = $status;
$regions = Model\Region::getAll();
$data = [];
$data['user'] = $user;
$data['addressList'] = $addressList;
$data['regions'] = $regions;
$data['navName'] = '';
$data['userSidebar'] = 'address-list';
echo $this->viewoutput('pc/address-list', $data);
}
/**
* 代理信息
*/
public function agentInfoAction()
{
global $db, $ecs, $_CFG, $_LANG, $err;
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'languages/' .$_CFG['lang']. '/shopping_flow.php');
$user = $this->auth->user();
$data = [];
$agent = Model\Agent::findFirstByAgentId($user['agent_id']);
if (empty($agent)) {
$data['isEdit'] = false;
} else {
$data['isEdit'] = true;
}
$data['user'] = $user;
$data['agent'] = $agent;
$data['navName'] = '';
$data['userSidebar'] = 'agent';
echo $this->viewoutput('pc/agent-info', $data);
}
/**
* 代理订单信息
*/
public function agentOrderListAction()
{
global $db, $ecs, $_CFG, $_LANG, $err;
include_once(ROOT_PATH . 'includes/lib_transaction.php');
include_once(ROOT_PATH . 'languages/' .$_CFG['lang']. '/shopping_flow.php');
$user = $this->auth->user();
$page = intval($this->request->getQuery('page')) ?: 1;
$limit = 10;
$offset = ($page - 1) * $limit;
$ossql = $sssql = $pssql = '';
if (! empty($orderAttr['os'])) {
$ossql = ' AND ' . db_create_in($orderAttr['os'], 'order_status');
}
if (! empty($orderAttr['ss'])) {
$sssql = ' AND ' . db_create_in($orderAttr['ss'], 'shipping_status');
}
if (! empty($orderAttr['ps'])) {
$pssql = ' AND ' . db_create_in($orderAttr['ps'], 'pay_status');
}
$orderids = [];
$arr = [];
$count = 0;
if ($user['agent_id']) {
$params = [
'alias' => 'o',
'columns' => "o.order_id, o.order_sn, o.order_status, o.shipping_status, o.pay_status, o.add_time, " .
"(o.goods_amount + o.shipping_fee + o.insure_fee + o.pay_fee + o.pack_fee + o.card_fee + o.tax - o.discount) AS total_fee , u.user_name",
'conditions' => "o.agent_id = '{$user['agent_id']}' $ossql $sssql $pssql",
'offset' => $offset,
'limit' => $limit,
'join' => [
[
'type' => 'LEFT',
'table' => 'users',
'alias' => 'u',
'on' => 'u.user_id = o.user_id'
]
],
'order' => 'add_time DESC',
];
$colls = Model\OrderInfo::find($params);
foreach ($colls as $row)
{
$orderids[] = $row['order_id'];
$row['shipping_status'] = ($row['shipping_status'] == SS_SHIPPED_ING) ? SS_PREPARING : $row['shipping_status'];
$row['status'] = Model\OrderInfo::getOrderStatus($row);
$orderTime = local_date($GLOBALS['_CFG']['time_format'], $row['add_time']);
$dateArr = explode(' ', $orderTime);
$arr[$row['order_id']] = [
'order_id' => $row['order_id'],
'user_name' => $row['user_name'],
'status' => $row['status'],
'order_sn' => $row['order_sn'],
'order_time' => $orderTime,
'order_time_date' => $orderTime,
'order_time_arr' => $dateArr,
'order_status' => $row['order_status'],
'shipping_status' => $row['shipping_status'],
'pay_status' => $row['pay_status'],
'total_fee' => price_format($row['total_fee'], false),
'goods' => [],
];
}
unset($params['limit'], $params['offset']);
$count = Model\OrderInfo::count($params);
}
$paginator = new \Juice\Paginator(['type' => 'emptyArray', 'config' => [
'count' => $count,
'limit' => $limit,
'page' => $page,
]]);
$paginate = $paginator->getPaginate();
$orders = $arr;
if ($orderids) {
$orderidsStr = implode(',', $orderids);
$params = [
'alias' => 'og',
'columns' => "og.rec_id, og.order_id, og.goods_id, og.goods_name, og.goods_sn, og.market_price, og.goods_number, " .
"og.goods_price, og.goods_attr, og.is_real, og.parent_id, og.is_gift, " .
"og.goods_price * og.goods_number AS subtotal, og.extension_code, g.goods_thumb, g.goods_img " .
',c.comment_id ',
'conditions' => "og.order_id IN ($orderidsStr)",
'join' => [
[
'type' => 'LEFT',
'table' => 'goods',
'alias' => 'g',
'on' => 'g.goods_id = og.goods_id'
],
[
'type' => 'LEFT',
'table' => 'comment',
'alias' => 'c',
'on' => 'c.id_value = og.goods_id AND c.order_id = og.order_id '
]
],
];
$colls = Model\OrderGoods::find($params);
foreach ($colls as $row) {
if (isset($orders[$row['order_id']])) {
$row['market_price'] = price_format($row['market_price'], false);
$row['goods_price'] = price_format($row['goods_price'], false);
$row['subtotal'] = price_format($row['subtotal'], false);
$orders[$row['order_id']]['goods'][] = $row;
}
}
}
$data = [];
$data['user'] = $user;
$data['orders'] = $orders;
$data['paginate'] = $paginate;
$data['navName'] = '';
$data['userSidebar'] = 'agent-order-list';
echo $this->viewoutput('pc/agent-order-list', $data);
}
/**
* 绑定微信号
*/
public function wechatBindAction()
{
$user = $this->auth->user();
$wechat = new WechatSDK;
$url = 'http://www.liowang.com/shop/wechat/oauth2/callback/bind';
$state = ['env' => 'product'];
if (isLocal()) {
$state = ['env' => 'local'];
}
return $this->response->redirect($wechat->pcWebOauth2($url, $state), true);
}
/**
* PC 微信回调 oauth2 绑定已登录用户
*/
public function wechatOauth2CallbackBindAction()
{
global $ecs, $db, $user;
include_once(ROOT_PATH . 'includes/lib_passport.php');
$code = $this->request->getQuery('code');
if (empty($code)) {
echo '授权失败';
exit;
}
$state = $this->request->getQuery('state');
parse_str($state, $params);
if (! islocal() && ! empty($params['env']) && $params['env'] == 'local') {
return $this->response->redirect('http://liowang.dev.seafarer.me/shop/wechat/oauth2/callback/bind?' . http_build_query($this->request->getQuery()));
}
$wechat = new WechatSDK;
$config = $this->config->wechat;
$config->appid = $config->open_web_appid;
$config->appsecret = $config->open_web_secret;
$wechat->init($config);
$response = $wechat->getUserTokenByCode($code);
if (empty($response['errcode'])) {
// 授权成功
$ewxpcuser = Model\WxPcUser::findFirstByOpenid($response['openid']);
$data = [];
$isFirst = false;
$isNeedCreate = false;
$uuser = [];
$wxpcuser = [];
if ($ewxpcuser) {
// 用户已经存在
$euser = Model\User::findFirstByWxPcUserid($ewxpcuser['id']);
} else {
// 第一次访问
$isFirst = true;
$wxuser = Model\WxUser::findFirstByUnionid($response['unionid']);
if (empty($wxuser)) {
$isNeedCreate = true;
} else {
$euser = Model\User::findFirstByWxUserid($wxuser['id']);
}
}
$wxpcuser['openid'] = $response['openid'];
$wxpcuser['unionid'] = $response['unionid'];
$wxpcuser['access_token'] = $response['access_token'];
$wxpcuser['refresh_token'] = $response['refresh_token'];
$wxpcuser['expired_at'] = $response['expires_in'] + time() - 200;
$wxpcuser['scope'] = $response['scope'];
$wxpcuser['ip'] = $this->request->getClientAddress();
$info = $wechat->getUserInfoByOauth($response['openid'], $response['access_token']);
$wxpcuser['nickname'] = $info['nickname'];
$wxpcuser['gender'] = $info['sex'];
$wxpcuser['language'] = $info['language'];
$wxpcuser['city'] = $info['city'];
$wxpcuser['country'] = $info['country'];
$wxpcuser['province'] = $info['province'];
$wxpcuser['headimgurl'] = $info['headimgurl'];
if (! empty($euser) && $euser['user_id'] != $_SESSION['user_id']) {
exit('该微信已被绑定');
}
if ($isFirst) {
// 首次PC扫码登录
$id = Model\WxPcUser::insert($wxpcuser);
$uuser['wx_pc_userid'] = $id;
Model\User::update($uuser, "user_id = {$_SESSION['user_id']}");
} else {
$uuser['wx_pc_userid'] = $ewxpcuser['id'];
Model\User::update($uuser, "user_id = {$_SESSION['user_id']}");
Model\WxPcUser::update($wxpcuser, "openid = '{$response['openid']}'");
}
} else {
SystemException::error($response['errmsg']);
}
$referer = $this->url->get('user/info');
return $this->response->redirect($referer, true);
}
}
<?php
error_reporting(E_ALL);
try {
define('BASE_PATH', realpath('..') . '/');
define('APP_PATH', BASE_PATH . 'app/');
define('STORAGE_PATH', BASE_PATH . 'storage/');
// 系统环境变量,参考laravel模式
$systemEnvs = [
'local' => [
'juice-pc'
],
'production' => [
'10-10-92-111'
],
'testing' => [
'10-10-13-185'
],
];
/**
* 项目引导文件
*/
$config = include __DIR__ . "/../app/start/start.php";
/**
* Handle the request
*/
$application = new \Phalcon\Mvc\Application($di);
$application->useImplicitView(false); // 禁用自动渲染
echo $application->handle()->getContent();
} catch (\Exception $e) {
//echo $e;
throw $e;
莉旺时尚
注册成功
激活邮件已经发送到
PHP{2025-05-04 19:40:05|| file:/usr/local/www/liowang-shop-robotwar/shop/app/libraries/PrettyExceptions/Library/Phalcon/Utils/PrettyExceptions.php line:440 msg:Cannot modify header information - headers already sent by (output started at /usr/local/www/liowang-shop-robotwar/shop/app/libraries/PrettyExceptions/Library/Phalcon/Utils/PrettyExceptions.php:339)||/shop/login}