Browse Source

Merge pull request #269 from dodgepudding/master

XXE外部实体注入漏洞
dodge 8 years ago
parent
commit
43132e1ddb
7 changed files with 828 additions and 27 deletions
  1. 59 0
      Codeigniter/CI_Wechat.php
  2. 218 0
      Thinkphp/JsSdkPay.class.php
  3. 108 0
      Thinkphp/Wxauth.class.php
  4. 1 1
      qywechat.class.php
  5. 45 0
      test/getQRCode_test.php
  6. 16 0
      test/merchanttest.php
  7. 381 26
      wechat.class.php

+ 59 - 0
Codeigniter/CI_Wechat.php

@@ -0,0 +1,59 @@
+<?php
+defined('BASEPATH') OR exit('No direct script access allowed');
+
+/**
+ *  微信公众平台PHP-SDK, Codeigniter实例
+ *  @author nigelvon@gmail.com
+ *  @link https://github.com/dodgepudding/wechat-php-sdk
+ *  usage:
+ *  $this->load->library('CI_Wechat');
+ *  $this->ci_wechat->valid();
+ *  ...
+ *
+ */
+require_once(dirname(__FILE__) . '/wechat-php-sdk/wechat.class.php');
+
+class CI_Wechat extends Wechat {
+    protected $_CI;
+    public function __construct() {
+        $this->_CI =& get_instance();
+        $this->_CI->config->load('wechat');
+        $options = $this->_CI->config->item('wechat');
+
+        $this->_CI->load->driver('cache', array('adapter' => 'apc', 'backup' => 'file'));
+
+        parent::__construct($options);
+    }
+
+    /**
+     * 重载设置缓存
+     * @param string $cachename
+     * @param mixed $value
+     * @param int $expired
+     * @return boolean
+     */
+    protected function setCache($cachename, $value, $expired) {
+        return $this->_CI->cache->save($cachename, $value, $expired);
+    }
+
+    /**
+     * 重载获取缓存
+     * @param string $cachename
+     * @return mixed
+     */
+    protected function getCache($cachename) {
+        return $this->_CI->cache->get($cachename);
+    }
+
+    /**
+     * 重载清除缓存
+     * @param string $cachename
+     * @return boolean
+     */
+    protected function removeCache($cachename) {
+        return $this->_CI->cache->delete($cachename);
+    }
+}
+
+/* End of file CI_Wechat.php */
+/* Location: ./application/libraries/CI_Wechat.php */

+ 218 - 0
Thinkphp/JsSdkPay.class.php

