Browse Source

add wechat payment API support in php sdk and js sdk.

dodgepudding 11 years ago
parent
commit
fb38ac8858
7 changed files with 730 additions and 11 deletions
  1. 44 2
      README.md
  2. 216 0
      Thinkphp/Wechat.class.php
  3. 98 0
      test/auth.php
  4. 2 2
      test/test3.php
  5. 42 0
      test/weshare.html
  6. 214 0
      wechat.class.php
  7. 114 7
      wechat.js

+ 44 - 2
README.md

@@ -11,6 +11,39 @@ http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%9
 
 1. wechat.class.php  
 调用官方API,具有更灵活的消息分类响应方式,支持链式调用操作 ; 
+
+### 主要功能 
+- 接入验证 (初级权限)
+- 自动回复(文本、图片、语音、视频、音乐、图文)(初级权限)
+- 菜单操作(查询、创建、删除)(菜单权限)
+- 客服消息(文本、图片、语音、视频、音乐、图文)(认证权限)
+- 二维码(创建临时、永久二维码,获取二维码URL)(认证权限)
+- 分组操作(查询、创建、修改、移动用户到分组)(认证权限)
+- 网页授权(基本授权,用户信息授权)(认证权限)
+- 用户信息(查询用户基本信息、获取关注者列表)(认证权限)
+- 媒体文件(上传、获取)(认证权限) 
+- 调用地址组件 (支付权限) 
+- 生成订单签名数据 (支付权限) 
+- 订单成功回调 (支付权限) 
+- 发货通知 (支付权限) 
+- 支付订单查询 (支付权限) 
+
+
+### 初始化动作 
+```php
+ $options = array(
+	'token'=>'tokenaccesskey', //填写你设定的key
+	'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询
+	'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥
+	'partnerid'=>'88888888', //财付通商户身份标识,支付权限专用,没有可不填
+	'partnerkey'=>'', //财付通商户权限密钥Key,支付权限专用
+	'paysignkey'=>'' //商户签名密钥Key,支付权限专用
+	);
+ $weObj = new Wechat($options); //创建实例对象
+ //TODO:调用$weObj各实例方法
+
+```
+
 新增Auth高级权限类方法:   
  *  checkAuth($appid,$appsecret) 此处传入公众后台高级接口提供的appid和appsecret, 函数将返回access_token操作令牌
  *  createMenu($data) 创建菜单 $data菜单结构详见 http://mp.weixin.qq.com/wiki/index.php?title=%E8%87%AA%E5%AE%9A%E4%B9%89%E8%8F%9C%E5%8D%95%E5%88%9B%E5%BB%BA%E6%8E%A5%E5%8F%A3 
@@ -28,9 +61,18 @@ http://mp.weixin.qq.com/wiki/index.php?title=%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%9
  *  sendCustomMessage($data) 发送客服消息  
  *  getOauthRedirect($callback,$state,$scope) 获取网页授权oAuth跳转地址  
  *  getOauthAccessToken() 通过回调的code获取网页授权access_token  
- *  getOauthRefreshToken($refresh_token) 通过refresh_token对access_token续期
- *  getOauthUserinfo($access_token,$openid) 通过网页授权的access_token获取用户资料
+ *  getOauthRefreshToken($refresh_token) 通过refresh_token对access_token续期  
+ *  getOauthUserinfo($access_token,$openid) 通过网页授权的access_token获取用户资料  
+ *  getSignature($arrdata,'sha1') 生成签名字串  
+ *  generateNonceStr($length) 获取随机字串  
+ *  createPackage($out_trade_no,$body,$total_fee,$notify_url,$spbill_create_ip,$fee_type=1,$bank_type="WX",$input_charset="UTF-8",$time_start="",$time_expire="",$transport_fee="",$product_fee="",$goods_tag="",$attach="") 生成订单package字符串  
+ *  getPaySign($package, $timeStamp, $nonceStr) 支付签名(paySign)生成方法  
+ *  checkOrderSignature($orderxml='') 回调通知签名验证  
+ *  sendPayDeliverNotify($openid,$transid,$out_trade_no,$status=1,$msg='ok') 发货通知  
+ *  getPayOrder($out_trade_no) 查询订单信息  
+ *  getAddrSign($url, $timeStamp, $nonceStr, $user_token='') 获取收货地址JS的签名  
  
+  
 2. wechatext.class.php  
 非官方扩展API,需要配置公众平台账户和密码,能实现对已关注用户的点对点微信,此方式不保证长期有效。  
 类方法里提及的用户id在接口返回结构里表述为FakeId, 属同一概念, 在下面wechatauth类里则表示为Uin, 用户id对应的微信号必须通过getInfo()方法通过返回数组的Username值获取, 但非关注关系用户资料不能获取.  

