php開發微信APP支付接口


之前在開發APP中用到了微信支付,因為是第一次用,所以中途也遇到了好多問題,通過查看文檔和搜集資料,終於完成了該功能的實現。在這里簡單分享一下后台php接口的開發實例。
原文地址:代碼匯個人博客 http://www.codehui.net/info/4.html

開發流程

1:用戶在商戶APP中選擇商品,提交訂單,選擇微信支付。
2:商戶后台收到用戶支付單,調用微信支付統一下單接口。參見【統一下單API】。
3:統一下單接口返回正常的prepay_id,再按簽名規范重新生成簽名后,將數據傳輸給APP。參與簽名的字段名為appid,partnerid,prepayid,noncestr,timestamp,package。注意:package的值格式為Sign=WXPay
4:商戶APP調起微信支付。api參見本章節【app端開發步驟說明
5:商戶后台接收支付通知。api參見【支付結果通知API
6:商戶后台查詢支付結果。,api參見【查詢訂單API

開發中

首先呢我們需要拿到三個參數(appid,mch_id,key),這三個參數分別對應的是 在微信開發平台中創建的移動應用appid,微信支付商戶號商戶支付秘鑰,詳情看參考【支付結果通知API
然后我們先把統一下單所需要的參數列出來

$request_data = array(
      'appid' => C('WX_APPID'),                         #應用APPID
       'mch_id' => C('WX_MCHID'),                        #商戶號
       'trade_type' => 'APP',                            #支付類型
       'nonce_str' => \Org\Util\String::randString(30),  #隨機字符串 不長於32位
       'body' => '商品名稱',                              #商品名稱
       'out_trade_no' => '12345678912456',               #商戶后台訂單號
       'total_fee' => '1',                               #商品價格
       'spbill_create_ip' => get_client_ip(),            #用戶端實際ip
       'notify_url' => 'http://***/app/index.php/Home/Wxpay/wx_notify', #異步通知回調地址
  );

這些都是請求參數必填項,其他參數請查看文檔
下來我們就要使用這些參數生成簽名了

$request_data['sign'] = $this -> get_sign($request_data);

我們下來需要把微信請求的數據拼裝成 xml格式,注意:xml數據要使用<![CDATA[]]>包括

$xml_data = $this -> set_xmldata($request_data);

打印$xml_data結果如下

<xml>
	<appid><![CDATA[wx7ad3cc6c6111111]]></appid>
	<mch_id><![CDATA[1494741111]]></mch_id>
	<trade_type><![CDATA[APP]]></trade_type>
	<nonce_str><![CDATA[WXXWkMDOgLIqhUnITfNrBbJEVGQdRO]]></nonce_str>
	<body><![CDATA[u5546u54c1u540du79f0]]></body>
	<out_trade_no><![CDATA[12345678912456]]></out_trade_no>
	<total_fee><![CDATA[1]]></total_fee>
	<spbill_create_ip><![CDATA[1.86.242.193]]></spbill_create_ip>
	<notify_url><![CDATA[http://***/app/index.php/Home/Wxpay/wx_notify]]></notify_url>
	<sign><![CDATA[EC0BFB3434A72F20C2CA3378BF07264C]]></sign>
</xml>

現在就可以向微信發送請求了

$res = $this -> send_prePaycurl($xml_data);

這是請求的返回值

{
  return_code: "SUCCESS",       #業務結果 只有這里返回SUCCESS才會有prepay_id
  return_msg: "OK",             #返回結果描述
  appid: "wx7ad3cc6c6111111",  #應用APPID
  mch_id: "1494741111",         #商戶號
  nonce_str: "jkh9mmRlmSHBJxO0",   #隨機字符串
  sign: "AF3B26B1E58591D6565E61DDFBB7837B",  #簽名
  result_code: "SUCCESS",    #也是業務結果
  prepay_id: "wx20171226005556c5c65b325a0132782836", #預支付交易會話標識,用於APP請求微信支付調用,有效期兩小時
  trade_type: "APP"  #支付類型
}

到這里拿到prepay_id還沒完我們還需要對返回的數據進行二次簽名

if($res['return_code'] == 'SUCCESS' && $res['result_code'] == 'SUCCESS'){
       $two_data['appid'] = C('WX_APPID');  #APPID
       $two_data['partnerid'] = C('WX_MCHID');  #商戶號
       $two_data['prepayid'] = $res['prepay_id'];  //預支付交易會話標識
       $two_data['noncestr'] = \Org\Util\String::randString(30);  
       $two_data['timestamp'] = time();   #時間戳
       $two_data['package'] = "Sign=WXPay";   #固定值
       $two_data['sign'] = $this -> get_twosign($two_data);  #二次簽名
       $this->ajaxReturn(array('code'=>200,'info'=>$two_data));
 }else{
       $this->ajaxReturn(array('code'=>201,'info'=>$res['err_code_des']));
 }

然后就可以在商戶APP端通過prepayid進行支付了
下面我們來列出上面調用的幾個公共方法

    //一次簽名的函數
    private function get_sign($data){
        ksort($data);
        $str = '';
        foreach ($data as $key => $value) {
            $str .= !$str ? $key . '=' . $value : '&' . $key . '=' . $value;
        }
        $str.='&key='.C('WX_KEY');
        $sign = strtoupper(md5($str));
        return $sign;
    }
    //二次簽名的函數
    private function get_twosign($data){
        $sign_data = array(
            "appid"=>$data['appid'],
            "partnerid"=>$data['partnerid'],
            "prepayid"=>$data['prepayid'],
            "noncestr"=>$data['noncestr'],
            "timestamp"=>$data['timestamp'],
            "package"=>$data['package'],
        );
        return $this -> get_sign($sign_data);
    }
    //生成xml格式的函數
    private function set_xmldata($data) {
        $xmlData = "<xml>";
          foreach ($data as $key => $value) {
           $xmlData.="<".$key."><![CDATA[".$value."]]></".$key.">";
          }
          $xmlData = $xmlData."</xml>";
          return $xmlData;
    }
    //通過curl發送數據給微信接口的函數
    private function send_prePaycurl($xmlData) {
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        $header[] = "Content-type: text/xml";
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $xmlData);
        $data = curl_exec($curl);
        if (curl_errno($curl)) {
            print curl_error($curl);
        }
        curl_close($curl);
        return $this -> _xmldataparse($data);
    }
    //xml數據解析函數
    private function _xmldataparse($data){
        $msg = array();
        $msg = (array)simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
        return $msg;
    }

微信回調

支付有了,肯定還得有回調

    //微信回調
    public function wx_notify(){  
       //允許從外部加載XML實體(防止XML注入攻擊)
        libxml_disable_entity_loader(true);  
        $postStr = $this -> post_data();  #接收微信返回數據xml格式
        $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);   #xml格式數據轉換成對象
        $arr = $this -> object_toarray($postObj); #對象轉成數組  
        ksort($arr);   # 對數據進行排序  
        $str = $this -> params_tourl($arr);  #對數據拼接成字符串 
        $user_sign = strtoupper(md5($str));   //把微信返回的數據進行再次簽名
       //驗證簽名
        if($user_sign == $arr['sign']){
            //驗證簽名成功  處理商戶訂單邏輯
            //給微信返回接收到數據通知
            return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
        }else{
            //簽名驗證失敗   微信會再次訪問回調方法
            return '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
        }
    } 