@@ -0,0 +1,218 @@
+<?php
+/**
+ * 官方文档:http://mp.weixin.qq.com/wiki/7/aaa137b55fb2e0456bf8dd9148dd613f.html
+ * 微信支付:http://pay.weixin.qq.com/wiki/doc/api/index.php?chapter=9_1#
+ * 官方示例:http://demo.open.weixin.qq.com/jssdk/sample.zip
+ * UCToo示例:http://git.oschina.net/uctoo/uctoo/blob/master/Addons/Jssdk/Controller/JssdkController.class.php
+ * 
+ * 微信JSSDK支付类,主要实现了微信JSSDK支付,参考官方提供的示例文档,
+ * @命名空间版本
+ * @author uctoo (www.uctoo.com)
+ * @date 2015-5-15 14:10
+ */
+namespace Com;
+
+class JsSdkPay {
+  private $appId;
+  private $appSecret;
+  public $debug = false;
+  public $weObj;      //微信类实例
+  public $parameters;//获取prepay_id时的请求参数
+  //受理商ID,身份标识
+  public $MCHID = '';
+  //商户支付密钥Key。审核通过后,在微信商户平台中查看 https://pay.weixin.qq.com
+  public $KEY = '';
+
+  //=======【JSAPI路径设置】===================================
+  //获取access_token过程中的跳转uri,通过跳转将code传入jsapi支付页面
+  public $JS_API_CALL_URL = '';
+
+  //=======【证书路径设置】=====================================
+  //证书路径,注意应该填写绝对路径
+  public $SSLCERT_PATH = '/xxx/xxx/xxxx/WxPayPubHelper/cacert/apiclient_cert.pem';
+  public $SSLKEY_PATH = '/xxx/xxx/xxxx/WxPayPubHelper/cacert/apiclient_key.pem';
+
+  //=======【异步通知url设置】===================================
+  //异步通知url,商户根据实际开发过程设定
+  //C('url')."admin.php/order/notify_url.html";
+  public $NOTIFY_URL = '';
+
+  //=======【curl超时设置】===================================
+  //本例程通过curl使用HTTP POST方法,此处可修改其超时时间,默认为30秒
+  public  $CURL_TIMEOUT = 30;
+
+  public  $prepay_id;
+
+  public function __construct($options) {
+    $this->appId = $options['appid'];
+    $this->appSecret = $options['appsecret'];
+    $this->weObj = new TPWechat($options);
+  }
+
+  //微信支付相关方法
+  /**
+   * 	作用:格式化参数,签名过程需要使用
+   */
+  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;
+  }
+  /**
+   * 	作用:设置jsapi的参数
+   */
+  public function getParameters()
+  {
+    $jsApiObj["appId"] = $this->appId;           //请求生成支付签名时需要,js调起支付参数中不需要
+    $timeStamp = time();
+    $jsApiObj["timeStamp"] = "$timeStamp";      //用大写的timeStamp参数请求生成支付签名
+    $jsParamObj["timestamp"] = $timeStamp;      //用小写的timestamp参数生成js支付参数,还要注意数据类型,坑!
+    $jsParamObj["nonceStr"] = $jsApiObj["nonceStr"] = $this->weObj->generateNonceStr();
+    $jsParamObj["package"] = $jsApiObj["package"] = "prepay_id=$this->prepay_id";
+    $jsParamObj["signType"] = $jsApiObj["signType"] = "MD5";
+    $jsParamObj["paySign"] = $jsApiObj["paySign"] = $this->getSign($jsApiObj);
+
+    $jsParam = json_encode($jsParamObj);
+
+    return $jsParam;
+  }
+
+  /**
+   * 获取prepay_id
+   */
+  function getPrepayId()
+  {
+    $result = $this->xmlToArray($this->postXml());
+    $prepay_id = $result["prepay_id"];
+    return $prepay_id;
+  }
+  /**
+   * 	作用:将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
+   */
+  function postXml()
+  {
+    $xml = $this->createXml();
+    return  $this->postXmlCurl($xml,"https://api.mch.weixin.qq.com/pay/unifiedorder",$this->CURL_TIMEOUT);
+
+  }
+  /**
+   * 	作用:以post方式提交xml到对应的接口url
+   */
+  public function postXmlCurl($xml,$url,$second=30)
+  {
+    //初始化curl
+    $ch = curl_init();
+    //设置超时
+    curl_setopt($ch,CURLOP_TIMEOUT, $this->CURL_TIMEOUT);
+    //这里设置代理,如果有的话
+    //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)
+    {
+      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;
+    }
+  }
+  /**
+   * 	作用:设置标配的请求参数,生成签名,生成接口参数xml
+   */
+  function createXml()
+  {
+    $this->parameters["appid"] = $this->appId;//公众账号ID
+    $this->parameters["mch_id"] = $this->MCHID;//商户号
+    $this->parameters["nonce_str"] = $this->weObj->generateNonceStr();//随机字符串
+    $this->parameters["sign"] = $this->getSign($this->parameters);//签名
+    return  $this->arrayToXml($this->parameters);
+  }
+   /**
+   * 	作用:array转xml
+   */
+  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;
+  }
+  /**
+   * 	作用:生成签名
+   */
+  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->KEY;
+    //echo "【string2】".$String."</br>";
+    //签名步骤三:MD5加密
+    $String = md5($String);
+    //echo "【string3】 ".$String."</br>";
+    //签名步骤四:所有字符转为大写
+    $result_ = strtoupper($String);
+    //echo "【result】 ".$result_."</br>";
+    return $result_;
+  }
+	
+}
+

+ 108 - 0
Thinkphp/Wxauth.class.php