+ 216 - 0
Thinkphp/Wechat.class.php

@@ -9,6 +9,9 @@
  *			'token'=>'tokenaccesskey', //填写你设定的key
  *			'appid'=>'wxdk1234567890', //填写高级调用功能的app id
  *			'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥
+ * 			'partnerid'=>'88888888', //财付通商户身份标识
+ *			'partnerkey'=>'', //财付通商户权限密钥Key
+ *			'paysignkey'=>'' //商户签名密钥Key
  *		);
  *	 $weObj = new Wechat($options);
  *   $weObj->valid();
@@ -74,12 +77,17 @@ class Wechat
 	const OAUTH_TOKEN_URL = '/access_token?';
 	const OAUTH_REFRESH_URL = '/refresh_token?';
 	const OAUTH_USERINFO_URL = 'https://api.weixin.qq.com/sns/userinfo?';
+	const PAY_DELIVERNOTIFY = 'https://api.weixin.qq.com/pay/delivernotify?';
+	const PAY_ORDERQUERY = 'https://api.weixin.qq.com/pay/orderquery?';
 	
 	private $token;
 	private $appid;
 	private $appsecret;
 	private $access_token;
 	private $user_token;
+	private $partnerid;
+	private $partnerkey;
+	private $paysignkey;
 	private $_msg;
 	private $_funcflag = false;
 	private $_receive;
@@ -93,6 +101,9 @@ class Wechat
 		$this->token = isset($options['token'])?$options['token']:'';
 		$this->appid = isset($options['appid'])?$options['appid']:'';
 		$this->appsecret = isset($options['appsecret'])?$options['appsecret']:'';
+		$this->partnerid = isset($options['partnerid'])?$options['partnerid']:'';
+		$this->partnerkey = isset($options['partnerkey'])?$options['partnerkey']:'';
+		$this->paysignkey = isset($options['paysignkey'])?$options['paysignkey']:'';
 		$this->debug = isset($options['debug'])?$options['debug']:false;
 		$this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false;
 	}
@@ -1042,4 +1053,209 @@ class Wechat
 		}
 		return false;
 	}
