TP5 -- 微信支付整合(APP,JSAPI)


原本項目上有一個提現功能,因原客戶商戶號和流水原因一直沒做。

最近因為剛剛符合條件的原因,今天才開始做。

順便瀏覽下博客,發現原來寫的微信支付有些那個啥了。

因為最近項目前后端分離居多,而且項目也多JSAPI、APP、NATIVE等多種一起居多

所以就將原來的微信支付 重新整合了下

與原來的相比,整合過后的更適用於前后端分離來做

如果沒有前后端分離,還可以借鑒原來的公眾號支付:tp5 -- 微信公眾號支付

不過最好還是自己調整下,畢竟那個代碼比較久了

好了,話不多說,還是直接上代碼吧

因為此支付是自己封裝,同樣還是在 extend/  文件下 的Wxpay.php

代碼如下:

<?php 
namespace pay;

class Wxpay{
    private $config =[
        "appid"  => "",  // 開放平台或商戶平台APPID
        "mch_id" => "",  // 商戶平台 商戶號
        "key"    => "",  // 商戶平台 秘鑰KEY
        "TOKEN"  => "",  // 此參數非必傳 有的前端在jsapi 支付時會要求返回signature 參數  此參數即為此准備
    ];
    public function index($param,$openid="")
    {
        $order = [
            'out_trade_no'  => $param['out_trade_no'],// 訂單號
            'total_fee'     => intval($param['total_fee']*100),// 訂單金額  以(分)為單位
            'body'          => $param['body'],// 商品描述
            'notify_url'    => $param['notify_url'], //回調地址
            'spbill_create_ip' => $param['spbill_create_ip'], //對應IP
            'trade_type'    => $param['trade_type']  //對應支付類型
        ];

        #當支付類型為JAPI時  openid  必傳
        if($param['trade_type'] == "JSAPI")
        {
            $order['openid'] = $openid;
        }

        #統一下單 獲取prepay_id
        $unified_order=$this->unifiedOrder($order);

        #獲取當前時間戳
        $time = time();

        #JSAPI
        if($param['trade_type'] == 'JSAPI')
        {
            #組合jssdk需要用到的數據
            $data = [
                'appId'     => $this->config['appid'], //appid
                'timeStamp' => strval($time), //時間戳
                'nonceStr'  =>$unified_order['nonce_str'],// 隨機字符串
                'package'   => 'prepay_id='.$unified_order['prepay_id'],// 預支付交易會話標識
                'signType'  => 'MD5'      //加密方式
            ];
            // 生成簽名
            $data['paySign']=$this->makeSign($data);
            // #有的可能會有需求signature 此參數的在此加密一下即可
            // $token = $this->config['token'];
            // $tmpArr = array($token, strval($time),$unified_order['nonce_str']);
            // sort($tmpArr, SORT_STRING);
            // $tmpStr = implode( $tmpArr );
            // $tmpStr = sha1($tmpStr);
            // $data['signature'] = $tmpStr;
        }
        #APP
        elseif($param['trade_type'] == 'APP')
        {
            $data  = [
                'appid'     => $this->config['appid'],
                'partnerid' => $this->config['mch_id'],
                'prepayid'  => $unified_order['prepay_id'],
                'package'   => 'Sign=WXPay',
                'noncestr'  => $unified_order['nonce_str'],// 隨機字符串
                'timestamp' => strval($time), //時間戳
            ];
            //生成簽名
            $data['sign'] =  $this->makeSign($data);
        }
        return $data;
    }
    /**
     * 統一下單
     * @param  array $order 訂單 必須包含支付所需要的參數 body(產品描述)、total_fee(訂單金額)、out_trade_no(訂單號)、product_id(產品id)、trade_type(類型:JSAPI,NATIVE,APP)
     */
    public function unifiedOrder($order)
    {
        $config =[
            'appid'     => $this->config['appid'], //appid
            'mch_id'    => $this->config['mch_id'], //商戶號ID
            'nonce_str' => $this->getNonceStr()
        ];

        # 合並配置數據和訂單數據
        $data=array_merge($order,$config);

        # 生成簽名
        $sign=$this->makeSign($data);
        $data['sign']=$sign;
        #轉換成xml
        $xml=$this->toXml($data);
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';  //接收xml數據的文件
        $header[] = "Content-type: text/xml";      //定義content-type為xml,注意是數組
        $ch = curl_init ($url);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 兼容本地沒有指定curl.cainfo路徑的錯誤
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        $response = curl_exec($ch);
        if(curl_errno($ch)){
            # 顯示報錯信息;終止繼續執行
            die(curl_error($ch));
        }
        curl_close($ch);

        #轉換成數組
        $result=$this->toArray($response);

        #顯示錯誤信息
        if ($result['return_code']=='FAIL') 
        {
            die($result['return_msg']);
        }

        $result['sign']=$sign;
        $result['nonce_str']=$this->getNonceStr();
        return $result;
    }
    /**
     * 生成簽名
     * @return 簽名,本函數不覆蓋sign成員變量,如要設置簽名需要調用SetSign方法賦值
     */
    public function makeSign($data)
    {
        # 去空
        $data=array_filter($data);
        #簽名步驟一:按字典序排序參數
        ksort($data);
        #將數組轉成url形式
        $string_a=http_build_query($data);
        $string_a=urldecode($string_a);
        #簽名步驟二:在string后加入KEY
        $string_sign_temp=$string_a."&key=".$this->config['key'];
        #簽名步驟三:MD5加密
        $sign = md5($string_sign_temp);
        # 簽名步驟四:所有字符轉為大寫
        $result=strtoupper($sign);
        return $result;
    }