回調用到的方法如下

// 接收post數據  
    /*  
    *  微信是用$GLOBALS['HTTP_RAW_POST_DATA'];這個函數接收post數據的  
    */  
    public function post_data(){  
        $receipt = $_REQUEST;  
        if($receipt==null){  
            $receipt = file_get_contents("php://input");  
            if($receipt == null){  
                $receipt = $GLOBALS['HTTP_RAW_POST_DATA'];  
            }  
        }  
        return $receipt;  
    }  
      
    //把對象轉成數組  
    public function object_toarray($arr) {  
        if(is_object($arr)) {  
            $arr = (array)$arr;  
        } if(is_array($arr)) {  
            foreach($arr as $key=>$value) {  
                $arr[$key] = $this->object_toarray($value);  
            }  
        }  
        return $arr;  
    }  
      
      
     /**  
     * 格式化參數格式化成url參數  
     */  
    private function params_tourl($arr)  
    {  
        $weipay_key = C('WX_KEY');//微信的key,這個是微信支付給你的key,不要瞎填。  
        $buff = "";  
        foreach ($arr as $k => $v)  
        {  
            if($k != "sign" && $v != "" && !is_array($v)){  
                $buff .= $k . "=" . $v . "&";  
            }  
        }  
        $buff = trim($buff, "&");  
        return $buff.'&key='.$weipay_key;  
    }      

總結:首先微信支付的流程比較多,公眾號開放平台微信商戶,配置參數的時候要看仔細,不要后面的坑特別多,因為是第一次寫微信支付,可能會存在部分問題,歡迎大家可以在下面留言反饋。

這是最后完成的功能
效果演示_1225.gif
下面分享一下全部的代碼

<?php
namespace Home\Controller;
use Think\Controller;
/**
 * php開發微信app支付接口
 * @global  WX_APPID  開放平台->移動應用appid
 * @global  WX_MCHID  微信支付商戶號            
 * @global  WX_KEY    商戶支付秘鑰              
 * @author codehi <admin@codehui.net> 2017-12-23
 */
