記一次小程式支付開發--thinkphp5.1
阿新 • • 發佈:2020-09-04
上篇記錄了小程式授權登入的介面,這次就直接收尾記錄一下小程式支付,依舊先上文件 小程式支付文件
小程式支付是包含在JSAPI支付的型別中的,所以有過JSAPI支付開發經驗的,不值一提。
開發之前,小程式開通微信支付,並繫結關聯商戶號,這其中的稽核過程就不說了,比較多,但跟著步驟走一般都沒問題。
直接記錄開發過程:
前端請求支付介面,後臺需要獲取使用者的openid,由於我已經在前面把openid獲取並存到資料庫,因此這裡直接讀取資料庫的openid。若想獲取openid請移步上一篇文章《小程式授權登入》
public function pay_order() { $id= intval(input('id')); $user = get_user(input('openid')); if (empty($user)) { return json(['status' => 201, 'msg' => '請先登入']); }
//查詢自己資料庫的訂單 $order = Db::name('order')->where('id', $id)->where('status', 1)->where('user_id', $user['id'])->find();if (empty($order)) { return json(['status' => 201, 'msg' => '訂單錯誤']); } $uniurl = "https://api.mch.weixin.qq.com/pay/unifiedorder"; //微信統一下單API $params = [ 'appid' => config('site.app_id'), //小程式app_id 'mch_id' => config('site.mch_id'), //微信支付商戶號 'nonce_str' => createNoncestr(), //隨機字串 'sign_type' => 'MD5', //簽名方式MD5 'body' => '夜夜夜夜', 'out_trade_no' => $order['order_num'], //訂單編號 // 'total_fee' => $order['money'] * 100, //價格,單位 :分 'total_fee' => 1, 'spbill_create_ip' => 'X.X.X.X', //伺服器地址 'notify_url' => 'http://xxx.com/pay_order', //微信支付結果回撥通知地址 'trade_type' => 'JSAPI', 'openid' => $user['openid'], //使用者openid ]; $params['sign'] = getSign($params); $xml = arrayToXml($params); $res = curl_post($uniurl,$xml); $data = xmlToArr($res); $return_params = [ 'appId' => config('site.app_id'), 'timeStamp' => strval(time()), 'nonceStr' => createNoncestr(), 'package' => 'prepay_id='.$data['prepay_id'], 'signType' => 'MD5' ]; $return_params['paySign'] = getSign($return_params); unset($return_params['appId']); $return_params['total_fee'] = 0.01; return json_encode($return_params); }
我這裡呢加密方式使用了預設的MD5加密,也可以使用HMAC-SHA256方式。
下面是一些用到的函式
生成隨機字串nonce_str函式:
function createNoncestr($length = 32) { $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; $str = ""; for ($i = 0; $i < $length; $i++) { $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1); } return $str; }
對請求引數進行簽名函式,簽名是重中之重,可以在微信官方提供的簽名校驗校驗一下,加密是否成功。
function getSign($param) { if (isset($param['sign'])) { unset($param['sign']); } ksort($param); $str = urldecode(http_build_query($param)); $str .= '&key=' . config('site.mch_key'); return strtoupper(md5($str)); }
下面是陣列轉XML,XML轉陣列,因為請求引數必須為XML格式
/** * 陣列轉XML */ function arrayToXml($arr) { if (!is_array($arr) || count($arr) == 0) return ''; $xml = "<xml>"; foreach ($arr as $key => $val) { if (is_numeric($val)) { $xml .= "<" . $key . ">" . $val . "</" . $key . ">"; } else { $xml .= "<" . $key . ">" . $val . "</" . $key . ">"; } } $xml .= "</xml>"; return $xml; } /** * XML轉為陣列 * @param $xml * @return mixed|string * @create_time: 2020-09-01 15:06:49 */ function xmlToArr($xml) { if ($xml == '') return ''; libxml_disable_entity_loader(true); $arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true); return $arr; }
curl_post,請求介面函式
// post方法 function curl_post($url, $postData) { $ch = curl_init(); $header = ['Accept-Charset: utf-8']; curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST"); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)'); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_AUTOREFERER, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $tmpInfo = curl_exec($ch); if (curl_errno($ch)) { return false; } else { curl_close($ch); return $tmpInfo; } }
之後請求統一下單介面,會得到預支付交易標識prepay_id,需要再次對調起支付API的引數進行簽名。如上變數return_params所示。之後就可以返回給前端,前端喚起微信支付。
支付成功的回撥也是很重要的:如下,需要對返回的資料進行延籤,並比對金額是否正確
//訂單支付成功處理邏輯 public function pay_order() { $arr = file_get_contents('php://input'); $data = xmlToArr($arr); $sign = getSign($data); if ($sign == $data['sign']) { if ($data['return_code'] == 'SUCCESS' || $data['result_code'] == 'SUCCESS') { $orderInfo = Db::name('order')->where('order_num', $data['out_trade_no'])->find(); $user = Db::name('user')->where('id', $orderInfo['user_id'])->find(); if (!empty($orderInfo) && !empty($user)) { //判斷訂單是否已經處理 if ($orderInfo['status'] == 2) { return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"; } //判斷返回金額和訂單金額是否一致 if ($orderInfo['money']*100 == $data['total_fee']) { /**此處寫自己的成功之後的邏輯 * XXXX */ //記錄日誌 Log::record($user['phone'].'支付了一筆訂單,訂單編號:'.$orderInfo['order_num'],'wxpay'); return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"; } } } } else { return 'Access Deny'; } }
這裡處理成功之後需要給微信返回
<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>這樣一條資訊,告知微信伺服器已收到通知並處理成功。這樣微信伺服器便不會繼續請求,否則會按一定時間規則進行請求。