    /**
     * 將xml轉為array
     * @param  string $xml xml字符串
     * @return array       轉換得到的數組
     */
    public function toArray($xml){   
        #禁止引用外部xml實體
        libxml_disable_entity_loader(true);
        $result= json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);        
        return $result;
    }

    /**
     * 
     * 產生隨機字符串,不長於32位
     * @param int $length
     * @return 產生的隨機字符串
     */
    public function getNonceStr($length = 32) 
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";  
        $str ="";
        for ( $i = 0; $i < $length; $i++ )  
        {  
            $str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);  
        } 
        return $str;
    }

    /**
     * 輸出xml字符
     * @throws WxPayException
    **/
    public function toXml($data)
    {
        if(!is_array($data) || count($data) <= 0)
        {
            throw new WxPayException("數組數據異常!");
        }
        $xml = "<xml>";
        foreach ($data as $key=>$val){
            if (is_numeric($val)){
                $xml.="<".$key.">".$val."</".$key.">";
            }else{
                $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
            }
        }
        $xml.="</xml>";
        return $xml; 
    }

    /**
     * 驗證
     * @return array 返回數組格式的notify數據
     */
    public function notify()
    {
        // 獲取xml
        $xml=file_get_contents('php://input', 'r'); 
        # 轉成php數組
        $data=$this->toArray($xml);
        # 保存原sign
        $data_sign=$data['sign'];
        # sign不參與簽名
        unset($data['sign']);
        $sign=$this->makeSign($data);
        # 判斷簽名是否正確  判斷支付狀態
        if ($sign===$data_sign && $data['return_code']=='SUCCESS' && $data['result_code']=='SUCCESS') 
        {
            $result=$data;
        }else{
            $result=false;
        }

        # 返回狀態給微信服務器
        if ($result) 
        {
            $str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
        }else{
            $str='<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[簽名失敗]]></return_msg></xml>';
        }
        return $result;
    }
}
?>

 

下面就是調用方法超級簡單:

<?php
namespace app\index\controller;
header("Content-Type: text/html;charset=utf-8");

use think\Controller;
use pay\Wxpay;

class Buy extends Controller
{
    #新的微信支付類調用
    public function newpay()
    {
        $pay_sn = date('YmdHis').rand(1000,9999);
        $total_fee = '0.01';
        $body   = "商品描述";
        $spbill_create_ip = '192.168.0.1';
        $notify_url = "你的回調地址";  #根據不同類型回調地址不同
        $trade_type = 'APP';
        #JSAPI--JSAPI支付(或小程序支付)、
        #NATIVE--Native支付、
        #APP--app支付,
        #MWEB--H5支付,
        #不同trade_type決定了調起支付的方式,請根據支付產品正確上傳
       

        #新的需要參數為六個
        # out_trade_no 商戶訂單號
        # total_fee    訂單總額
        # body         商品描述
        # spbill_create_ip   終端IP
        # notify_url   回調通知地址
        # trade_type   交易類型
        
        $wxpay  = new Wxpay();
        $date = [
            'out_trade_no'  => $pay_sn,
            'total_fee'     => $total_fee,
            'body'          => $body,
            'spbill_create_ip' => getIp(),
            'notify_url' => $notify_url,
            'trade_type' => $trade_type
        ];
        #根據 trade_type  類型不同,是否傳遞openid
    
        #APP   類型支付調用
        $res = $wxpay->index($date);

        #JSAPI 類型支付調用
        $res = $wxpay->index($date,$openid);

        #獲得后將對應內容返回前端即可
        return $res;
    }

}
?>

 

最后就是回調咯:

<?php
namespace app\index\controller;
header("Content-Type: text/html;charset=utf-8");

use think\Controller;
use pay\Wxpay;

class Pays extends Controller
{
    public function notify()
    {
        $wxpay  = new Wxpay();
        $result = $wxpay->notify();

        #根據拿到的數據 來進行自己的數據邏輯
        if($result)
        {
            $out_trade_no = $result['out_trade_no'];
            echo "success";exit;
        }
        echo  "error";
    }
}
?>

 

以上就是本次整合的微信支付咯 

感謝各位大大的觀看。

2020年4月17日

 


免責聲明!

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



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