+	
+	/**
+	 * 获取签名
+	 * @param array $arrdata 签名数组
+	 * @param string $method 签名方法
+	 * @return boolean|string 签名值
+	 */
+	public function getSignature($arrdata,$method="sha1") {
+		if (!function_exists($method)) return false;
+		ksort($arrdata);
+		$paramstring = "";
+		foreach($arrdata as $key => $value)
+		{
+			if(strlen($paramstring) == 0)
+				$paramstring .= $key . "=" . $value;
+			else
+				$paramstring .= "&" . $key . "=" . $value;
+		}
+		$paySign = $method($paramstring);
+		return $paySign;
+	}
+	
+	/**
+	 * 生成随机字串
+	 * @param number $length 长度,默认为16,最长为32字节
+	 * @return string
+	 */
+	public function generateNonceStr($length=16){
+		// 密码字符集,可任意添加你需要的字符
+		$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+		$str = "";
+		for($i = 0; $i < $length; $i++)
+		{
+			$str .= $chars[mt_rand(0, strlen($chars) - 1)];
+		}
+		return $str;
+	}
+	
+	/**
+	 * 生成订单package字符串
+	 * @param string $out_trade_no 必填,商户系统内部的订单号,32个字符内,确保在商户系统唯一
+	 * @param string $body 必填,商品描述,128 字节以下
+	 * @param int $total_fee 必填,订单总金额,单位为分
+	 * @param string $notify_url 必填,支付完成通知回调接口,255 字节以内
+	 * @param string $spbill_create_ip 必填,用户终端IP,IPV4字串,15字节内
+	 * @param int $fee_type 必填,现金支付币种,默认1:人民币
+	 * @param string $bank_type 必填,银行通道类型,默认WX
+	 * @param string $input_charset 必填,传入参数字符编码,默认UTF-8,取值有UTF-8和GBK
+	 * @param string $time_start 交易起始时间,订单生成时间,格式yyyyMMddHHmmss
+	 * @param string $time_expire 交易结束时间,也是订单失效时间
+	 * @param int $transport_fee 物流费用,单位为分
+	 * @param int $product_fee 商品费用,单位为分,必须保证 transport_fee + product_fee=total_fee 
+	 * @param string $goods_tag 商品标记,优惠券时可能用到
+	 * @param string $attach 附加数据,notify接口原样返回
+	 * @return string
+	 */
+	public function createPackage($out_trade_no,$body,$total_fee,$notify_url,$spbill_create_ip,$fee_type=1,$bank_type="WX",$input_charset="UTF-8",$time_start="",$time_expire="",$transport_fee="",$product_fee="",$goods_tag="",$attach=""){
+			$arrdata = array("bank_type" => $bank_type, "body" => $body, "partner" => $this->partnerid, "out_trade_no" => $out_trade_no, "total_fee" => $total_fee, "fee_type" => $fee_type, "notify_url" => $notify_url, "spbill_create_ip" => $spbill_create_ip, "input_charset" => $input_charset);
+			if ($time_start)  $arrdata['time_start'] = $time_start;
+			if ($time_expire)  $arrdata['time_expire'] = $time_expire;
+			if ($transport_fee)  $arrdata['transport_fee'] = $transport_fee;
+			if ($product_fee)  $arrdata['product_fee'] = $product_fee;
+			if ($goods_tag)  $arrdata['goods_tag'] = $goods_tag;
+			if ($attach)  $arrdata['attach'] = $attach;
+			ksort($arrdata);
+			$paramstring = "";
+			foreach($arrdata as $key => $value)
+			{
+			if(strlen($paramstring) == 0)
+				$paramstring .= $key . "=" . $value;
+				else
+				$paramstring .= "&" . $key . "=" . $value;
+			}
+			$stringSignTemp = $paramstring . "&key=" . $this->paysignkey;
+			$signValue = strtoupper(md5($stringSignTemp));
+			$package = http_build_query($arrdata) . "&sign=" . $signValue;
+			return $package;
+	}
+	
+	/**
+	 * 支付签名(paySign)生成方法
+	 * @param string $package 订单详情字串
+	 * @param string $timeStamp 当前时间戳(需与JS输出的一致)
+	 * @param string $nonceStr 随机串(需与JS输出的一致)
+	 * @return string 返回签名字串
+	 */
+	public function getPaySign($package, $timeStamp, $nonceStr){
+		$arrdata = array("appid" => $this->appid, "timestamp" => $timeStamp, "noncestr" => $nonceStr, "package" => $package, "appkey" => $this->paysignkey);
+		$paySign = $this->getSignature($arrdata);
+		return $paySign;
+	}
+	
+	/**
+	 * 回调通知签名验证
+	 * @param array $orderxml 返回的orderXml的数组表示,留空则自动从post数据获取
+	 * @return boolean
+	 */
+	public function checkOrderSignature($orderxml=''){
+		if (!$orderxml) {
+			$postStr = file_get_contents("php://input");
+			if (!empty($postStr)) {
+				$orderxml = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
+			} else return false;
+		}
+		$arrdata = array('appid'=>$orderxml['AppId'],'appkey'=>$this->paysignkey,'timestamp'=>$orderxml['TimeStamp'],'noncestr'=>$orderxml['NonceStr'],'openid'=>$orderxml['OpenId'],'issubscribe'=>$orderxml['IsSubscribe']);
+		$paySign = $this->getSignature($arrdata);
+		if ($paySign!=$orderxml['AppSignature']) return false;
+		return true;
+	}
+	
+	/**
+	 * 发货通知
+	 * @param string $openid 用户open_id
+	 * @param string $transid 交易单号
+	 * @param string $out_trade_no 第三方订单号
+	 * @param int $status 0:发货失败;1:已发货
+	 * @param string $msg 失败原因
+	 * @return boolean|array
+	 */
+	public function sendPayDeliverNotify($openid,$transid,$out_trade_no,$status=1,$msg='ok'){
+		if (!$this->access_token && !$this->checkAuth()) return false;
+		$postdata = array(
+				"appid"=>$this->appid,
+				"appkey"=>$this->paysignkey,
+				"openid"=>$openid,
+				"transid"=>strval($transid),
+				"out_trade_no"=>strval($out_trade_no),
+				"deliver_timestamp"=>strval(time()),
+				"deliver_status"=>strval($status),
+				"deliver_msg"=>$msg,
+		);
+		$postdata['app_signature'] = $this->getSignature($postdata);
+		$postdata['sign_method'] = 'sha1';
+		unset($postdata['appkey']);
+		$result = $this->http_post(self::PAY_DELIVERNOTIFY.'access_token='.$this->access_token,self::json_encode($postdata));
+		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;
+	}
+	
+	/*
+	 * 查询订单信息
+	 * @param string $out_trade_no 订单号
+	 * @return boolean|array
+	 */
+	public function getPayOrder($out_trade_no) {
+		if (!$this->access_token && !$this->checkAuth()) return false;
+		$sign = strtoupper(md5("out_trade_no=$out_trade_no&partner={$this->partnerid}&key={$this->partnerkey}"));
+		$postdata = array(
+				"appid"=>$this->appid,
+				"appkey"=>$this->paysignkey,
+				"package"=>"out_trade_no=$out_trade_no&partner={$this->partnerid}&sign=$sign",
+				"timestamp"=>strval(time()),
+		);
+		$postdata['app_signature'] = $this->getSignature($postdata);
+		$postdata['sign_method'] = 'sha1';
+		unset($postdata['appkey']);
+		$result = $this->http_post(self::PAY_ORDERQUERY.'access_token='.$this->access_token,self::json_encode($postdata));
+		if ($result)
+		{
+			$json = json_decode($result,true);
+			if (!$json || !empty($json['errcode'])) {
+				$this->errCode = $json['errcode'];
+				$this->errMsg = $json['errmsg'].json_encode($postdata);
+				return false;
+			}
+			return $json["order_info"];
+		}
+		return false;
+	}
+	
+	/**
+	 * 获取收货地址JS的签名
+	 * @tutorial 参考weixin.js脚本的WeixinJS.editAddress方法调用
+	 * @param string $appId
+	 * @param string $url
+	 * @param int $timeStamp
+	 * @param string $nonceStr
+	 * @param string $user_token
+	 * @return Ambigous <boolean, string>
+	 */
+	public function getAddrSign($url, $timeStamp, $nonceStr, $user_token=''){
+		if (!$user_token) $user_token = $this->user_token;
+		if (!$user_token) {
+			$this->errMsg = 'no user access token found!';
+			return false;
+		}
+		$url = htmlspecialchars_decode($url);
+		$arrdata = array(
+				'appid'=>$this->appid,
+				'url'=>$url,
+				'timestamp'=>strval($timeStamp),
+				'noncestr'=>$nonceStr,
+				'accesstoken'=>$user_token
+		);
+		return $this->getSignature($arrdata);
+	}
 }

+ 98 - 0
test/auth.php

@@ -0,0 +1,98 @@
+<?php
+/**
+ * 微信oAuth认证示例
+ */
+include("../wechat.class.php");
+class wxauth {
+	private $options;
+	public $open_id;
+	public $wxuser;
+	
+	public function __construct($options){
+		$this->options = $options;
+		$this->wxoauth();
+		session_start();
+	}
+	
+	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'];
+			}
+			return $this->open_id;
+		}
+		else
+		{
+			$options = array(
+					'token'=>$this->options["token"], //填写你设定的key
+					'appid'=>$this->options["appid"], //填写高级调用功能的app id
+					'appsecret'=>$this->options["appsecret"] //填写高级调用功能的密钥
+			);
+			$we_obj = new Wechat($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);
+			header('Location: ' . $oauth_url);
+		}
+	}
+}
+$options = array(
+		'token'=>'tokenaccesskey', //填写你设定的key
+		'appid'=>'wxdk1234567890', //填写高级调用功能的app id, 请在微信开发模式后台查询
+		'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥
+);
+$auth = new wxauth($options);
+var_dump($auth->wxuser);

+ 2 - 2
test/test3.php

@@ -5,12 +5,12 @@
 	include("../wechatauth.class.php");
 	session_start();
 	function logdebug($text){
-		file_put_contents('../data/logwechat.txt',$text."\n",FILE_APPEND);		
+		file_put_contents('../log/logwechat.txt',$text."\n",FILE_APPEND);		
 	};
 	$sid  = session_id();
 	$options = array(
 		'account'=>$sid,
-		'datapath'=>'../data/cookiecode_',
+		'datapath'=>'../log/cookiecode_',
 			'debug'=>true,
 			'logcallback'=>'logdebug'	
 	); 

+ 42 - 0
test/weshare.html

@@ -0,0 +1,42 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<script src="../wechat.js" type="text/javascript"></script>
+<script>
+var dataForWeixin={
+	   appId:"",
+	   MsgImg:"https://open.weixin.qq.com/zh_CN/htmledition/res/assets/res-design-download/icon_res_download_wxlogo.png",
+	   TLImg:"https://open.weixin.qq.com/zh_CN/htmledition/res/assets/res-design-download/icon_res_download_wxlogo.png",
+	   url:"https://raw.githubusercontent.com/dodgepudding/wechat-php-sdk/master/test/weshare.html",
+	   title:"微信分享测试文件",
+	   desc:"这里演示了微信分享前调和回调方法",
+	   fakeid:"",
+	   prepare:function(e){
+		   var log = '';
+			for (var i in e) {
+				log+= i+':'+e[i]+';';
+			}
+			alert(log);
+	   },
+	   callback:function(e){
+		   var log = '';
+			for (var i in e) {
+				log+= i+':'+e[i]+';';
+			}
+			alert(log);
+	   }
+	};
+WeixinJS.hideToolbar();
+WeixinJS.getNetworkType(
+	function(e){
+		alert(e);
+	});
+</script>
+<title>weshare</title>
+</head>
+
+<body>
+<h3>这里演示了微信分享前调和回调方法,点击微信右上角分享相关的功能,即可看到各类返回的alert信息</h3>
+</body>
+</html>

