简单介绍了微信移动支付的申请、接入、使用、确认支付结果等相关流程
0 系列文章
系列一 微信App支付全解析
系列二 支付宝App支付全解析
系列三 微信公众号支付全解析
系列四 微信扫码支付全解析
系列五 支付宝即时到账支付全解析
系列六 微信退款全解析
系列七 支付宝退款全解析
系列八 支付宝开放平台支付更新升级全解析
1 申请
申请步骤直接参考官方文档
主要2个大块:
- 申请开通开放平台
- 申请支付开通商户平台
全部申请通过后,获取支付必须的参数如下:
1.1 AppID和AppSecret
开放平台创建的应用唯一标识。
登录微信开放平台,进入应用详情可查看AppID和AppSecret。
1.2 mch_id
微信支付申请完成之后,微信商户平台会给你的邮箱发通知邮件,里面包含开通支付的商户信息
Paste_Image.png1.3 API秘钥
即商户支付秘钥,主要负责处理通信相关参数加密。登陆微信商户平台(账号密码在微信商户平台发来的邮件里)
点击左侧的「账户设置 - API 安全」(第一次登陆会让你安装操作证书,请先安装操作证书)。点击设置密钥,设置自己的密钥。
1.4 商户证书
用于退款等一些需要证书验证的接口使用。在微信商户平台点击「账户中心 - API 安全」,点击「下载证书」
Paste_Image.png证书下载后,打开压缩包会看到「apiclient_cert.pem」和「apiclient_key.pem」和rootca.pem证书。
2 接入流程
参考接入文档
主要几个步骤:
- 统一下单(放在服务端,需要加密参数)
- 生成支付参数(放在服务端,需要生成签名)
- 调用客户端SDK发起支付
- 服务端异步接收支付结果
2.1 统一下单
$appid = ""; //你的appid
$mch_id = ""; //商户id
$wx_api_key = ""; //商户api秘钥
$out_trade_no = ""; //自己业务系统生成的交易no,可以唯一标识
$client_ip = ""; //客户端ip
$notify_url = ""; //接收支付结果通知url
$UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; //统一下单地址
$data = array();
$data['appid'] = $appid;
$data['mch_id'] =$mch_id;
$data['nonce_str'] = randomStr(20); //随机20位字符串
$data['body'] = "微信移动支付测试";
$data['detail'] = "微信移动支付测试";
$data['out_trade_no'] = $out_trade_no;
$data['total_fee'] = 1; //注意 单位是分
$data['spbill_create_ip'] = $client_ip;
$data['notify_url'] = $notify_url;
$data['trade_type'] = "APP"; //交易类型
$data['sign'] =sign($data, $wx_api_key); //签名
//转为xml格式
$xml_str = arrayToXmlStr($data);
//发送请求 使用封装好的curl_post
$result = curl_post($UNIFIED_ORDER_URL, $xml_str);
//解析得到的值
$get_data = simplexml_load_string($raw_data, 'SimpleXMLElement', LIBXML_NOCDATA);
$get_para = array();
$get_sign = "";
foreach ($get_data->children() as $child)
{
if($child->getName() == 'sign') {
$get_sign = strval($child);
} else {
$get_para[strval($child->getName())] = strval($child);
}
}
if($get_para['return_code'] !== "SUCCESS") {
//return code fail
}
//验证签名
if(!verifySign($get_sign, $get_para, $wx_api_key)) {
//验证签名非法
}
//可以自行处理解析获得的参数
//todo...
一些函数:
/**
* array转成xml str
* @param $arr
*/
public static function arrayToXmlStr($arr) {
$xml_data = new \SimpleXMLElement("<xml></xml>");
Func::arrayToXml($arr, $xml_data);
return $xml_data->asXML();
}
/**
* 生成指定长度的随机字符串(包含大写英文字母, 小写英文字母, 数字)
* @param $length int 需要生成的字符串的长度
* @return string 包含 大小写英文字母 和 数字 的随机字符串
*/
public static function randomStr($length){
//生成一个包含 大写英文字母, 小写英文字母, 数字 的数组
$arr = array_merge(range(0, 9), range('a', 'z'), range('A', 'Z'));
$str = '';
$arr_len = count($arr);
for ($i = 0; $i < $length; $i++) {
$rand = mt_rand(0, $arr_len-1);
$str.=$arr[$rand];
}
return $str;
}
/**
* 微信签名
* @param $para mixed 带签名参数数组
* @param $wx_key string wxkey
*/
public static function sign($para, $wx_key) {
$unsign_str = Func::createLinkString(Func::argSort($para)) . "&key=" . $wx_key;
$sign = strtoupper(md5($unsign_str));
return $sign;
}
/**
* 微信签名验证
* @param $sign
* @param $para
* @param $wx_key
* @return false-验证失败 true-验证成功
*/
public static function verifySign($sign, $para, $wx_key) {
$unsign_str = Func::createLinkString(Func::argSort($para)) . "&key=" . $wx_key;
$sign_str = strtoupper(md5($unsign_str));
if($sign === $sign_str) {
return true;
}
return false;
}
2.2 生成支付参数
客户端需要的支付参数是带签名的,所以最好支付参数也在服务端生成后,jsondecode后传入客户端即可直接调用
//生成支付参数
$data = array();
$data['appid'] = $appid;
$data['mch_id'] =$mch_id;
$data['prepayid'] = $prepayid; //刚才统一下单生成的prepayid
$data['package'] = "Sign=WXPay";
$data['noncestr'] = randomStr(20);
$data['timestamp'] = time();
$data['sign'] =sign($data, $wx_api_key);
$pay_param = json_encode($data);
3. 调用支付
3.1 Android
注:微信支付在开放平台中填入应用对应的包名和签名,并且测试时要签名打包,不然支付失败
可以直接参考调用我二次封装过的Android SDK。
Github地址:https://github.com/tsy12321/PayAndroid
3.2 iOS
二次封装过的iOS SDK。
Github地址:https://github.com/tsy12321/PayiOS
4 异步结果通知
注:尤其要注意通知结果验证成功后要能正确处理重复通知,放置多次发货造成资金损失
$raw_data = $GLOBALS["HTTP_RAW_POST_DATA"];
$get_data = simplexml_load_string($raw_data, 'SimpleXMLElement', LIBXML_NOCDATA);
$get_para = array();
$get_sign = "";
foreach ($get_data->children() as $child)
{
if($child->getName() == 'sign') {
$get_sign = strval($child);
} else {
$get_para[strval($child->getName())] = strval($child);
}
}
if($get_para['return_code'] !== "SUCCESS") {
//return code fail
die("<xml><return_code><![CDATA[FAIL]]></return_code></xml>");
}
//验证签名
if(!verifySign($get_sign, $get_para, $wx_api_key)) {
//验证签名非法
//todo
die("<xml><return_code><![CDATA[FAIL]]></return_code></xml>");
}
//在这其实通知已经接受成功 可以返回成功告诉微信不用再次通知了
echo("<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>");
//业务状态码判断
if ($get_para['result_code'] !== 'SUCCESS') {
//状态码错误
//支付错误 更改订单状态 记录log等
//...
}
//支付成功 更改订单状态 记录log等
//todo
5 其他
- 客户端收到同步支付结果后建议一段时间内轮询检查服务端,获取服务端的结果,支付最终状态以服务端为准
结尾
更多文章关注我的公众号
我的公众号
网友评论
推荐下,源码圈 300 胖友的书单整理:http://t.cn/R0Uflld
尻
https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_1
通过openid获取转给谁,可选参数可以验证姓名之类的。
感觉统一下单的代码还有有问题啊
curl_post()方法没有找到。。
//发送请求 使用封装好的curl_post
$result = curl_post($UNIFIED_ORDER_URL, $xml_str);
//解析得到的值
$get_data = simplexml_load_string($raw_data, 'SimpleXMLElement', LIBXML_NOCDATA);
$raw_data应该换成$result ?
$raw_data应该换成$result ,笔误
微信客户端支付完成后会返回给你客户端一个支付结果。
同时微信的服务端会主动调用你服务端的接口发送支付结果通知。
逻辑处理应该是你服务端接收到支付结果后处理,比如修改订单状态,发货等等。不能依赖客户端的返回结果认为支付成功,是不可靠的,微信的文档也是这么建议的。
你前后端的时序可以这样。客户端支付完成收到支付结果后,在一定时间内不断轮询查询服务端订单的状态有没有修改。(比如5s内每s查询一次)这样以服务端的交易状态为准
异步就是你支付完第三方会发post数据给你的服务端。由于是被动获取无法准确掌握时间,所以是异步。
最后的支付结果要以服务端通知的结果为准
可以去看退款解析那篇文章