@@ -0,0 +1,108 @@
+<?php
+/**
+ * 微信oAuth认证示例
+ * 官方文档:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html
+ * UCToo示例:http://git.oschina.net/uctoo/uctoo/blob/master/Addons/Ucuser/UcuserAddon.class.php
+ *
+ * 微信oAuth认证类,适配Thinkphp框架,
+ * @命名空间版本
+ * @author uctoo (www.uctoo.com)
+ * @date 2015-5-15 14:10
+ */
+namespace Com;
+
+class Wxauth {
+	private $options;
+	public $open_id;
+	public $wxuser;
+	
+	public function __construct($options){
+		$this->options = $options;
+		$this->wxoauth();
+	}
+	
+	public function wxoauth(){
+		$scope = 'snsapi_base';
+		$code = isset($_GET['code'])?$_GET['code']:'';
+		$token_time = isset($_SESSION['token_time'])?$_SESSION['token_time']:0;
+		if(!$code && isset($_SESSION['open_id']) && isset($_SESSION['user_token']) && $token_time>time()-3600)
+		{
+			if (!$this->wxuser) {
+				$this->wxuser = $_SESSION['wxuser'];
+			}
+			$this->open_id = $_SESSION['open_id'];
+			return $this->open_id;
+		}
+		else
+		{
+
+			$options = array(
+					'token'=>$this->options["token"], //填写你设定的key
+                    'encodingaeskey'=>$this->options["encodingaeskey"], //填写加密用的EncodingAESKey
+					'appid'=>$this->options["appid"], //填写高级调用功能的app id
+					'appsecret'=>$this->options["appsecret"] //填写高级调用功能的密钥
+			);
+			$we_obj = new TPWechat($options);
+			if ($code) {
+				$json = $we_obj->getOauthAccessToken();
+				if (!$json) {
+					unset($_SESSION['wx_redirect']);
+					die('获取用户授权失败,请重新确认');
+				}
+				$_SESSION['open_id'] = $this->open_id = $json["openid"];
+				$access_token = $json['access_token'];
+				$_SESSION['user_token'] = $access_token;
+				$_SESSION['token_time'] = time();
+				$userinfo = $we_obj->getUserInfo($this->open_id);
+				if ($userinfo && !empty($userinfo['nickname'])) {
+					$this->wxuser = array(
+							'open_id'=>$this->open_id,
+							'nickname'=>$userinfo['nickname'],
+							'sex'=>intval($userinfo['sex']),
+							'location'=>$userinfo['province'].'-'.$userinfo['city'],
+							'avatar'=>$userinfo['headimgurl']
+					);
+				} elseif (strstr($json['scope'],'snsapi_userinfo')!==false) {
+					$userinfo = $we_obj->getOauthUserinfo($access_token,$this->open_id);
+					if ($userinfo && !empty($userinfo['nickname'])) {
+						$this->wxuser = array(
+								'open_id'=>$this->open_id,
+								'nickname'=>$userinfo['nickname'],
+								'sex'=>intval($userinfo['sex']),
+								'location'=>$userinfo['province'].'-'.$userinfo['city'],
+								'avatar'=>$userinfo['headimgurl']
+						);
+					} else {
+						return $this->open_id;
+					}
+				}
+				if ($this->wxuser) {
+					$_SESSION['wxuser'] = $this->wxuser;
+					$_SESSION['open_id'] =  $json["openid"];
+					unset($_SESSION['wx_redirect']);
+					return $this->open_id;
+				}
+				$scope = 'snsapi_userinfo';
+			}
+			if ($scope=='snsapi_base') {
+				$url = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
+				$_SESSION['wx_redirect'] = $url;
+			} else {
+				$url = $_SESSION['wx_redirect'];
+			}
+			if (!$url) {
+				unset($_SESSION['wx_redirect']);
+				die('获取用户授权失败');
+			}
+			$oauth_url = $we_obj->getOauthRedirect($url,"wxbase",$scope);
+			redirect ( $oauth_url );
+		}
+	}
+}
+//$options = array(
+//		'token'=>'uctoo', //填写你设定的key
+//		'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询
+//		'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥
+//);
+//$auth = new Wxauth($options);
+//var_dump($auth->wxuser);

+ 1 - 1
qywechat.class.php

@@ -202,7 +202,7 @@ class Wechat
 	            elseif ($value === true)
 	            $str .= 'true';
 	            else
-	                $str .= '"' . addslashes ( $value ) . '"'; //All other things
+	                $str .= '"' .addcslashes($value, "\\\"\n\r\t/"). '"'; //All other things
 	            // :TODO: Is there any more datatype we should be in the lookout for? (Object?)
 	            $parts [] = $str;
 	        }

+ 45 - 0
test/getQRCode_test.php

@@ -0,0 +1,45 @@
+<?php
+/**
+ * 微信公共接口测试
+ *
+ */
+include("../wechat.class.php");
+
+function logdebug($text){
+	file_put_contents('../data/log.txt',$text."\n",FILE_APPEND);
+};
+$options = array(
+	'token'=>'tokenaccesskey', //填写你设定的key
+	'debug'=>true,
+	'logcallback'=>'logdebug'
+);
+$weObj = new Wechat($options);
+
+// check null $scene_id
+$qrcode = $weObj->getQRCode();
+if ($qrcode != false) {
+	echo "test failed.\n";
+	die();
+}
+
+// check bad $type
+$qrcode = $weObj->getQRCode(123, -1);
+if ($qrcode != false) {	echo "test failed.\n";	die();}
+
+// check bad $type
+$qrcode = $weObj->getQRCode(123, 5);
+if ($qrcode != false) {	echo "test failed.\n";	die();}
+
+// check bad $scene_id
+$qrcode = $weObj->getQRCode('ad', 0);
+if ($qrcode != false) {	echo "test failed.\n";	die();}
+
+// check bad $scene_id
+$qrcode = $weObj->getQRCode('ad', 1);
+if ($qrcode != false) {	echo "test failed.\n";	die();}
+
+// check bad $scene_id
+$qrcode = $weObj->getQRCode(123, 2);
+if ($qrcode != false) {	echo "test failed.\n";	die();}
+
+echo "test passed.\n";

+ 16 - 0
test/merchanttest.php