class WxpayController extends Controller
{
    /**
     * 微信支付統一下單 >>> 生成預支付交易單
     */
    public function wx_pay(){

       $request_data = array(
             'appid' => C('WX_APPID'),                         #應用APPID
             'mch_id' => C('WX_MCHID'),                        #商戶號
             'trade_type' => 'APP',                            #支付類型
             'nonce_str' => \Org\Util\String::randString(30),  #隨機字符串 不長於32位
             'body' => '商品名稱',                             #商品名稱
             'out_trade_no' => '12345678912456',                    #商戶后台訂單號
             'total_fee' => '1',                             #商品價格
             'spbill_create_ip' => get_client_ip(),            #用戶端實際ip
             'notify_url' => 'http://shop.lsmrsd.com/app/index.php/Home/Wxpay/wx_notify', #異步通知回調地址
        );        
        // 獲取簽名
        $request_data['sign'] = $this -> get_sign($request_data);
        // 拼裝數據
        $xml_data = $this -> set_xmldata($request_data);
        

        // 發送請求
        $res = $this -> send_prePaycurl($xml_data);
        $this->ajaxReturn($res);
        if($res['return_code'] == 'SUCCESS' && $res['result_code'] == 'SUCCESS'){
            $two_data['appid'] = C('WX_APPID');  #APPID
            $two_data['partnerid'] = C('WX_MCHID');  #商戶號
            $two_data['prepayid'] = $res['prepay_id'];  //預支付交易會話標識
            $two_data['noncestr'] = \Org\Util\String::randString(30);  
            $two_data['timestamp'] = time();
            $two_data['package'] = "Sign=WXPay";
            $two_data['sign'] = $this->get_twosign($two_data);
            $this->ajaxReturn(array('code'=>200,'info'=>$two_data));
        }else{
            $this->ajaxReturn(array('code'=>201,'info'=>$res['err_code_des']));
        }
    }
 
    //通過curl發送數據給微信接口的函數
    private function send_prePaycurl($xmlData) {
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        $header[] = "Content-type: text/xml";
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $xmlData);
        $data = curl_exec($curl);
        if (curl_errno($curl)) {
            print curl_error($curl);
        }
        curl_close($curl);
        return $this->_xmldataparse($data);
    }
 
    //xml數據解析函數
    private function _xmldataparse($data){
        $msg = array();
        $msg = (array)simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);
        return $msg;
    }
 
    //生成xml格式的函數
    private function set_xmldata($data) {
        $xmlData = "<xml>";
          foreach ($data as $key => $value) {
           $xmlData.="<".$key."><![CDATA[".$value."]]></".$key.">";
          }
          $xmlData = $xmlData."</xml>";
          return $xmlData;
    }
 
    //一次簽名的函數
    private function get_sign($data){
        ksort($data);
        $str = '';
        foreach ($data as $key => $value) {
            $str .= !$str ? $key . '=' . $value : '&' . $key . '=' . $value;
        }
        $str.='&key='.C('WX_KEY');
        $sign = strtoupper(md5($str));
        return $sign;
    }
 
    //二次簽名的函數
    private function get_twosign($data){
        $sign_data = array(
            "appid"=>$data['appid'],
            "partnerid"=>$data['partnerid'],
            "prepayid"=>$data['prepayid'],
            "noncestr"=>$data['noncestr'],
            "timestamp"=>$data['timestamp'],
            "package"=>$data['package'],
        );
        return $this->get_sign($sign_data);
    }

    //微信回調
    public function wx_notify(){  
       //允許從外部加載XML實體(防止XML注入攻擊)
        libxml_disable_entity_loader(true);  
        $postStr = $this -> post_data();//接收post數據  
        $postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);  
        $arr = $this -> object_toarray($postObj);//對象轉成數組  
        ksort($arr);// 對數據進行排序  
        $str = $this -> params_tourl($arr);//對數據拼接成字符串 
        $user_sign = strtoupper(md5($str));  
        if($user_sign == $arr['sign']){//驗證簽名 
            return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
        }else{
            return '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
        }
    }  
      
    // 接收post數據  
    /*  
    *  微信是用$GLOBALS['HTTP_RAW_POST_DATA'];這個函數接收post數據的  
    */  
    public function post_data(){  
        $receipt = $_REQUEST;  
        if($receipt==null){  
            $receipt = file_get_contents("php://input");  
            if($receipt == null){  
                $receipt = $GLOBALS['HTTP_RAW_POST_DATA'];  
            }  
        }  
        return $receipt;  
    }  
      
    //把對象轉成數組  
    public function object_toarray($arr) {  
        if(is_object($arr)) {  
            $arr = (array)$arr;  
        } if(is_array($arr)) {  
            foreach($arr as $key=>$value) {  
                $arr[$key] = $this->object_toarray($value);  
            }  
        }  
        return $arr;  
    }  
      
      
     /**  
     * 格式化參數格式化成url參數  
     */  
    private function params_tourl($arr)  
    {  
        $weipay_key = C('WX_KEY');//微信的key,這個是微信支付給你的key,不要瞎填。  
        $buff = "";  
        foreach ($arr as $k => $v)  
        {  
            if($k != "sign" && $v != "" && !is_array($v)){  
                $buff .= $k . "=" . $v . "&";  
            }  
        }  
        $buff = trim($buff, "&");  
        return $buff.'&key='.$weipay_key;  
    }      

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM