个人认为最佳的学习方式是抛开内容繁重的文档,先去https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1 下载个php的demo 边阅读源码变看文档 这种方式我觉得最有效 顺便吐槽下Java居然只有刷卡支付的栗子。。。
这篇博文不会很详细的介绍demo的源码 其实我们有时候业务并不需要使用微信支付的全部功能
还有例如Wxpay.Data.php这个文件 这个文件接近3000行
其实就是维护一个内部数组
protected $values = array();
同设置get set为这个数组设置参数,例如签名等等 大约有几十个参数
最后将这个数组转为xml等等给微信服务器做交互等等 如果要全部说清楚 那么你和看微信文档没什么区别 反而难上手
如果你用的是php 那么你把这个demo作为sdk我觉得是最快的方法,然后再阅读下本博文,基本就可以做出微信支付
如果你用的是java等语言 那么阅读完本博文之后还需继续啃下各个签名算法,然后再啃啃文档。所以一定程度上说,php是最好的语言,也不无道理哈哈(不喜勿喷),可能因为实现的不方便,微信才没有给出jsapi的Java实现版吧
1,设置基本的参数 在Wxpay.Config.php里可以看到
如果你没有认证的可以微信支付的公众号 不要紧 可以使用demo自带的四个参数 支付的钱是转到微信测试账号里 金额设置为0.01即可 土豪随意
有四个参数要设置 这里就不说了
//=======【基本信息设置】=====================================
//
/**
* TODO: 修改这里配置为您自己申请的商户信息
* 微信公众号信息配置
*
* APPID:绑定支付的APPID(必须配置,开户邮件中可查看)
*
* MCHID:商户号(必须配置,开户邮件中可查看)
*
* KEY:商户支付密钥,参考开户邮件设置(必须配置,登录商户平台自行设置)
* 设置地址:https://pay.weixin.qq.com/index.php/account/api_cert
*
* APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置, 登录公众平台,进入开发者中心可设置),
* 获取地址:https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&token=2005451881&lang=zh_CN
* @var string
*/
2,认证并获取获取用户openid
通过向微信公众平台认证 获取一个code 通过这个code来换取用户的openid
openid其实就是微信用户在你的公众号上的唯一标识
demo的源码如下
首先检测你当前(也就是你要执行支付的页面)有没有收到get参数
如果有的话 就通过code调用获取openid的方法
public function GetOpenid()
{
//通过code获得openid
if (!isset($_GET['code'])){
//触发微信返回code码
$baseUrl = urlencode('http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'].$_SERVER['QUERY_STRING']);
$url = $this->__CreateOauthUrlForCode($baseUrl);
Header("Location: $url");
exit();
} else {
//获取code码,以获取openid
$code = $_GET['code'];
$openid = $this->getOpenidFromMp($code);
return $openid;
}
}
如果没有get到code的话 将当前(或者你要执行支付页面的其他url)拼接成一个baseurl
调用__CreateOauthUrlForCode方法 并跳转到生成出来的url
我们来看看生成认证url的方法
private function __CreateOauthUrlForCode($redirectUrl)
{
$urlObj["appid"] = WxPayConfig::APPID;
$urlObj["redirect_uri"] = "$redirectUrl";
$urlObj["response_type"] = "code";
$urlObj["scope"] = "snsapi_base";
$urlObj["state"] = "STATE"."#wechat_redirect";
$bizString = $this->ToUrlParams($urlObj);
return "https://open.weixin.qq.com/connect/oauth2/authorize?".$bizString;
}
其实就是将一段参数拼接后 访问到微信的认证url
注意redirect_uri 就是你上一步传入的baseurl
你跳转到微信的认证url后 微信会经过认证 考核你的各种条件(公众号有没有这个权限啊,网页授权有没有开啊等等)
认证成功后 会重定向回我们的baseurl 并带上一个get参数 ?code=xxxx
然后我们这时我们再进行下一步,就有code可以用了 走有code的路线
看下GetOpenidFromMp 就是用code换openid的方法
和__CreateOauthUrlForOpenid即拼接获取openidurl的方法
其实都很简单 拼接参数 拼成你要的url 然后curl到微信需要的url 设置参数 发送请求 获取后获得code
private function __CreateOauthUrlForOpenid($code)
{
$urlObj["appid"] = WxPayConfig::APPID;
$urlObj["secret"] = WxPayConfig::APPSECRET;
$urlObj["code"] = $code;
$urlObj["grant_type"] = "authorization_code";
$bizString = $this->ToUrlParams($urlObj);
return "https://api.weixin.qq.com/sns/oauth2/access_token?".$bizString;
}
public function GetOpenidFromMp($code)
{
$url = $this->__CreateOauthUrlForOpenid($code);
//初始化curl
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,FALSE);
curl_setopt($ch, CURLOPT_HEADER, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
if(WxPayConfig::CURL_PROXY_HOST != "0.0.0.0"
&& WxPayConfig::CURL_PROXY_PORT != 0){
curl_setopt($ch,CURLOPT_PROXY, WxPayConfig::CURL_PROXY_HOST);
curl_setopt($ch,CURLOPT_PROXYPORT, WxPayConfig::CURL_PROXY_PORT);
}
//运行curl,结果以jason形式返回
$res = curl_exec($ch);
curl_close($ch);
//取出openid
$data = json_decode($res,true);
$this->data = $data;
$openid = $data['openid'];
return $openid;
}
需要注意一个问题 微信给出的demo只是一次性使用的 所以我一开始看忽略了一个问题
你们需要记住: 认证后获得的code只可使用一次 用完就失效 所以获取openid后应该保存起来 建议是放到session里
3,设置统一下单参数
$input = new WxPayUnifiedOrder();
$input->SetBody("test");
$input->SetAttach("test");
$input->SetOut_trade_no(WxPayConfig::MCHID.date("YmdHis"));
$input->SetTotal_fee("1");
$input->SetTime_start(date("YmdHis"));
$input->SetTime_expire(date("YmdHis", time() + 600));
$input->SetGoods_tag("test");
$input->SetNotify_url("http://paysdk.weixin.qq.com/example/notify.php");
$input->SetTrade_type("JSAPI");
$input->SetOpenid($openId);
这里我觉得需要说下的就是Attach和notifyurl
attach就是放你这个订单里,你要的自定义信息,例如商品id等等
notifyurl 就是支付成功后 微信服务器会向你的这个url放出请求,并带上attach等等参数 然你对用户购买成功后进行一系列逻辑操作
4,生成给前端的订单信息
就是调两个方法 一个根据你刚才设置的参数生成订单
另一个就是根据这个订单生成给前端的js参数
如果你用的是php 可以直接把这个两个方法当sdk用 里面包含了很多签名和加密的操作,看起来有点恶心,不过你看下文档就能知道 别人封装好了 你当然用就是 如果你用的是Python或者Java 那么恭喜你 你还得去看它们的源码并自己实现一遍
$order = WxPayApi::unifiedOrder($input);
$jsApiParameters = $tools->GetJsApiParameters($order);
5,前端根据生成的js参数唤起支付页面
我没做地址的获取 所以我这里跳过地址获取
首先要注意一个问题 微信内置对象WeixinJSBridge 不是一点进页面就生成好的 网速不好的时候 经常会是undefined 所以需要如下代码
function callpay()
{
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
}
}else{
jsApiCall();
}
}
jsApiCall就是唤起支付的核心
function jsApiCall()
{
WeixinJSBridge.invoke(
'getBrandWCPayRequest',
<?php echo $jsApiParameters; ?>,
function(res){
WeixinJSBridge.log(res.err_msg);
alert(res.err_code+res.err_desc+res.err_msg);
}
);
}
如果你不是用后端渲染的话 把前面的步骤做成api,让前端调一下就可以获得 $jsApiParameters 然后赋到函数里面去 而不用直接在js里echo 看着有点不优雅
剩下的事就暂时不用你管了,交给用户输密码然后剁手 最后你还要做的一件事就是
notifyurl
开发支付成功后微信异步回调的接口
notify.php中内置了一个内部类 并继承自WxPayNotify
class PayNotifyCallBack extends WxPayNotify
然后实例了这个内部类并且调用了他的handle方法
$notify = new PayNotifyCallBack();
$notify->Handle(false);
我们来看下父类WxPayNotify的handle方法
忽略掉签名等操作
/**
*
* 回调入口
* @param bool $needSign 是否需要签名输出
*/
final public function Handle($needSign = true)
{
$msg = "OK";
//当返回false的时候,表示notify中调用NotifyCallBack回调失败获取签名校验失败,此时直接回复失败
$result = WxpayApi::notify(array($this, 'NotifyCallBack'), $msg);
if($result == false){
$this->SetReturn_code("FAIL");
$this->SetReturn_msg($msg);
$this->ReplyNotify(false);
return;
} else {
//该分支在成功回调到NotifyCallBack方法,处理完成之后流程
$this->SetReturn_code("SUCCESS");
$this->SetReturn_msg("OK");
}
$this->ReplyNotify($needSign);
}
来看看WxpayApi::notify这个方法
首先把微信支付成功后发给你的xml获取并解析 里面有你设置的订单信息 attach等等
最后使用call_user_func(
他会执行NotifyCallBack方法 并把xml解析后的数据作为参数传入
/**
*
* 支付结果通用通知
* @param function $callback
* 直接回调函数使用方法: notify(you_function);
* 回调类成员函数方法:notify(array($this, you_function));
* $callback 原型为:function function_name($data){}
*/
public static function notify($callback, &$msg)
{
//获取通知的数据
$xml = $GLOBALS['HTTP_RAW_POST_DATA'];
//如果返回成功则验证签名
try {
$result = WxPayResults::Init($xml);
} catch (WxPayException $e){
$msg = $e->errorMessage();
return false;
}
return call_user_func($callback, $result);
}
再来看看NotifyCallBack
/**
*
* notify回调方法,该方法中需要赋值需要输出的参数,不可重写
* @param array $data
* @return true回调出来完成不需要继续回调,false回调处理未完成需要继续回调
*/
final public function NotifyCallBack($data)
{
$msg = "OK";
$result = $this->NotifyProcess($data, $msg);
if($result == true){
$this->SetReturn_code("SUCCESS");
$this->SetReturn_msg("OK");
} else {
$this->SetReturn_code("FAIL");
$this->SetReturn_msg($msg);
}
return $result;
}
他会执行NotifyProcess根据执行后的结果 设置返回参数为成功或者失败 返回参数等下是要返回给微信服务器的
而NotifyProcess 就是处理里自己核心购买业务的地方 需要你自己重写
/**
*
* 回调方法入口,子类可重写该方法
* 注意:
* 1、微信回调超时时间为2s,建议用户使用异步处理流程,确认成功之后立刻回复微信服务器
* 2、微信服务器在调用失败或者接到回包为非确认包的时候,会发起重试,需确保你的回调是可以重入
* @param array $data 回调解释出的参数
* @param string $msg 如果回调处理失败,可以将错误信息输出到该方法
* @return true回调出来完成不需要继续回调,false回调处理未完成需要继续回调
*/
public function NotifyProcess($data, &$msg)
{
//TODO 用户基础该类之后需要重写该方法,成功的时候返回true,失败返回false
return true;
}
返回false时微信还会继续回调,我理解一般是微信传的的参数有误什么的才回造成你处理失败,一直返回false给他,他也应该不会无限次回调
重新回到handle方法 你会看到
根据你自己处理回调参数,处理业务后 还需要返回信息给微信服务器 最长返回时间是2秒 也就是2秒内你必须告诉微信yes or no
if($result == false){
$this->SetReturn_code("FAIL");
$this->SetReturn_msg($msg);
$this->ReplyNotify(false);
return;
} else {
//该分支在成功回调到NotifyCallBack方法,处理完成之后流程
$this->SetReturn_code("SUCCESS");
$this->SetReturn_msg("OK");
}
$this->ReplyNotify($needSign);
最后是ReplyNotify方法
/**
*
* 回复通知
* @param bool $needSign 是否需要签名输出
*/
final private function ReplyNotify($needSign = true)
{
//如果需要签名
if($needSign == true &&
$this->GetReturn_code($return_code) == "SUCCESS")
{
$this->SetSign();
}
WxpayApi::replyNotify($this->ToXml());
}
而replyNotify 就是一句echo 告诉微信服务器
/**
* 直接输出xml
* @param string $xml
*/
public static function replyNotify($xml)
{
echo $xml;
}
微信支付到这里就告一段落了
我之前做项目的时候用的是php ,所以我没深究签名等等 只需掌握上述的基本流程,借助demo作为sdk即可完成微信支付开发 这是最快的方式
如果你用的是Java或者其他没提供签名方法的语言 就还需要再继续阅读签名等方法 然后再啃啃文档 以后有空我写一个springmvc的sdk出来玩哈哈