PHP實現微信支付(微信小程式版)
阿新 • • 發佈:2018-12-13
先解答上一篇部落格中form_id的獲取方法: js程式碼
Page({
formSubmit: function (e) {
console.log("formid:"+e.detail.formId)
},
})
wxml程式碼
<form bindsubmit="formSubmit" report-submit="ture">
<button formType="submit">submit</button>
</form>
結果:
注意兩點
- 表單wxml中需要加入,普通表單是不攜帶formId。
- formId只在真器中才能獲取,開發者工具無法獲取。
下面開始講解PHP實現微信小程式支付
準備工作:
1.你的小程式做過微信認證並且開通微信支付(相信能學程式設計的都不是愚笨之人,跟著微信指引填寫資料就行,剩下就等他客服打電話給你) 2.微信小程式appid 3.微信商戶號mch_id(開戶郵件裡面有) 4.微信API金鑰key(登入商戶平臺->賬戶中心->API安全自行設定)
開發步驟:
1.獲取prepay_id 2.獲取paySign
封裝方法程式碼:
<?php namespace App\libs\Pay; use App\libs\HttpUtils\HttpUtils; class WxPay { const url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; const key = ""; const appid = ""; const mch_id = ""; const total_fee = ""; const notify_url = ""; const body = ""; /** * 微信統一下單 所有引數為必填項 資料傳輸格式為xml 提交方式POST 其他可選項參照微信官方文件 * @param url 統一下單地址 * @param key API金鑰 微信支付平臺自定義 * @param appid 小程式appid 微信公眾平臺獲取 * @param mch_id 商戶號 微信支付平臺獲取 * @param total_fee 金額 單位:分 作者寫的東西金額是固定的,可作為引數傳入函式 * @param notify_url 非同步接收微信支付結果通知的回撥地址 * @param nonce_str 隨機字串,長度要求在32位以內 下方已給出方法str_rand() * @param sign 通過簽名演算法計算得出的簽名值 簽名演算法getSignTemp():1.引數名ASCII碼從小到大排序(字典序)2.如果引數的值為空不參與簽名;3.引數名區分大小寫 4.驗證呼叫返回或微信主動通知簽名時,傳送的sign引數不參與簽名,將生成的簽名與該sign值作校驗。 * @param out_trade_no 商戶系統內部訂單號,要求32個字元內,只能是數字、大小寫字母_-|*且在同一個商戶號下唯一 * @param openid 使用者對應小程式的openid * @param body 商品描述 如:騰訊充值中心-QQ會員充值 * @param trade_type 小程式取值如下:JSAPI 其他支付參照官方文件 */ public static function PrePay($out_trade_no,$openid){ $nonce_str=self::str_rand(); //生成隨機32位字串 $SignTemp=self::getSignTemp($nonce_str,$openid,$out_trade_no); // 生成簽名 $data=[ "appid"=>self::appid, //appid "mch_id"=>self::mch_id, //商戶號 "nonce_str"=>$nonce_str, //隨機字串 "sign"=>$SignTemp, //簽名 "body"=>self::body, //商品描述 "out_trade_no"=>$out_trade_no, //訂單號 "total_fee"=>self::total_fee, //金額 "trade_type"=>"JSAPI", //支付型別 小程式取值如下:JSAPI 其他支付參照官方文件 "notify_url"=>self::notify_url, //非同步接收微信支付結果通知的回撥地址 "openid"=>$openid //使用者對應小程式的openid ]; $result=HttpUtils::curl(self::url,$params=self::XmlAndArray($data,"array"),$ispost=1,$https=1);//請求介面得到預支付資訊,curl方法在前一篇文章已經貼出,引數必須為xml格式,方法已給出XmlAndArray() return self::XmlAndArray($result,"xml"); //轉化為陣列方便取值,返回結果 } public static function getPaySign($prepay_id,$nonce_str){ $timeStamp=time();//時間戳 $PaySign=md5("appId=".self::appid."&nonceStr=".$nonce_str."&package=prepay_id=".$prepay_id."&signType=MD5&timeStamp=".(string)$timeStamp."&key=".self::key);//生成支付簽名,嚴格按照官方要求填寫引數 return ["PaySign"=>$PaySign,"timeStamp"=>$timeStamp];//返回簽名以及時間戳 } private static function getSignTemp($nonce_str,$openid,$out_trade_no){ $SignTemp="appid=".self::appid."&body=".self::body."&mch_id=".self::mch_id."&nonce_str=".$nonce_str."¬ify_url=".self::notify_url."&openid=".$openid."&out_trade_no=".$out_trade_no."&total_fee=".self::total_fee."&trade_type=JSAPI";//生成下單簽名,嚴格按照官方要求填寫引數 $SignTemp=md5($SignTemp."&key=".self::key); return $SignTemp; //返回簽名,統一下單需要呼叫 } /** * 陣列與XML互轉 方便取資料 * @param $data * @param $type 轉換物件 * return 陣列或XML */ private static function XmlAndArray($data,$type) { if(!$data){ return false; } else if($type=="xml"){ libxml_disable_entity_loader(true); $values = json_decode(json_encode(simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $values; } else if($type=="array"){ $xml="<xml>"; foreach ($data as $key=>$val){ $xml.="<".$key.">".$val."</".$key.">"; } $xml.="</xml>"; return $xml; } } /** * 隨機字串生成 * @param $length 字串長度 * @param $char 字符集 * return $string 隨機字串 */ private static function str_rand($length = 32, $char = 'abcdefghijklmnopqrstuvwxyz0123456789') { $str =""; for ( $i = 0; $i < $length; $i++ ) { $str .= substr($char, mt_rand(0, strlen($char)-1), 1); } return $str; } }
呼叫程式碼,我用的laravel框架,可以自行重構
<?php namespace App\Http\Controllers\Pay; use App\Http\Controllers\Controller; use App\libs\Pay\WxPay; use Illuminate\Http\Request; class PayController extends Controller { public function getPrePay(Request $request){ $openid=$request->get("openid"); //接收openid $out_trade_no=""; //訂單號 $PrePay=WxPay::PrePay($out_trade_no,$openid); //統一下單 if($PrePay["return_code"]=="FAIL"){ /*返回錯誤*/ } else{ $PrePayId=$PrePay["prepay_id"]; // 獲取預支付id $PaySign=WxPay::getPaySign($PrePayId,$PrePay["nonce_str"]); 生成支付簽名 echo json_encode(["prepay_id"=>$PrePayId,"paySign"=>$PaySign["PaySign"],"nonce_str"=>$PrePay["nonce_str"],"timeStamp"=>$PaySign["timeStamp"],"order_no"=>$out_trade_no]); //返回小程式中拉起支付所需要的資訊 } } }
小程式呼叫程式碼:
wx.request({
url: "你的伺服器端介面地址",
data: {
openid: "使用者的openid",
},
header: {
'content-type': 'application/json'
},
success: function (res) {
let prepay_id = res.data.prepay_id //預支付id
let order_no = res.data.order_no //訂單號
let timeStamp = String(res.data.timeStamp) //伺服器端返回的時間戳,必須強轉字串,微信要求的
let nonceStr = res.data.nonce_str //伺服器返回的隨機字串
let package = 'prepay_id=' + res.data.prepay_id //package需要這麼填寫,自己注意
let paySign = res.data.paySign //支付簽名
if (res.data.code == "") {
//伺服器端預支付處理失敗提示
wx.showToast({
title: '下單失敗',
icon: 'none',
duration: 1000
})
}
else {
// 拉起微信支付
wx.requestPayment({
'timeStamp': timeStamp,
'nonceStr': nonceStr,
'package': package,
'signType': 'MD5',
'paySign': paySign,
success: function (res) {
//介面呼叫成功的回撥函式
},
fail:function(res){
//介面呼叫失敗的回撥函式
},
complete:function(res){
//介面呼叫結束的回撥函式(呼叫成功、失敗都會執行)
}
})
}
}
})
總結
- 最重要的就是生成簽名一定要按照官方給出的文件,引數順序不能搞錯!!!
- 所有介面返回的都是xml,為了方便我都轉化成陣列,方法也在程式碼中給出。
- 隨機字串生成後,會在統一下單介面返回值中給出,生成PaySign簽名時延用就行,第一次開發就卡在這,能拉起支付,然後閃退說PaySign錯誤
- 統一下單介面返回的prepay_id就是模板訊息需要的引數,支付成功後就可以用此引數呼叫模板訊息提示使用者
- 作者自己寫了個小程式BadBoy,有興趣的可以搜尋來玩玩,其中的程式碼可找作者索取
- 微信小程式交流群:895964328 php交流群:165728481