+ 214 - 0
wechat.class.php

@@ -9,6 +9,9 @@
  *			'token'=>'tokenaccesskey', //填写你设定的key
  *			'appid'=>'wxdk1234567890', //填写高级调用功能的app id
  *			'appsecret'=>'xxxxxxxxxxxxxxxxxxx', //填写高级调用功能的密钥
+ *			'partnerid'=>'88888888', //财付通商户身份标识
+ *			'partnerkey'=>'', //财付通商户权限密钥Key
+ *			'paysignkey'=>'' //商户签名密钥Key
  *		);
  *	 $weObj = new Wechat($options);
  *   $weObj->valid();
@@ -73,12 +76,17 @@ class Wechat
 	const OAUTH_TOKEN_URL = '/access_token?';
 	const OAUTH_REFRESH_URL = '/refresh_token?';
 	const OAUTH_USERINFO_URL = 'https://api.weixin.qq.com/sns/userinfo?';
+	const PAY_DELIVERNOTIFY = 'https://api.weixin.qq.com/pay/delivernotify?';
+	const PAY_ORDERQUERY = 'https://api.weixin.qq.com/pay/orderquery?';
 	
 	private $token;
 	private $appid;
 	private $appsecret;
 	private $access_token;
 	private $user_token;
+	private $partnerid;
+	private $partnerkey;
+	private $paysignkey;
 	private $_msg;
 	private $_funcflag = false;
 	private $_receive;
@@ -92,6 +100,9 @@ class Wechat
 		$this->token = isset($options['token'])?$options['token']:'';
 		$this->appid = isset($options['appid'])?$options['appid']:'';
 		$this->appsecret = isset($options['appsecret'])?$options['appsecret']:'';
+		$this->partnerid = isset($options['partnerid'])?$options['partnerid']:'';
+		$this->partnerkey = isset($options['partnerkey'])?$options['partnerkey']:'';
+		$this->paysignkey = isset($options['paysignkey'])?$options['paysignkey']:'';
 		$this->debug = isset($options['debug'])?$options['debug']:false;
 		$this->_logcallback = isset($options['logcallback'])?$options['logcallback']:false;
 	}
@@ -1032,4 +1043,207 @@ class Wechat
 	}
 	
 	
