上篇记录了小程序授权登录的接口,这次就直接收尾记录一下小程序支付,依旧先上文档 小程序支付文档

小程序支付是包含在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>这样一条信息,告知微信服务器已收到通知并处理成功。这样微信服务器便不会继续请求,否则会按一定时间规则进行请求。

版权声明:本文为dlmg原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/dlmg/p/13613084.html