1. 程式人生 > >php微信公眾號JSAPI支付

php微信公眾號JSAPI支付

首先是生成簽名方法

    //生成簽名
    private function MakeSign($params, $KEY)
    {
        //簽名步驟一:按字典序排序陣列引數
        ksort($params);
        $string = $this->ToUrlParams($params);  //引數進行拼接key=value&k=v
        //簽名步驟二:在string後加入KEY
        $string = $string . "&key=" . $KEY;
        //簽名步驟三:MD5加密
        $string
= md5($string); //簽名步驟四:所有字元轉為大寫 $result = strtoupper($string); return $result; } //模擬post提交(調介面用)

模擬post提交

    //傳送http請求

    private function http_request($url, $data = null, $headers = array())
    {
        $curl = curl_init();
        if (count($headers) >= 1) {
            curl_setopt($curl
, CURLOPT_HTTPHEADER, $headers); } curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); // $zs1 = APP_PATH . "app/cert/apiclient_cert.pem"; $zs2 = APP_PATH . "app/cert/apiclient_key.pem"
; // curl_setopt ( $curl, CURLOPT_SSLCERT, $zs1 ); // curl_setopt ( $curl, CURLOPT_SSLKEY, $zs2 ); //設定證書 //使用證書:cert 與 key 分別屬於兩個.pem檔案 curl_setopt($curl, CURLOPT_SSLCERTTYPE, 'PEM'); curl_setopt($curl, CURLOPT_SSLCERT, $zs1); curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM'); curl_setopt($curl, CURLOPT_SSLKEY, $zs2); if (!empty($data)) { curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, $data); } curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); $output = curl_exec($curl); curl_close($curl); return $output; }

xml轉換陣列

    //獲取xml裡面資料,轉換成array
    private function xml2array($xml)
    {
        $p = xml_parser_create();
        xml_parse_into_struct($p, $xml, $vals, $index);
        xml_parser_free($p);
        $data = "";
        foreach ($index as $key => $value) {
            if ($key == 'xml' || $key == 'XML') continue;
            $tag = $vals[$value[0]]['tag'];
            $value = $vals[$value[0]]['value'];
            $data[$tag] = $value;
        }
        return $data;

    }

然後是統一下單方法

   //構建訂單/* 首先在伺服器端呼叫微信【統一下單】介面,返回prepay_id和sign簽名等資訊給前端,前端呼叫微信支付介面 */

    private function construct_order($total_fee, $openid, $order_sn, $order_id)
    {
        //獲取系統的配置
        $appid = '';//如果是公眾號 就是公眾號的appid;小程式就是小程式的appid
        $body = '商戶號構造訂單';
        $mch_id = '';
        $KEY = '';
        $nonce_str = $this->rand_str(12);//隨機字串
        $notify_url = config('app_url') . "pay/notify";  //支付完成回撥地址url,不能帶引數
        $out_trade_no = $order_sn;//商戶訂單號
        $spbill_create_ip = $_SERVER['SERVER_ADDR'];
        $trade_type = 'JSAPI';//交易型別 預設JSAPI

        //這裡是按照順序的 因為下面的簽名是按照(字典序)順序 排序錯誤 肯定出錯
        $post['appid'] = $appid;
        $post['body'] = $body;
        $post['mch_id'] = $mch_id;
        $post['nonce_str'] = $nonce_str;//隨機字串
        $post['notify_url'] = $notify_url;
        $post['openid'] = $openid;
        $post['out_trade_no'] = $out_trade_no;
        $post['spbill_create_ip'] = $spbill_create_ip;//伺服器終端的ip
        $post['total_fee'] = intval($total_fee);        //總金額 最低為一分錢 必須是整數
        $post['trade_type'] = $trade_type;
        $sign = $this->MakeSign($post, $KEY);              //簽名
        $this->sign = $sign;

        //$post_xml = '<xml><appid>'.$appid.'</appid><body>'.$body.'</body><mch_id>'.$mch_id.'</mch_id><nonce_str>'.$nonce_str.'</nonce_str><notify_url>'.$notify_url.'</notify_url><openid>'.$openid.'</openid><out_trade_no>'.$out_trade_no.'</out_trade_no><spbill_create_ip>'.$spbill_create_ip.'</spbill_create_ip><total_fee>'.$total_fee.'</total_fee><trade_type>'.$trade_type.'</trade_type><sign>'.$sign.'</sign></xml>';


        $post_xml = "
                <xml>
                    <appid>$appid</appid>
                    <body>$body</body>
                    <mch_id>$mch_id</mch_id>
                    <nonce_str>$nonce_str</nonce_str>
                    <notify_url>$notify_url</notify_url>
                    <openid>$openid</openid>
                    <out_trade_no>$out_trade_no</out_trade_no>
                    <spbill_create_ip>$spbill_create_ip</spbill_create_ip>
                    <total_fee>{$post["total_fee"]}</total_fee>
                    <trade_type>$trade_type</trade_type>
                    <sign>$sign</sign>
                </xml>";

        //統一下單介面prepay_id
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        $xml = $this->http_request($url, $post_xml);     //POST方式請求http
        $array = $this->xml2array($xml);               //將【統一下單】api返回xml資料轉換成陣列,全要大寫
        $array['my_sign'] = $sign;
        $array['post_xml'] = $post_xml;
        $array['source_xml'] = $xml;
        if ($array['RETURN_CODE'] == 'SUCCESS' && $array['RESULT_CODE'] == 'SUCCESS') {
            $time = time();
            $tmp = '';                            //臨時陣列用於簽名
            $tmp['appId'] = $appid;
            $tmp['nonceStr'] = $nonce_str;
            $tmp['package'] = 'prepay_id=' . $array['PREPAY_ID'];
            $tmp['signType'] = 'MD5';
            $tmp['timeStamp'] = "$time";


            $data['state'] = 1;
            $data['timeStamp'] = "$time";           //時間戳
            $data['nonceStr'] = $nonce_str;         //隨機字串
            $data['signType'] = 'MD5';              //簽名演算法,暫支援 MD5
            $data['package'] = 'prepay_id=' . $array['PREPAY_ID'];   //統一下單介面返回的 prepay_id 引數值,提交格式如:prepay_id=*
            $data['paySign'] = $this->MakeSign($tmp, $KEY);       //簽名,具體簽名方案參見微信公眾號支付幫助文件;
            $data['prepay_id'] = $array['PREPAY_ID'];
            $data['out_trade_no'] = $out_trade_no;
            $data['order_id'] = $order_id;
        } else {
            $data['statusCode'] = USER_ERROR;
            $data['statusMsg'] = "請求錯誤";
            $data['data']['RETURN_CODE'] = $array['RETURN_CODE'];
            $data['data']['RETURN_MSG'] = $array['RETURN_MSG'];
        }
        return $data;
    }

回撥函式

    //訂單支付回撥
    function notify()
    {
        $post = $this->post_data();    //接受POST資料XML個數
        $post_data = $this->xml_to_array($post);   //微信支付成功,返回回撥地址url的資料:XML轉陣列Array
        //輸出訂單號
        $order_sn = $post_data['out_trade_no'];
        $key = '';//key

        $postSign = $post_data['sign'];
        unset($post_data['sign']);

        /* 微信官方提醒:
         *  商戶系統對於支付結果通知的內容一定要做【簽名驗證】,
         *  並校驗返回的【訂單金額是否與商戶側的訂單金額】一致,
         *  防止資料洩漏導致出現“假通知”,造成資金損失。
        */
        ksort($post_data);// 對資料進行排序
        $str = $this->ToUrlParams($post_data);//對陣列資料拼接成key=value字串
        $KEY = $key;
        $str .= '&key=';
        $str .= $KEY;
        $user_sign = strtoupper(md5($str));   //再次生成簽名,與$postSign比較


        if ($post_data['return_code'] == 'SUCCESS') {
            //判斷證書是否正確
            if ($postSign != $user_sign) {
                Log::write('簽名不匹配');
                exit;
            }
            $order_info = Db::table('cs_hborders')->where('order_sn', $order_sn)->find();

            if ($order_info['pay_status'] != 1) {
               //支付成功後操作(如推送、更改訂單狀態等)
                    }
                }
            }
            echo 'success';
        } else {
            echo '微信支付失敗';
        }
    }