在用 JSAPI 开发微信支付的时候,遇到了很多坑,我也对朋友说过,一步一坑。最后终于算是走出来了。期间翻阅过很多网友写的教程,但是都不实用,JAVA,Python 都有看过,大多数都是粘贴复制,倍感失望。
开发环境
thinkphp5.0 php
(开始使用 JSAPI 需要一个概念,就是在整个 JSAPI 的逻辑里面,只存在一个随机字符串 和一个 时间戳。相当于 JSSAPI 类里的全局。)
开始开发
全局初始化
public function __construct($total_fee, $body, $openid)
{
$rand = rand(11, 99);
$mp_info = get_mpid_info();// 获取微信信息
$this->appid = $mp_info[‘appid’];
$this->nonce_str = nonceStr(32);
$this->spbill_create_ip = Request::instance()->ip();
$this->mch_id = $mp_info[‘mch_id’];
$this->key = $mp_info[‘paykey’];
$this->timestamp = time();
$this->sign;// 一次签名
$this->total_fee = $total_fee;
$this->out_trade_no = time() . $rand;
$this->notify_url = ‘http://uedream.cn/index.php’;
$this->body = $body;
$this->openid = $openid;
$this->sign_type = ‘MD5’;
$this->createsign(); // 生成签名方法,需要结合 createsign 方法
}
以上是初始化签名结构体
获取签名
文档:https://pay.weixin.qq.com/wik…
public function createsign()
{
$build = [
‘appid’ => $this->appid,
‘body’ => $this->body,
‘mch_id’ => $this->mch_id,
‘nonce_str’ => $this->nonce_str,
‘notify_url’ => $this->notify_url,
‘openid’ => $this->openid,
‘out_trade_no’ => $this->out_trade_no,
‘sign_type’ => $this->sign_type,
‘spbill_create_ip’ => $this->spbill_create_ip,
‘timeStamp’ => $this->timestamp,
‘total_fee’ => $this->total_fee,
‘trade_type’ => $this->trade_type,
‘key’ => $this->key,
];
$string = http_build_query($build);
$string = str_replace(‘%2F’, ‘/’, $string); // 格式化网址
$string = str_replace(‘%3A’, ‘:’, $string); // 格式化网址
$md5 = md5($string);
$this->sign = strtoupper($md5);
}
统一下单
文档:https://pay.weixin.qq.com/wik…
public function unifiedorder()
{
$data = [
‘appid’ => $this->appid,
‘body’ => $this->body,
‘mch_id’ => $this->mch_id,
‘nonce_str’ => $this->nonce_str,
‘notify_url’ => $this->notify_url,
‘openid’ => $this->openid,
‘out_trade_no’ => $this->out_trade_no,
‘sign’ => $this->sign,
‘sign_type’ => ‘MD5’,
‘spbill_create_ip’ => $this->spbill_create_ip,
‘timeStamp’ => $this->timestamp,
‘total_fee’ => $this->total_fee * 1,
‘trade_type’ => $this->trade_type,
];
$xml = arrayToXml($data);
$result = http_post(self::UNIFIEDORDER, $xml);
$return = xmlToArray($result);
$this->package = ‘prepay_id=’ . $return[‘prepay_id’];
$this->renCreatesign();// 这是二次签名。文档里面我是没有看到,反正我是卡到这里了。
$returns = [
‘appid’ => $this->appid,
‘noncestr’ => $this->nonce_str,
‘signtype’ => $this->sign_type,
‘package’ => $this->package,
‘sign’ => $this->resign,
‘timestamp’ => $this->timestamp,
];
return $returns;
}
统一下单请忽略的所有的回调参数,只要 prepay_id,其它的参数暂时看做障眼法,获取到了统一下单,还需要进行二次签名,上面代码里面有一个 $this->renCreatesign(),就是调用的二次签名方法
二次签名
文档:https://pay.weixin.qq.com/wik…
所谓的二次签名就是,appId,nonceStr,package,signType,timeStamp,key 的加密。一样的签名方式,可是参考签名文档,进行签名。(上面参数已经按照 ASCII 进行排序,大小写也请按照给出的进行加密) 注意:package 格式为 prepay_id=xxxxxxxxxxxx。xxxx 部分为统一下单获取的 prepay_id
代码参考:
public function renCreatesign()
{
$build_data = [
‘appId’ => $this->appid,
‘nonceStr’ => $this->nonce_str,
‘package’ => $this->package,
‘signType’ => $this->sign_type,
‘timeStamp’ => $this->timestamp,
‘key’ => $this->key,
];
$result = http_build_query($build_data);
$put_data = str_replace(‘%3D’, ‘=’, $result); // 格式化网址
$signatrue = md5($put_data);
$this->resign = strtoupper($signatrue);
}
至此,所有的签名应经完成,控制器使用 unifiedorder() 进行参数获取。
前端
这里开始使用 jsapi 做支付动作
WeixinJSBridge.invoke(
“getBrandWCPayRequest”,
{
appId: res.appid, // 公众号名称,由商户传入
timeStamp: res.timeStamp, // 时间戳,自 1970 年以来的秒数
nonceStr: res.nonce_str, // 随机串
package: res.package,
signType: res.signType, // 微信签名方式:
paySign: res.sign // 微信签名
},
function(res) {
alert(JSON.stringify(res));
if (res.err_msg == “get_brand_wcpay_request:ok”) {
// 使用以上方式判断前端返回, 微信团队郑重提示:
//res.err_msg 将在用户支付成功后返回 ok,但并不保证它绝对可靠。
}
}
);
前端所有需要调用的参数均在 unifiedorder() 可以获取到。
这里提一下 WeixinJSBridge.invoke 与 wx.chooseWXPay 的区别。WeixinJSBridge.invoke 可以不用引用 weixinjs 便可使用,也不需要 config。在安卓手机上也能回调出错误信息。wx.chooseWXPay 需要引用 weixinjs,也需要使用 config,而且在安卓手机上面的提示特别不友好。
结语
微信支付文档说实话真的很坑 很坑。貌似写文档的小哥这天情绪不好。写出来的让人也感觉到了情绪不好。以上为本人切身操作写出的教程。如还有补充的地方可以随时留言评论。