@@ -0,0 +1,16 @@
+<?php
+include "../wechat.class.php";
+
+$options = array(
+		'token'=>'XXXXXX', //填写你设定的key
+		'encodingaeskey'=>'XXXXXX', //填写加密用的EncodingAESKey,如接口为明文模式可忽略
+		'appid'=>'XXXXXX', //填写高级调用功能的app id
+		'appsecret'=>'XXXXXX' //填写高级调用功能的密钥
+	);
+$weObj = new Wechat($options);
+
+$order = $weObj->getOrderByID("13791169361138306965");
+var_dump($order);
+
+$order = $weObj->getOrderByFilter();
+var_dump($order);

+ 381 - 26
wechat.class.php

@@ -73,11 +73,15 @@ class Wechat
 	const EVENT_CARD_NOTPASS = 'card_not_pass_check';   //卡券 - 审核未通过
 	const EVENT_CARD_USER_GET = 'user_get_card';        //卡券 - 用户领取卡券
 	const EVENT_CARD_USER_DEL = 'user_del_card';        //卡券 - 用户删除卡券
+	const EVENT_MERCHANT_ORDER = 'merchant_order';        //微信小店 - 订单付款通知
 	const API_URL_PREFIX = 'https://api.weixin.qq.com/cgi-bin';
 	const AUTH_URL = '/token?grant_type=client_credential&';
 	const MENU_CREATE_URL = '/menu/create?';
 	const MENU_GET_URL = '/menu/get?';
 	const MENU_DELETE_URL = '/menu/delete?';
+	const MENU_ADDCONDITIONAL_URL = '/menu/addconditional?';
+	const MENU_DELCONDITIONAL_URL = '/menu/delconditional?';
+	const MENU_TRYMATCH_URL = '/menu/trymatch?';
 	const GET_TICKET_URL = '/ticket/getticket?';
 	const CALLBACKSERVER_GET_URL = '/getcallbackip?';
 	const QRCODE_CREATE_URL='/qrcode/create?';
@@ -87,6 +91,7 @@ class Wechat
 	const SHORT_URL='/shorturl?';
 	const USER_GET_URL='/user/get?';
 	const USER_INFO_URL='/user/info?';
+	const USERS_INFO_URL='/user/info/batchget?';
 	const USER_UPDATEREMARK_URL='/user/info/updateremark?';
 	const GROUP_GET_URL='/groups/get?';
 	const USER_GROUP_URL='/groups/getid?';
@@ -97,8 +102,8 @@ class Wechat
 	const CUSTOM_SEND_URL='/message/custom/send?';
 	const MEDIA_UPLOADNEWS_URL = '/media/uploadnews?';
 	const MASS_SEND_URL = '/message/mass/send?';
-	const TEMPLATE_SET_INDUSTRY_URL = '/message/template/api_set_industry?';
-	const TEMPLATE_ADD_TPL_URL = '/message/template/api_add_template?';
+	const TEMPLATE_SET_INDUSTRY_URL = '/template/api_set_industry?';
+	const TEMPLATE_ADD_TPL_URL = '/template/api_add_template?';
 	const TEMPLATE_SEND_URL = '/message/template/send?';
 	const MASS_SEND_GROUP_URL = '/message/mass/sendall?';
 	const MASS_DELETE_URL = '/message/mass/delete?';
@@ -143,7 +148,8 @@ class Wechat
 	const CARD_DELETE                     = '/card/delete?';
 	const CARD_UPDATE                     = '/card/update?';
 	const CARD_GET                        = '/card/get?';
-	const CARD_BATCHGET                   = '/card/batchget?';
+        const CARD_USER_GETCARDLIST         = '/card/user/getcardlist?';
+        const CARD_BATCHGET                   = '/card/batchget?';
 	const CARD_MODIFY_STOCK               = '/card/modifystock?';
 	const CARD_LOCATION_BATCHADD          = '/card/location/batchadd?';
 	const CARD_LOCATION_BATCHGET          = '/card/location/batchget?';
@@ -204,6 +210,11 @@ class Wechat
 	const SHAKEAROUND_USER_GETSHAKEINFO = '/shakearound/user/getshakeinfo?';//获取摇周边的设备及用户信息
 	const SHAKEAROUND_STATISTICS_DEVICE = '/shakearound/statistics/device?';//以设备为维度的数据统计接口
     const SHAKEAROUND_STATISTICS_PAGE = '/shakearound/statistics/page?';//以页面为维度的数据统计接口
+	///微信小店相关接口
+	const MERCHANT_ORDER_GETBYID = '/merchant/order/getbyid?';//根据订单ID获取订单详情
+	const MERCHANT_ORDER_GETBYFILTER = '/merchant/order/getbyfilter?';//根据订单状态/创建时间获取订单详情
+	const MERCHANT_ORDER_SETDELIVERY = '/merchant/order/setdelivery?';//设置订单发货信息
+	const MERCHANT_ORDER_CLOSE = '/merchant/order/close?';//关闭订单
 
 	private $token;
 	private $encodingAesKey;
