這幾天在做小程式的支付,沒有用官方的SDK,這裡就純用官方的文件搞一發。
* 注作者使用的PHP,不過支付流程都是這樣
開發前必讀
主要流程
- 小程式前端傳送求參請求
- 接受請求封裝 “統一下單” 獲取
package
- 小程式接受 “統一下單” 獲取的
package
值帶入wx.requestPayment
發起支付請求
準備工具
- 申請小程式微信支付
- 拿到小程式微信支付的商戶號及設定祕鑰
注意:小程式就只需要這兩步,如果是web的話還需要設定支付目錄授權域名,文件裡面也有寫的:https://pay.weixin.qq.com/wik…
統一下單
/**
* 統一訂單
*/
public function unifiedorder(){
// 以下配置是必填項,如有其它需求請自行配置
$config = array(
`appid` => `xxxxxxx`,//這裡是小程式appid
`mch_id` => `xxxxxxx`,//商戶ID
`nonce_str` => $this->getNonceStr(),//隨機字串
`body` => `這裡是測試 - 測試`,//請按照文件要求填寫合格名稱
`out_trade_no` => time().$this->getNonceStr(2),//流水單號
`total_fee` => `20`,//金額,分為單位,這裡是0.2元
`spbill_create_ip` => `123.123.123.123`,//當前IP
`notify_url` => `http://xxxx.com`,//請恕我愚昧,我沒搞懂他有什麼用
`trade_type` => `JSAPI`,//必須填寫JSAPI
`openid` => `xxxxxxxx`//當前使用者的openid,在trade_type=JSAPI的時候,此項就變成必填項了
);
$config[`sign`] = $this->getSignPay($config);
$xmlData = $this->ToXml($config);//轉成xml資料
$postData = $this->http_post($xmlData);
$arrayData = $this->FromXml($postData);
if($arrayData[`return_code`] == `SUCCESS` || $arrayData[`result_code`] == `SUCCESS`){
return $arrayData[`prepay_id`];//重點來了:走了這麼多路,就為了這個值。到這一步就證明成功一多半了。
}else{
return $arrayData;//返回錯誤
}
}
/**
* 獲取簽名
*/
public function getSignPay($config){
$key = `xxxxxxx`;//商戶祕鑰,就是自己生成的32位密碼
$strA = `appid=`.$config[`appid`].`&body=`.$config[`body`].`&mch_id=`.$config[`mch_id`].`&nonce_str=`.$config[`nonce_str`].`¬ify_url=`.$config[`notify_url`].`&spbill_create_ip`.$config[`spbill_create_ip`].`&total_fee=`.$config[`total_fee`].`&trade_type=`.$config[`trade_type`];//ASCII 字典序
$strB = $strA.`&key=`.$key;
$sign = strtoupper(md5($strB));//大寫MD5
return $sign;
}
/**
* 隨機字串 32位
*/
public function getNonceStr($length = 32){
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
$str ="";
for ( $i = 0; $i < $length; $i++ ) {
$str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);
}
return $str;
}
/**
* array轉XML
*/
public function ToXml($data){
if(!is_array($data) || count($data) <= 0){
throw new WxPayException("陣列資料異常!");
}
$xml = "<xml>";
foreach ($data as $key=>$val){
$xml.="<".$key.">".$val."</".$key.">";
}
$xml.="</xml>";
return $xml;
}
/**
* xml轉array
*/
public function FromXml($xml){
if(!$xml){
throw new WxPayException("xml資料異常!");
}
libxml_disable_entity_loader(true);
$this->values = json_decode(json_encode(simplexml_load_string($xml, `SimpleXMLElement`, LIBXML_NOCDATA)), true);
return $this->values;
}
/**
* post 請求
*/
public function http_post($url,$param,$post_file=false){
$oCurl = curl_init();
if(stripos($url,"https://")!==FALSE){
curl_setopt($oCurl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($oCurl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($oCurl, CURLOPT_SSLVERSION, 1); //CURL_SSLVERSION_TLSv1
}
if (PHP_VERSION_ID >= 50500 && class_exists(`CURLFile`)) {
$is_curlFile = true;
} else {
$is_curlFile = false;
if (defined(`CURLOPT_SAFE_UPLOAD`)) {
curl_setopt($oCurl, CURLOPT_SAFE_UPLOAD, false);
}
}
if (is_string($param)) {
$strPOST = $param;
}elseif($post_file) {
if($is_curlFile) {
foreach ($param as $key => $val) {
if (substr($val, 0, 1) == `@`) {
}
}
}
$strPOST = $param;
} else {
$aPOST = array();
foreach($param as $key=>$val){
$aPOST[] = $key."=".urlencode($val);
}
$strPOST = join("&", $aPOST);
}
curl_setopt($oCurl, CURLOPT_URL, $url);
curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($oCurl, CURLOPT_POST,true);
curl_setopt($oCurl, CURLOPT_POSTFIELDS,$strPOST);
$sContent = curl_exec($oCurl);
$aStatus = curl_getinfo($oCurl);
curl_close($oCurl);
if(intval($aStatus["http_code"])==200){
return $sContent;
}else{
return false;
}
}
好了現在已經獲取到了 prepay_id
的值,我們的統一下單就算完成了,其實我更樂意叫他資料封裝
小程式微信支付
先來一個插曲,首先我們小程式的前端需要去觸發pay,實現的功能肯定是要點選小程式的一個觸發,然後才能支付對吧,
pay:function(e){
//這裡面使用post去請求。然後通過我接下來要寫的API支付程式碼獲取小程式支付引數
success:function(res){
wx.requestPayment({
`timeStamp`:toString(res.timeStamp),//這裡轉字串,這裡被坑過,不轉的話可能會出現total_fee為空
`nonceStr`:toString(res.nonceStr),
`package`:toString(res.package),
`signType`:`MD5`,
`paySign`:toString(res.paySign),
success:function(res){
console.log(res);//這裡可以跳轉到帶參地址
},
fail:function(res){
console.info(`支付失敗`,res);
},
complete:function(){
console.info(`支付觸發回撥`,res);
}
})
}
}
api支付
也就是上面小程式程式碼的後端請求地址
/**
* api組裝資料
*/
public function payApiBlack(){
$appid = `xxxxxx`;//小程式appid,上面有重複,不過這樣比較直觀
$timeStamp = time();
$nonceStr = $this->getNonceStr();//這是呼叫統一下單裡面的方法,為了直觀,我把這些程式碼都寫在了一個類裡
$package = `prepay_id=`.$this->unifiedorder();
$signType = `MD5`;
$key = `xxxxxx`;//這裡是商戶祕鑰,32位,同上面也有
$strA = `appId=`.$appid.`&nonceStr=`.$nonceStr.`package=`.$package.`&= signType=`.$signType.`&timeStamp=`.$timeStamp.`&key=`.$key;
$paySign = strtoupper(md5($strA));
$data = array(
`appid`=>$appid,
`timeStamp`=>$timeStamp,
`nonceStr`=>$nonceStr,
`package`=>$package,
`signType`=>$signType
);
return $data;//返回給小程式
}
以上就是全部程式碼,還有小程式的支付回撥沒有什麼資訊,所以,我的思路判斷success後進行跳轉帶參
//此程式碼為wx.requestPayment success,部分程式碼省略
//res 回撥引數包括使用者uid及其他重要傳遞
success:function(res){
wx.redirect({
url:`pages/pay/done?uid=`+res.uid
})
}
當然那個統一下單的 notify_url
好像與回撥有關,至於怎麼用,試了幾次回撥的CURD都沒反應,所以有空再研究啦。
以上程式碼僅作為支付流程解釋,所以真正要用到專案上,還是去套官方的SDK吧,畢竟涉及到錢嘛