+	/**
+	 * 获取签名
+	 * @param array $arrdata 签名数组
+	 * @param string $method 签名方法
+	 * @return boolean|string 签名值
+	 */
+	public function getSignature($arrdata,$method="sha1") {
+		if (!function_exists($method)) return false;
+		ksort($arrdata);
+		$paramstring = "";
+		foreach($arrdata as $key => $value)
+		{
+			if(strlen($paramstring) == 0)
+				$paramstring .= $key . "=" . $value;
+			else
+				$paramstring .= "&" . $key . "=" . $value;
+		}
+		$paySign = $method($paramstring);
+		return $paySign;
+	}
+	
+	/**
+	 * 生成随机字串
+	 * @param number $length 长度,默认为16,最长为32字节
+	 * @return string
+	 */
+	public function generateNonceStr($length=16){
+		// 密码字符集,可任意添加你需要的字符
+		$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+		$str = "";
+		for($i = 0; $i < $length; $i++)
+		{
+			$str .= $chars[mt_rand(0, strlen($chars) - 1)];
+		}
+		return $str;
+	}
+	
+	/**
+	 * 生成订单package字符串
+	 * @param string $out_trade_no 必填,商户系统内部的订单号,32个字符内,确保在商户系统唯一
+	 * @param string $body 必填,商品描述,128 字节以下
+	 * @param int $total_fee 必填,订单总金额,单位为分
+	 * @param string $notify_url 必填,支付完成通知回调接口,255 字节以内
+	 * @param string $spbill_create_ip 必填,用户终端IP,IPV4字串,15字节内
+	 * @param int $fee_type 必填,现金支付币种,默认1:人民币
+	 * @param string $bank_type 必填,银行通道类型,默认WX
+	 * @param string $input_charset 必填,传入参数字符编码,默认UTF-8,取值有UTF-8和GBK
+	 * @param string $time_start 交易起始时间,订单生成时间,格式yyyyMMddHHmmss
+	 * @param string $time_expire 交易结束时间,也是订单失效时间
+	 * @param int $transport_fee 物流费用,单位为分
+	 * @param int $product_fee 商品费用,单位为分,必须保证 transport_fee + product_fee=total_fee 
+	 * @param string $goods_tag 商品标记,优惠券时可能用到
+	 * @param string $attach 附加数据,notify接口原样返回
+	 * @return string
+	 */
+	public function createPackage($out_trade_no,$body,$total_fee,$notify_url,$spbill_create_ip,$fee_type=1,$bank_type="WX",$input_charset="UTF-8",$time_start="",$time_expire="",$transport_fee="",$product_fee="",$goods_tag="",$attach=""){
+			$arrdata = array("bank_type" => $bank_type, "body" => $body, "partner" => $this->partnerid, "out_trade_no" => $out_trade_no, "total_fee" => $total_fee, "fee_type" => $fee_type, "notify_url" => $notify_url, "spbill_create_ip" => $spbill_create_ip, "input_charset" => $input_charset);
+			if ($time_start)  $arrdata['time_start'] = $time_start;
+			if ($time_expire)  $arrdata['time_expire'] = $time_expire;
+			if ($transport_fee)  $arrdata['transport_fee'] = $transport_fee;
+			if ($product_fee)  $arrdata['product_fee'] = $product_fee;
+			if ($goods_tag)  $arrdata['goods_tag'] = $goods_tag;
+			if ($attach)  $arrdata['attach'] = $attach;
+			ksort($arrdata);
+			$paramstring = "";
+			foreach($arrdata as $key => $value)
+			{
+			if(strlen($paramstring) == 0)
+				$paramstring .= $key . "=" . $value;
+				else
+				$paramstring .= "&" . $key . "=" . $value;
+			}
+			$stringSignTemp = $paramstring . "&key=" . $this->paysignkey;
+			$signValue = strtoupper(md5($stringSignTemp));
+			$package = http_build_query($arrdata) . "&sign=" . $signValue;
+			return $package;
+	}
+	
+	/**
+	 * 支付签名(paySign)生成方法
+	 * @param string $package 订单详情字串
+	 * @param string $timeStamp 当前时间戳(需与JS输出的一致)
+	 * @param string $nonceStr 随机串(需与JS输出的一致)
+	 * @return string 返回签名字串
+	 */
+	public function getPaySign($package, $timeStamp, $nonceStr){
+		$arrdata = array("appid" => $this->appid, "timestamp" => $timeStamp, "noncestr" => $nonceStr, "package" => $package, "appkey" => $this->paysignkey);
+		$paySign = $this->getSignature($arrdata);
+		return $paySign;
+	}
+	
+	/**
+	 * 回调通知签名验证
+	 * @param array $orderxml 返回的orderXml的数组表示,留空则自动从post数据获取
+	 * @return boolean
+	 */
+	public function checkOrderSignature($orderxml=''){
+		if (!$orderxml) {
+			$postStr = file_get_contents("php://input");
+			if (!empty($postStr)) {
+				$orderxml = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
+			} else return false;
+		}
+		$arrdata = array('appid'=>$orderxml['AppId'],'appkey'=>$this->paysignkey,'timestamp'=>$orderxml['TimeStamp'],'noncestr'=>$orderxml['NonceStr'],'openid'=>$orderxml['OpenId'],'issubscribe'=>$orderxml['IsSubscribe']);
+		$paySign = $this->getSignature($arrdata);
+		if ($paySign!=$orderxml['AppSignature']) return false;
+		return true;
+	}
+	
+	/**
+	 * 发货通知
+	 * @param string $openid 用户open_id
+	 * @param string $transid 交易单号
+	 * @param string $out_trade_no 第三方订单号
+	 * @param int $status 0:发货失败;1:已发货
+	 * @param string $msg 失败原因
+	 * @return boolean|array
+	 */
+	public function sendPayDeliverNotify($openid,$transid,$out_trade_no,$status=1,$msg='ok'){
+		if (!$this->access_token && !$this->checkAuth()) return false;
+		$postdata = array(
+				"appid"=>$this->appid,
+				"appkey"=>$this->paysignkey,
+				"openid"=>$openid,
+				"transid"=>strval($transid),
+				"out_trade_no"=>strval($out_trade_no),
+				"deliver_timestamp"=>strval(time()),
+				"deliver_status"=>strval($status),
+				"deliver_msg"=>$msg,
+		);
+		$postdata['app_signature'] = $this->getSignature($postdata);
+		$postdata['sign_method'] = 'sha1';
+		unset($postdata['appkey']);
+		$result = $this->http_post(self::PAY_DELIVERNOTIFY.'access_token='.$this->access_token,self::json_encode($postdata));
+		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;
+	}
+	
+	/*
+	 * 查询订单信息
+	 * @param string $out_trade_no 订单号
+	 * @return boolean|array
+	 */
+	public function getPayOrder($out_trade_no) {
+		if (!$this->access_token && !$this->checkAuth()) return false;
+		$sign = strtoupper(md5("out_trade_no=$out_trade_no&partner={$this->partnerid}&key={$this->partnerkey}"));
+		$postdata = array(
+				"appid"=>$this->appid,
+				"appkey"=>$this->paysignkey,
+				"package"=>"out_trade_no=$out_trade_no&partner={$this->partnerid}&sign=$sign",
+				"timestamp"=>strval(time()),
+		);
+		$postdata['app_signature'] = $this->getSignature($postdata);
+		$postdata['sign_method'] = 'sha1';
+		unset($postdata['appkey']);
+		$result = $this->http_post(self::PAY_ORDERQUERY.'access_token='.$this->access_token,self::json_encode($postdata));
+		if ($result)
+		{
+			$json = json_decode($result,true);
+			if (!$json || !empty($json['errcode'])) {
+				$this->errCode = $json['errcode'];
+				$this->errMsg = $json['errmsg'].json_encode($postdata);
+				return false;
+			}
+			return $json["order_info"];
+		}
+		return false;
+	}	
+	
+	/**
+	 * 获取收货地址JS的签名
+	 * @param string $appId
+	 * @param string $url
+	 * @param int $timeStamp
+	 * @param string $nonceStr
+	 * @param string $user_token
+	 * @return Ambigous <boolean, string>
+	 */
+	public function getAddrSign($url, $timeStamp, $nonceStr, $user_token=''){
+		if (!$user_token) $user_token = $this->user_token;
+		if (!$user_token) {
+			$this->errMsg = 'no user access token found!';
+			return false;
+		}
+		$url = htmlspecialchars_decode($url);
+		$arrdata = array(
+				'appid'=>$this->appid,
+				'url'=>$url,
+				'timestamp'=>strval($timeStamp),
+				'noncestr'=>$nonceStr,
+				'accesstoken'=>$user_token
+		);
+		return $this->getSignature($arrdata);
+	}
 }