@@ -212,6 +223,7 @@ class Wechat
 	private $appsecret;
 	private $access_token;
 	private $jsapi_ticket;
+	private $api_ticket;
 	private $user_token;
 	private $partnerid;
 	private $partnerkey;
@@ -809,6 +821,18 @@ class Wechat
 	    }
 	}
 
+	/**
+	 * 获取订单ID - 订单付款通知
+	 * 当Event为 merchant_order(订单付款通知)
+	 * @return orderId|boolean
+	 */
+	public function getRevOrderId(){
+		if (isset($this->_receive['OrderId']))     //订单 ID
+			return $this->_receive['OrderId'];
+		else
+			return false;
+	}
+
 	public static function xmlSafeStr($str)
 	{
 		return '<![CDATA['.preg_replace("/[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]/",'',$str).']]>';
@@ -1291,11 +1315,43 @@ class Wechat
 	    return $signPackage;
 	}
 
+    /**
+     * 获取卡券签名cardSign
+     * @param string $card_type 卡券的类型,不可为空,官方jssdk文档说这个值可空,但签名验证工具又必填这个值,官方文档到处是坑,
+     * @param string $card_id 卡券的ID,可空
+     * @param string $location_id 卡券的适用门店ID,可空
+     * @param string $timestamp 当前时间戳 (为空则自动生成)
+     * @param string $noncestr 随机串 (为空则自动生成)
+     * @param string $appid 用于多个appid时使用,可空
+     * @return array|bool 返回签名字串
+     */
+    public function getCardSign($card_type='',$card_id='',$code='',$location_id='',$timestamp=0, $noncestr='', $appid=''){
+        if (!$this->api_ticket && !$this->getJsCardTicket($appid)) return false;
+        if (!$timestamp)
+            $timestamp = time();
+        if (!$noncestr)
+            $noncestr = $this->generateNonceStr();
+        $arrdata = array("api_ticket" => $this->api_ticket,"app_id" => $this->appid,"card_id" => $card_id,"code" => $code,"card_type" => $card_type,"location_id" => $location_id,"timestamp" => $timestamp, "noncestr" => $noncestr );
+        $sign = $this->getTicketSignature($arrdata);
+        if (!$sign)
+            return false;
+        $signPackage = array(
+            "cardType"     => $card_type,
+            "cardId"       => $card_id,
+            "shopId"       => $location_id,         //location_id就是shopId
+            "nonceStr"  => $noncestr,
+            "timestamp" => $timestamp,
+            "cardSign" => $sign
+        );
+        return $signPackage;
+    }
+
 	/**
 	 * 微信api不支持中文转义的json结构
 	 * @param array $arr
 	 */
 	static function json_encode($arr) {
+		if (count($arr) == 0) return "[]";
 		$parts = array ();
 		$is_list = false;
 		//Find out if the given array is a numerical array
@@ -1363,18 +1419,18 @@ class Wechat
 	/**
 	 * 获取微信卡券api_ticket
 	 * @param string $appid 用于多个appid时使用,可空
-	 * @param string $jsapi_ticket 手动指定jsapi_ticket,非必要情况不建议用
+	 * @param string $api_ticket 手动指定api_ticket,非必要情况不建议用
 	 */
-	public function getJsCardTicket($appid='',$jsapi_ticket=''){
+	public function getJsCardTicket($appid='',$api_ticket=''){
 		if (!$this->access_token && !$this->checkAuth()) return false;
 		if (!$appid) $appid = $this->appid;
-		if ($jsapi_ticket) { //手动指定token,优先使用
-		    $this->jsapi_ticket = $jsapi_ticket;
-		    return $this->jsapi_ticket;
+		if ($api_ticket) { //手动指定token,优先使用
+		    $this->api_ticket = $api_ticket;
+		    return $this->api_ticket;
 		}
-		$authname = 'wechat_jsapi_ticket_wxcard'.$appid;
+		$authname = 'wechat_api_ticket_wxcard'.$appid;
 		if ($rs = $this->getCache($authname))  {
-			$this->jsapi_ticket = $rs;
+			$this->api_ticket = $rs;
 			return $rs;
 		}
 		$result = $this->http_get(self::API_URL_PREFIX.self::GET_TICKET_URL.'access_token='.$this->access_token.'&type=wx_card');
@@ -1386,10 +1442,10 @@ class Wechat
 				$this->errMsg = $json['errmsg'];
 				return false;
 			}
-			$this->jsapi_ticket = $json['ticket'];
+			$this->api_ticket = $json['ticket'];
 			$expire = $json['expires_in'] ? intval($json['expires_in'])-100 : 3600;
-			$this->setCache($authname,$this->jsapi_ticket,$expire);
-			return $this->jsapi_ticket;
+			$this->setCache($authname,$this->api_ticket,$expire);
+			return $this->api_ticket;
 		}
 		return false;
 	}
@@ -1557,6 +1613,72 @@ class Wechat
 	}
 
 	/**
+	 * 创建个性化菜单(认证后的订阅号可用)
+	 * @param array $data
+	 * @return bool
+	 *
+	 */
+	public function addconditionalMenu($data){
+		if (!$this->access_token && !$this->checkAuth()) return false;
+		$result = $this->http_post(self::API_URL_PREFIX.self::MENU_ADDCONDITIONAL_URL.'access_token='.$this->access_token,self::json_encode($data));
+		if ($result)
+		{
+			$json = json_decode($result,true);
+			if (!$json || !empty($json['errcode'])) {
+				$this->errCode = $json['errcode'];
+				$this->errMsg = $json['errmsg'];
+				return false;
+			}
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * 删除个性化菜单(认证后的订阅号可用)
+	 * @param $data {"menuid":"208379533"}
+	 *
+	 * @return bool
+	 */
+	public function delconditionalMenu($data){
+		if (!$this->access_token && !$this->checkAuth()) return false;
+		$result = $this->http_post(self::API_URL_PREFIX.self::MENU_DELCONDITIONAL_URL.'access_token='.$this->access_token,self::json_encode($data));
+		if ($result)
+		{
+			$json = json_decode($result,true);
+			if (!$json || !empty($json['errcode'])) {
+				$this->errCode = $json['errcode'];
+				$this->errMsg = $json['errmsg'];
+				return false;
+			}
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * 测试个性化菜单匹配结果(认证后的订阅号可用)
+	 * @param $data {"user_id":"weixin"} user_id可以是粉丝的OpenID,也可以是粉丝的微信号
+	 *
+	 * @return bool|array('button'=>array(....s))
+	 */
+	public function trymatchMenu($data){
+		if (!$this->access_token && !$this->checkAuth()) return false;
+		$result = $this->http_post(self::API_URL_PREFIX.self::MENU_TRYMATCH_URL.'access_token='.$this->access_token,self::json_encode($data));
+		if ($result)
+		{
+			$json = json_decode($result,true);
+			if (!$json || !empty($json['errcode'])) {
+				$this->errCode = $json['errcode'];
+				$this->errMsg = $json['errmsg'];
+				return false;
+			}
+			return $json;
+		}
+		return false;
+	}
+
+	/**
 	 * 上传临时素材,有效期为3天(认证后的订阅号可用)
 	 * 注意:上传大文件时可能需要先调用 set_time_limit(0) 避免超时
 	 * 注意:数组的键值任意,但文件名前必须加@,使用单引号以避免本地路径斜杠被转义
@@ -2029,24 +2151,50 @@ class Wechat
 	/**
 	 * 创建二维码ticket
 	 * @param int|string $scene_id 自定义追踪id,临时二维码只能用数值型
-	 * @param int $type 0:临时二维码;1:永久二维码(此时expire参数无效);2:永久二维码(此时expire参数无效)
-	 * @param int $expire 临时二维码有效期,最大为1800
-	 * @return array('ticket'=>'qrcode字串','expire_seconds'=>1800,'url'=>'二维码图片解析后的地址')
+	 * @param int $type 0:临时二维码;1:数值型永久二维码(此时expire参数无效);2:字符串型永久二维码(此时expire参数无效)
+	 * @param int $expire 临时二维码有效期,最大为604800
+	 * @return array('ticket'=>'qrcode字串','expire_seconds'=>604800,'url'=>'二维码图片解析后的地址')
 	 */
-	public function getQRCode($scene_id,$type=0,$expire=1800){
+	public function getQRCode($scene_id,$type=0,$expire=604800){
 		if (!$this->access_token && !$this->checkAuth()) return false;
-		$type = ($type && is_string($scene_id))?2:$type;
+		if (!isset($scene_id)) return false;
+		switch ($type) {
+			case '0':
+				if (!is_numeric($scene_id))
+					return false;
+				$action_name = 'QR_SCENE';
+				$action_info = array('scene'=>(array('scene_id'=>$scene_id)));
+				break;
+
+			case '1':
+				if (!is_numeric($scene_id))
+					return false;
+				$action_name = 'QR_LIMIT_SCENE';
+				$action_info = array('scene'=>(array('scene_id'=>$scene_id)));
+				break;
+
+			case '2':
+				if (!is_string($scene_id))
+					return false;
+				$action_name = 'QR_LIMIT_STR_SCENE';
+				$action_info = array('scene'=>(array('scene_str'=>$scene_id)));
+				break;
+
+			default:
+				return false;
+		}
+
 		$data = array(
-			'action_name'=>$type?($type == 2?"QR_LIMIT_STR_SCENE":"QR_LIMIT_SCENE"):"QR_SCENE",
-			'expire_seconds'=>$expire,
-			'action_info'=>array('scene'=>($type == 2?array('scene_str'=>$scene_id):array('scene_id'=>$scene_id)))
+			'action_name'    => $action_name,
+			'expire_seconds' => $expire,
+			'action_info'    => $action_info
 		);
-		if ($type == 1) {
+		if ($type) {
 			unset($data['expire_seconds']);
 		}
+
 		$result = $this->http_post(self::API_URL_PREFIX.self::QRCODE_CREATE_URL.'access_token='.$this->access_token,self::json_encode($data));
-		if ($result)
-		{
+		if ($result) {
 			$json = json_decode($result,true);
 			if (!$json || !empty($json['errcode'])) {
 				$this->errCode = $json['errcode'];
@@ -2145,12 +2293,34 @@ class Wechat
 	/**
 	 * 获取关注者详细信息
 	 * @param string $openid
+	 * @param string $lang 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语
 	 * @return array {subscribe,openid,nickname,sex,city,province,country,language,headimgurl,subscribe_time,[unionid]}
 	 * 注意:unionid字段 只有在用户将公众号绑定到微信开放平台账号后,才会出现。建议调用前用isset()检测一下
 	 */
-	public function getUserInfo($openid){
+	public function getUserInfo($openid, $lang = 'zh_CN'){
 		if (!$this->access_token && !$this->checkAuth()) return false;
-		$result = $this->http_get(self::API_URL_PREFIX.self::USER_INFO_URL.'access_token='.$this->access_token.'&openid='.$openid);
+		$result = $this->http_get(self::API_URL_PREFIX.self::USER_INFO_URL.'access_token='.$this->access_token.'&openid='.$openid.'&lang='.$lang);
+		if ($result)
+		{
+			$json = json_decode($result,true);
+			if (isset($json['errcode'])) {
+				$this->errCode = $json['errcode'];
+				$this->errMsg = $json['errmsg'];
+				return false;
+			}
+			return $json;
+		}
+		return false;
+	}
+	/**
+	 * 批量获取关注者详细信息
+	 * @param array $openids user_list{{'openid:xxxxxx'},{},{}}
+	 * @return array user_info_list{subscribe,openid,nickname,sex,city,province,country,language,headimgurl,subscribe_time,[unionid]}{}{}...
+	 * 注意:unionid字段 只有在用户将公众号绑定到微信开放平台账号后,才会出现。建议调用前用isset()检测一下
+	 */
+	public function getUsersInfo($openids){
+		if (!$this->access_token && !$this->checkAuth()) return false;
+		$result = $this->http_post(self::API_URL_PREFIX.self::USERS_INFO_URL.'access_token='.$this->access_token,json_encode($openids));
 		if ($result)
 		{
 			$json = json_decode($result,true);
@@ -3050,6 +3220,40 @@ class Wechat
     }
 
     /**
+     * 获取用户已领取卡券接口
+     * @param string $openid
+     * @param string $card_id
+     * @return boolean|array    返回数组信息比较复杂,请参看卡券接口文档
+     * 成功返回结果
+     *  {
+     * "errcode":0,
+     * "errmsg":"ok",
+     * "card_list": [
+     * {"code": "xxx1434079154", "card_id": "xxxxxxxxxx"},
+     * {"code": "xxx1434079155", "card_id": "xxxxxxxxxx"}
+     * ]
+     * }
+     */
+    public function getUserCardList($openid,$card_id) {
+        $data = array(
+            'openid' => $openid,
+            'card_id' => $card_id
+        );
+        if (!$this->access_token && !$this->checkAuth()) return false;
+        $result = $this->http_post(self::API_BASE_URL_PREFIX . self::CARD_USER_GETCARDLIST . 'access_token=' . $this->access_token, self::json_encode($data));
+        if ($result) {
+            $json = json_decode($result, true);
+            if (!$json || !empty($json['errcode'])) {
+                $this->errCode = $json['errcode'];
+                $this->errMsg  = $json['errmsg'];
+                return false;
+            }
+            return $json;
+        }
+        return false;
+    }
+
+    /**
      * 获取颜色列表
 	 * 获得卡券的最新颜色列表,用于创建卡券
 	 * @return boolean|array   返回数组请参看 微信卡券接口文档 的json格式
@@ -4201,6 +4405,157 @@ class Wechat
         }
         return false;
     }
+
+	/**
+	 * 根据订单ID获取订单详情
+	 * @param string $order_id 订单ID
+	 * @return order array|bool
+	 */
+	public function getOrderByID($order_id){
+		if (!$this->access_token && !$this->checkAuth()) return false;
+		if (!$order_id) return false;
+
+		$data = array(
+			'order_id'=>$order_id
+		);
+		$result = $this->http_post(self::API_BASE_URL_PREFIX.self::MERCHANT_ORDER_GETBYID.'access_token='.$this->access_token, self::json_encode($data));
+		if ($result)
+		{
+			$json = json_decode($result,true);
+			if (isset($json['errcode']) && $json['errcode']) {
+				$this->errCode = $json['errcode'];
+				$this->errMsg = $json['errmsg'];
+				return false;
+			}
+			return $json['order'];
+		}
+		return false;
+	}
+
+	/**
+	 * 根据订单状态/创建时间获取订单详情
+	 * @param int $status 订单状态(不带该字段-全部状态, 2-待发货, 3-已发货, 5-已完成, 8-维权中, )
+	 * @param int $begintime 订单创建时间起始时间(不带该字段则不按照时间做筛选)
+	 * @param int $endtime 订单创建时间终止时间(不带该字段则不按照时间做筛选)
+	 * @return order list array|bool
+	 */
+	public function getOrderByFilter($status = null, $begintime = null, $endtime = null){
+		if (!$this->access_token && !$this->checkAuth()) return false;
+
+		$data = array();
+
+		$valid_status = array(2, 3, 5, 8);
+		if (is_numeric($status) && in_array($status, $valid_status)) {
+			$data['status'] = $status;
+		}
+
+		if (is_numeric($begintime) && is_numeric($endtime)) {
+			$data['begintime'] = $begintime;
+			$data['endtime'] = $endtime;
+		}
+		$result = $this->http_post(self::API_BASE_URL_PREFIX.self::MERCHANT_ORDER_GETBYFILTER.'access_token='.$this->access_token, self::json_encode($data));
+		if ($result)
+		{
+			$json = json_decode($result,true);
+			if (isset($json['errcode']) && $json['errcode']) {
+				$this->errCode = $json['errcode'];
+				$this->errMsg = $json['errmsg'];
+				return false;
+			}
+			return $json['order_list'];
+		}
+		return false;
+	}
+
+	/**
+	 * 设置订单发货信息
+	 * @param string $order_id 订单 ID
+	 * @param int $need_delivery 商品是否需要物流(0-不需要,1-需要)
+	 * @param string $delivery_company 物流公司 ID
+	 * @param string $delivery_track_no 运单 ID
+	 * @param int $is_others 是否为 6.4.5 表之外的其它物流公司(0-否,1-是)
+	 * @return bool
+	 */
+	public function setOrderDelivery($order_id, $need_delivery = 0, $delivery_company = null, $delivery_track_no = null, $is_others = 0){
+		if (!$this->access_token && !$this->checkAuth()) return false;
+		if (!$order_id) return false;
+
+		$data = array();
+		$data['order_id'] = $order_id;
+		if ($need_delivery) {
+			$data['delivery_company'] = $delivery_company;
+			$data['delivery_track_no'] = $delivery_track_no;
+			$data['is_others'] = $is_others;
+		}
+		else {
+			$data['need_delivery'] = $need_delivery;
+		}
+
+		$result = $this->http_post(self::API_BASE_URL_PREFIX.self::MERCHANT_ORDER_SETDELIVERY.'access_token='.$this->access_token, self::json_encode($data));
+		if ($result)
+		{
+			$json = json_decode($result,true);
+			if (isset($json['errcode']) && $json['errcode']) {
+				$this->errCode = $json['errcode'];
+				$this->errMsg = $json['errmsg'];
+				return false;
+			}
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * 关闭订单
+	 * @param string $order_id 订单 ID
+	 * @return bool
+	 */
+	public function closeOrder($order_id){
+		if (!$this->access_token && !$this->checkAuth()) return false;
+		if (!$order_id) return false;
+
+		$data = array(
+			'order_id'=>$order_id
+		);
+
+		$result = $this->http_post(self::API_BASE_URL_PREFIX.self::MERCHANT_ORDER_CLOSE.'access_token='.$this->access_token, self::json_encode($data));
+		if ($result)
+		{
+			$json = json_decode($result,true);
+			if (isset($json['errcode']) && $json['errcode']) {
+				$this->errCode = $json['errcode'];
+				$this->errMsg = $json['errmsg'];
+				return false;
+			}
+			return true;
+		}
+		return false;
+	}
+
+	private function parseSkuInfo($skuInfo) {
+		$skuInfo = str_replace("\$", "", $skuInfo);
+		$matches = explode(";", $skuInfo);
+
+		$result = array();
+		foreach ($matches as $matche) {
+			$arrs = explode(":", $matche);
+			$result[$arrs[0]] = $arrs[1];
+		}
+
+		return $result;
+	}
+
+	/**
+	 * 获取订单SkuInfo - 订单付款通知
+	 * 当Event为 merchant_order(订单付款通知)
+	 * @return array|boolean
+	 */
+	public function getRevOrderSkuInfo(){
+		if (isset($this->_receive['SkuInfo']))     //订单 SkuInfo
+			return $this->parseSkuInfo($this->_receive['SkuInfo']);
+		else
+			return false;
+	}
 }
 /**
  * PKCS7Encoder class