在用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,而且在安卓手機上面的提示特別不友好。
結語
微信支付文檔說實話真的很坑 很坑。貌似寫文檔的小哥這天情緒不好。寫出來的讓人也感覺到了情緒不好。以上爲本人切身操作寫出的教程。如還有補充的地方可以隨時留言評論。