+ 114 - 7
wechat.js

@@ -17,7 +17,65 @@
 	   title:"标题",
 	   desc:"描述",
 	   fakeid:"",
-	   callback:function(){}
+	   prepare:function(argv){
+	   if (typeof argv.shareTo!='undefined') 
+	   	switch(argv.shareTo) {
+	   		case 'friend':
+	   		//发送给朋友
+	   		alert(argv.scene); //friend
+	   		break;
+	   		case 'timeline':
+	   		//发送给朋友
+	   		break;
+	   		case 'weibo':
+	   		//发送到微博
+	   		alert(argv.url);
+	   		break;
+	   		case 'favorite':
+	   		//收藏
+	   		alert(argv.scene);//favorite
+	   		break;
+	   		case 'connector':
+	   		//分享到第三方应用
+	   		alert(argv.scene);//connector
+	   		break;
+	   		default:
+	   	}
+	   },
+	   callback:function(res){
+	   	//发送给好友或应用
+	   	if (res.err_msg=='send_app_msg:confirm') {
+	   		//todo:func1();
+	   		alert(res.err_desc);
+	   	}
+	   	if (res.err_msg=='send_app_msg:cancel') {
+	   		//todo:func2();
+	   		alert(res.err_desc);
+	   	}
+	   	//分享到朋友圈
+	   	if (res.err_msg=='share_timeline:confirm') {
+	   		//todo:func1();
+	   		alert(res.err_desc);
+	   	}
+	   	if (res.err_msg=='share_timeline:cancel') {
+	   		//todo:func1();
+	   		alert(res.err_desc);
+	   	}
+	   	//分享到微博
+	   	if (res.err_msg=='share_weibo:confirm') {
+	   		//todo:func1();
+	   		alert(res.err_desc);
+	   	}
+	   	if (res.err_msg=='share_weibo:cancel') {
+	   		//todo:func1();
+	   		alert(res.err_desc);
+	   	}
+	   	//收藏或分享到应用
+	   	if (res.err_msg=='send_app_msg:ok') {
+	   		//todo:func1();
+	   		alert(res.err_desc);
+	   	}   	
+	   }
 	};
  */
 
