tp5 -- 微信公众号支付

近来期间比较忙, 忙完之后发现最近有挺多的东西没有整理,于是乎。就将以前用到的一些小东西整理了一下。

如果您需要前后端分离的JASPI或者APP支付 请查看:TP5 -- 微信支付整合(APP,JSAPI)

如果对您有帮助,则是我最大的幸运。

本篇主要是说了一下整合TP5的微信公众号支付。

不过由于最近TP6已经出了,小伙伴们要记得向最新的进发哦。

好了,废话不多说了。开始。

首先呢,需要引入我们封装好的类库:

同样在 extend/ 下

因为会将支付类库放在一起,于是就在extend 文件夹下创建了一个pay文件夹用来存储所有类文件。

以下内容为wxpay类的内容:

namespace pay;

class Wxpay
{
    private $config =[
        "appid"      => "********",    //   公众号ID
        "mch_id"     => "********",    //   商户号
        "notify_url" => "********",    //   回调地址
        "key"        => "********",    //   微信支付 商户秘钥KEY
    ];
    public function index($param,$openid)
    {
        $order=array(
            'body'          => $param['body'],// 商品描述
            'total_fee'     => intval($param['total']*100),// 订单金额  以(分)为单位
            // 'total_fee'     => 1, 
            'out_trade_no'  => $param['order_sn'],// 订单号
            'trade_type'    => 'JSAPI',// JSAPI公众号支付
            'openid'        => $openid// 获取到的openid
        );
        $userip = $param['userip'];
        // 统一下单 获取prepay_id
        $unified_order=$this->unifiedOrder($order,$userip);
        // 获取当前时间戳
        $time=time();
        // 组合jssdk需要用到的数据
        $data=array(
            '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);
        return $data;
    }
    /**
     * 统一下单
     * @param  array $order 订单 必须包含支付所需要的参数 body(产品描述)、total_fee(订单金额)、out_trade_no(订单号)、product_id(产品id)、trade_type(类型:JSAPI,NATIVE,APP)
     */
    public function unifiedOrder($order,$userip)
    {
        $config=array(
            'appid'             => $this->config['appid'], //appid
            'mch_id'            => $this->config['mch_id'], //商户号ID
            'nonce_str'         => $this->getNonceStr(),
            'spbill_create_ip'  => $userip,  //你的IP
            'notify_url'        => $this->config['notify_url'],
            //'notify_url'        =>$url
            );
        // 合并配置数据和订单数据
        $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);
        //dump($result);
        // 显示错误信息
        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>';
        }
        // file_put_contents("./Public/aaa.text",$result);
        // echo $str;
        return $result;
    }

    public function https_request($url, $data = null)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        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);
         // var_dump($output);
        curl_close($curl);
        return $output;
    }

}

然后就是调用方法了:

namespace app\index\controller;

use pay\Wxpay;

class Index extends Base
{
    /**
     *调用支付接口
     *
     */
    public function getorder()
    {

        $param = input('get.');
        $pays   = model('pay')->where('id',$id)->find();
        $wxpay = [
            'order_sn' => $pays['pay_sn'],   //支付订单号
            'total'    => $pays['money'],    //支付总额
            'body'     => $pays['body'],     //支付说明
            'userip'   => $pays['userip'],   //用户IP
        ];

        $openid = $this->_user['openid'];

        $pay = new Wxpay();
        $data = $pay->index($wxpay,$openid);

        $logUrl = "********"; //支付完成跳转地址
 
        $this->assign([
                'data'=>json_encode($data),
                'logUrl'=>$logUrl,
            ]);
        return $this->fetch();
    }
    
    
    
}

其次是对应的调用空白页面:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>订单提交成功</title>

    <style>
        .info{
            text-align: center;
            margin-top: 40px;
        }
    </style>
</head>

<body>
<div class="info">支付进行中,请稍候...</div>
<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script type="text/javascript">
        //调用微信JS api 支付
        function onBridgeReady(){
            var data = {$data};
                WeixinJSBridge.invoke(
                      'getBrandWCPayRequest', data, 
                    function(res){
                        if(res.err_msg == "get_brand_wcpay_request:ok" ) {
                            // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。
                            window.location.href="{$logUrl}"; 
                        }else{
                           alert('支付失败');
                           // alert(res.err_code+res.err_desc+res.err_msg); // 显示错误信息 
                        }
                    }
                );
            }
             
             if (typeof WeixinJSBridge == "undefined"){
                 if( document.addEventListener ){
                     document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
                 }else if (document.attachEvent){
                     document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
                     document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
                 }
             }else{
                  onBridgeReady();
             }
</script>


</body>
</html>

最后就是回调了(拿到回调在此控制器汇中进行数据库操作):

namespace app\index\controller;

use Think\Controller;
/** * 支付回调 -- 接收并处理调整数据库 */ class ClassName extends AnotherClass { private $config =[ "key" => "*******", //微信支付 商户秘钥KEY ]; /** * notify_url接收页面 */ public function wxpaynotify() { $result=$this->notify(); if ($result) {//以下为处理数据库操作等 操作完成后输出success echo "SUCCESS"; } } /** * 一下验证类 活获取 token 等可写入至 common 公告类中 * 验证 * @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>'; } // file_put_contents("./Public/aaa.text",$result); // echo $str; 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; } /** * 生成签名 * @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; } }

以上就是微信公众号支付的全部内容。

如有疑问,请评论或留言。

感谢您的查看。

2019年05月31日

 

posted @ 2019-05-31 16:17  御风琊穹  阅读(2394)  评论(0编辑  收藏  举报

春风十里,我喜欢你。可是你喜欢的人不喜欢你,哈哈....
人来人往,莫失莫忘。最终还是选择淡忘于回忆之中...
落日余晖,待你而归。但你终究不是为我而归..
一米阳光,温暖安放;心若向阳,无谓悲伤;轻安喜乐,次第花开 。