1. 程式人生 > 實用技巧 >記一次小程式支付開發--thinkphp5.1

記一次小程式支付開發--thinkphp5.1

上篇記錄了小程式授權登入的介面,這次就直接收尾記錄一下小程式支付,依舊先上文件 小程式支付文件

小程式支付是包含在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>這樣一條資訊,告知微信伺服器已收到通知並處理成功。這樣微信伺服器便不會繼續請求,否則會按一定時間規則進行請求。