@@ -32,10 +90,58 @@ WeixinJS.hideToolbar = function() {
 		if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.call('hideToolbar');
 	});
 };
+WeixinJS.getNetworkType = function(callback) {
+	document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() {
+		if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke('getNetworkType',{},
+		function(res){
+			//result: network_type:wifi,network_type:edge,network_type:fail,network_type:wwan
+			callback(res.err_msg);
+	    });
+	});
+};
+
+WeixinJS.closeWindow = function() {
+	if (typeof WeixinJSBridge!='undefined') WeixinJSBridge.invoke("closeWindow", {});
+};
+
+WeixinJS.payCallback = function(appId,package,timeStamp,nonceStr,signType,paySign,callback){
+	if (typeof WeixinJSBridge!='undefined')
+	WeixinJSBridge.invoke('getBrandWCPayRequest',{
+        "appId" : appId.toString(),
+        "timeStamp" : timeStamp.toString(),
+        "nonceStr" : nonceStr.toString(),
+        "package" : package.toString(),
+        "signType" : signType.toString(),
+        "paySign" : paySign.toString()
+        
+    },function(res){
+    	// res.err_msg == "get_brand_wcpay_request:ok" return true;
+    	// res.err_msg == "get_brand_wcpay_request:cancel" return false;
+    	callback(res);
+    });
+};
+
+WeixinJS.editAddress = function(appId,addrSign,timeStamp,nonceStr,callback){
+	var postdata = {
+			"appId" : appId.toString(),
+            "scope" : "jsapi_address",
+            "signType" : "sha1",
+            "addrSign" : addrSign.toString(),
+            "timeStamp" : timeStamp.toString(),
+            "nonceStr" : nonceStr.toString()
+	};
+	if (typeof WeixinJSBridge!='undefined')
+	WeixinJSBridge.invoke('editAddress',postdata, function(res){
+	//return res.proviceFirstStageName,res.addressCitySecondStageName,res.addressCountiesThirdStageName,res.addressDetailInfo,res.userName,res.addressPostalCode,res.telNumber
+	//error return res.err_msg
+	callback(res);
+	});
+};
 
 (function(){
    var onBridgeReady=function(){
    WeixinJSBridge.on('menu:share:appmessage', function(argv){
+	  (dataForWeixin.prepare)(argv);
       WeixinJSBridge.invoke('sendAppMessage',{
          "appid":dataForWeixin.appId,
          "img_url":dataForWeixin.MsgImg,
@@ -44,10 +150,10 @@ WeixinJS.hideToolbar = function() {
          "link":dataForWeixin.url,
          "desc":dataForWeixin.desc,
          "title":dataForWeixin.title
-      }, function(res){(dataForWeixin.callback)();});
+      }, function(res){(dataForWeixin.callback)(res);});
    });
    WeixinJSBridge.on('menu:share:timeline', function(argv){
-      (dataForWeixin.callback)();
+	  (dataForWeixin.prepare)(argv);
       WeixinJSBridge.invoke('shareTimeline',{
          "img_url":dataForWeixin.TLImg,
          "img_width":"120",
@@ -55,16 +161,17 @@ WeixinJS.hideToolbar = function() {
          "link":dataForWeixin.url,
          "desc":dataForWeixin.desc,
          "title":dataForWeixin.title
-      }, function(res){});
+      }, function(res){(dataForWeixin.callback)(res);});
    });
    WeixinJSBridge.on('menu:share:weibo', function(argv){
+	  (dataForWeixin.prepare)(argv);
       WeixinJSBridge.invoke('shareWeibo',{
          "content":dataForWeixin.title,
          "url":dataForWeixin.url
-      }, function(res){(dataForWeixin.callback)();});
+      }, function(res){(dataForWeixin.callback)(res);});
    });
    WeixinJSBridge.on('menu:share:facebook', function(argv){
-      (dataForWeixin.callback)();
+	  (dataForWeixin.prepare)(argv);
       WeixinJSBridge.invoke('shareFB',{
          "img_url":dataForWeixin.TLImg,
          "img_width":"120",
@@ -72,7 +179,7 @@ WeixinJS.hideToolbar = function() {
          "link":dataForWeixin.url,
          "desc":dataForWeixin.desc,
          "title":dataForWeixin.title
-      }, function(res){(dataForWeixin.callback)();});
+      }, function(res){(dataForWeixin.callback)(res);});
    });
 };
 if(document.addEventListener){