zoukankan      html  css  js  c++  java
  • 支付封装合集(微信)1【更新至2020.11.17】

    最近几日对于h5的支付做了调试,发现调用其实非常简单,这里提示几点在调试过程中出现的错误

    1 config中的 key 是微信商户的key ,填写的时候需要注意。如果填写的是微信公众号的key,在其他传递的参数都是正确的情况下,爆出的是签名错误。

    2 前端调用如果是h5页面,使用 window.location.href = 返回的链接 直接做链接跳转 ,后可加 redirect_url 参数,指定在支付成功后的跳转页面。但是加参数的跳转链接 需要使用 encodeURI 做处理,如图

     3 如果是前后端分离,前端使用的是vue的话  建议前端在支付部分直接做 完全性的 h5页面处理  进行链接跳转,请勿在页面中使用任何有关vue的东西,这是比较简单的针对vue 微信h5支付 做处理的方式

      至于vue直接处理微信h5支付 前端尝试未通过,有兴趣的可以联合前端查看处理下,也欢迎反馈问题,这里会非常感谢,并标明提供的相关信息。

    以上就是微信h5支付在处理过程中需要注意的几点,而相对于支付宝的h5,虽然同微信h5都是直接根据返回的链接做跳转 但是不需要做什么麻烦的处理 相对来说还是比较方便的

    之前在csdn 上,为了方便修改更新现在移动到这里,那边的会直接删除

    从工作至今四年 终于将自己封装的写个差不多了 其中有借鉴各种支付方式 可有雷同 但一些内容为自己整理封装后的效果 希望多多提议

    目前测试通过无误的有 app wx 其他间隔时间过长 不太确定 会在之后不断调整 力求完美

    <?php
    namespace pay;
    /**
     * 微信支付
     */
    class  Wxpay
    {
        // 需了解一个应用对应一个appid 需要多个共存时就需要多个设置
        private $config = [
            'app_appid'=>'*********',
            'wx_appid'=>'******',
        
            'mobile_appid'=>'*****',
            'mch_id'=>'*********',
            'key'=>'******',
        ];
        // 设置证书 退款时需要
        private $cert=[
            'apiclient_cert'=>'',
            'apiclient_key'=>'',
        ];
        private $appid='';
        private $calltype = 'app';//调用端口 app wx mobile  pc wx_small
        private $unifiedorder = 'https://api.mch.weixin.qq.com/pay/unifiedorder';//微信支付接口
        private $refund = 'https://api.mch.weixin.qq.com/secapi/pay/refund';//微信退款提交接口
        private $refundquery = 'https://api.mch.weixin.qq.com/pay/refundquery';//微信退款查询接口
        public function __construct($payTypeInfo=[]){
            if($payTypeInfo)
            {
                foreach ($payTypeInfo as $k => $val) {
                    if($k=='calltype')
                    {
                        $this->calltype=$val['val']!=''?$val['val']:$this->calltype;
                    }else{
                        $this->config[$k]=$val['val'];
                    }
                } 
            }
            $appname=$this->calltype.'_appid';
            $this->appid=$this->config[$appname];
            // 线上 linux
            // $this->cert['apiclient_cert']=EXTEND_PATH.'pay/wxcert/apiclient_cert.pem';
            // $this->cert['apiclient_key']=EXTEND_PATH.'pay/wxcert/apiclient_key.pem';
            
            // 本地
           // $this->cert['apiclient_cert']=EXTEND_PATH.'paywxcertapiclient_cert.pem';
           // $this->cert['apiclient_key']=EXTEND_PATH.'paywxcertapiclient_key.pem';
        }
        public function index($data)
        {
            // 端口检测
            $res=$this->calltypeCheck($data);
            if($res['code']==0){ return $res; }
            // 初次提交进行数据判断
            // FLog($data);
            $res=$this->wxresult($this->postdata($data),$this->config['key']);
            FLog($res);
            if($res['return_code']=='FAIL')
            {
                return ['code'=>0,'msg'=>$res['return_msg']];
            }
            if($res['result_code']=='FAIL')
            {
                return ['code'=>0,'msg'=>$res['err_code'].' '.$res['err_code_des']];
            }
            $calltype=$this->calltype;
            return $this->$calltype($data,$res);
        }
        // 微信公众号
        public function wx($data,$res)
        {
            $newjsdata=[
                'appId'=>$this->appid,
                'nonceStr'=>'"'.rand(1000,9999).'"',
                'package'=>"prepay_id=".$res['prepay_id'],//
                'signType'=>"MD5",
                'timeStamp'=>'"'.time().'"',
                'paySign'=>'',
            ];
            $newjsdata=getsign($newjsdata,$this->config['key'],'paySign','key','1');
            return ['code'=>1,'msg'=>'请支付','data'=>['str'=>$newjsdata]];
        }
        // 微信小程序
        public function wx_small($data,$res)
        {
            $nonceStr=(string)rand(1000,9999);
            $timeStamp=(string)time();
            $newjsdata=[
                'appId'=>$this->appid,
                'nonceStr'=>$nonceStr,
                'package'=>"prepay_id=".$res['prepay_id'],//
                'signType'=>"MD5",
                'timeStamp'=>$timeStamp,
                'paySign'=>'',
            ];
    
            $newjsdata=getsign($newjsdata,$this->config['key'],'paySign','key',1);
    
            $returndata['time']=$timeStamp;
            $returndata['order']=$newjsdata;
    
            return ['code'=>1,'msg'=>'请支付','data'=>$returndata];
        }
        // app支付
        public function app($data,$res)
        {  
            $newdata=[
                'appid'=>$res['appid'],
                'partnerid'=>$res['mch_id'],
                'prepayid'=>$res['prepay_id'],
                'package'=>"Sign=WXPay",//
                'noncestr'=>rand(1000,9999),
                'timestamp'=>time(),
            ];
            $newdata=getsign($newdata,$this->config['key'],'sign','key','1');
            // 外壳app的操作
            // $buff = "";
            // foreach ($newdata as $k => $v) {
            //     if($v != "" && !is_array($v)){
            //         $buff .= $k . "=" . $v . "&";
            //     }
            // }
            // $buff = trim($buff, "&");
            // $url='http://www.ruidao888.com/home/order/index.html?type=wxpay&'.$buff;
            return ['code'=>1,'msg'=>'请支付','data'=>['type'=>'app','str'=>$newdata]];
        }
    
        public function mobile($data,$res)
        {
            //$web_link='http://pnceshi.zzyuyou.com/web/index/app_download';
            //$url=$res['mweb_url'].'&redirect_url='.urlencode($web_link);
        //以上两行为测试支付成功后指定返回的页面,一般写在前端比较好,这里只做事例查看
            $url=$res['mweb_url'];
            return ['code'=>1,'msg'=>'请支付','data'=>['type'=>'app','url'=>$url]];
        }
        
        public function postdata($data)
        {
            $trade_type=$this->calltypeD('trade_type');
            $postdata=[
                'appid'=>$this->appid,
                'mch_id'=>$this->config['mch_id'],
                'nonce_str'=>rand(10000,99999),
                'body'=>$data['subject'],
                'attach'=>$data['attach'],//对什么表进行操作
                'out_trade_no'=>$data['pay_sn'],
                'total_fee'=>$data['total']*100,
                'spbill_create_ip'=>request()->ip(),
                'notify_url'=>$data['notify_url'],
                'trade_type'=>$trade_type,//
            ];
            
            $postdata=$this->ReArr_postdata($postdata,$data);
            // dump($postdata);
            // die;
            return $postdata;
        }
        // 重组数据
        public function ReArr_postdata($postdata,$data)
        {
            if($this->calltype=='wx' || $this->calltype=='wx_small')
            {
                /*微信公众号小程序必须*/
                $postdata['openid']=$data['openid'];
            }else if($this->calltype=='app')
            {
                // 正常字段就行
            }else if($this->calltype=='mobile')
            {
                $ccD=cc('web_link,web_name');
                $scene_info=[
                    'type'=>'Wap',//场景类型
                    'wap_url'=>$ccD['web_link'],//WAP网站URL地址
                    'wap_name'=>$ccD['web_name'],
                ];
            //好像之前跟现在的不太一样 不过对号入座,改成这个也是没有问题的
                $h5_info=[
                    'h5_info'=>$scene_info
                ];
                $postdata['scene_info']=json_encode($h5_info);
            }else if($this->calltype=='jsapi')
            {
                $ccD=cc('web_link,web_name');
                $scene_info=[
                    'type'=>'Wap',//场景类型
                    'wap_url'=>$ccD['web_link'],//WAP网站URL地址
                    'wap_name'=>$ccD['web_name'],
                ];
                $postdata['scene_info']=json_encode($scene_info);
            }
            $postdata['body']=trim($postdata['body']);
            $postdata['total_fee']=(int)$postdata['total_fee'];
            return $postdata;
        }
        // 根据类型获取想要的信息
        public function calltypeD($field)
        {
            /**
             * JSAPI--JSAPI支付(或小程序支付) 测试通过
             * NATIVE--Native支付
             * APP--app支付   测试通过
             * MWEB--H5支付
             * MICROPAY--付款码支付
             */
            $D=[
                'wx'=>['trade_type'=>'JSAPI'],
                'wx_small'=>['trade_type'=>'JSAPI'],//小程序
                'app'=>['trade_type'=>'APP'],
                'mobile'=>['trade_type'=>'MWEB'],
            ];
            return isset($D[$this->calltype])?$D[$this->calltype][$field]:'';
        }
        // 端口检测
        public function calltypeCheck($data)
        {
            // 对配置信息进行检测
            if($this->appid=='') { return ['code'=>0,'msg'=>'请先配置 appid']; }
            if($this->config['mch_id']=='') { return ['code'=>0,'msg'=>'请先配置 mch_id']; }
            if($this->config['key']=='') { return ['code'=>0,'msg'=>'请先配置 key']; }
            
            if($this->calltype=='wx' && (!isset($data['openid']) || $data['openid']==''))
            {
                return ['code'=>0,'msg'=>'缺少微信openid,请先获取'];
            }
            return ['code'=>1,'msg'=>'检测通过'];
        }
        // 退款申请并查询
        public function refund_and_query($data)
        {
            $res=$this->refund($data);
            if($res['code']==0)
            {
                return $res;
            }
            $res2=$this->refundquery($res['refund_data']);
            return $res2;
        }
        // 退款申请
        public function refund($data)
        {
            $out_refund_no = date('YmdHis').rand(1000, 9999);//订单号每次随机
            $postdata=[
                'appid'=>$this->appid,
                'mch_id'=>$this->config['mch_id'],
                'nonce_str'=>rand(10000,99999),
                'out_trade_no'=>$data['out_trade_no'],
                'out_refund_no'=>$out_refund_no,
                'total_fee'=>$data['total_fee']*100,
                'refund_fee'=>$data['refund_fee']*100,
            ];
            $res=$this->wxresult_refund($postdata,$this->config['key'],$this->cert);
            // echo var_dump($res);
            // die;
            if($res['return_code']=='FAIL')
            {
                return ['code'=>0,'msg'=>$res['return_msg']];
            }
            if($res['result_code']=='FAIL')
            {
                return ['code'=>0,'msg'=>$res['err_code'].' '.$res['err_code_des']];
            }
            return ['code'=>1,'msg'=>'申请成功','refund_data'=>$res];
        }
        // 退款查询
        public function refundquery($data)
        {
            // 查询是否退款成功
            $s_postdata=[
                'appid'=>$this->appid,
                'mch_id'=>$this->config['mch_id'],
                'nonce_str'=>rand(10000,99999),
                'sign_type'=>'MD5',
                'refund_id'=>$data['refund_id'],//退款单号
            ];
            $res2=$this->wxresult_refund($s_postdata,$this->config['key'],$this->cert,$this->refundquery);
            if($res2['return_code']=='FAIL')
            {
                return ['code'=>0,'msg'=>$res2['return_msg']];
            }
            if($res2['result_code']=='FAIL')
            {
                return ['code'=>0,'msg'=>$res2['err_code'].' '.$res2['err_code_des']];
            }
            return ['code'=>1,'msg'=>'修改成功','refund_data'=>$res2];
        }
        private function wxresult_refund($data,$key,$cert,$url='') {
            $url=$url==''?$this->refund:$url;
            return postXmlSSLCurl(arrayToXml(getsign($data,$key)),$cert,$url); 
        }
        private function wxresult($data,$key) {
            return $this->wxCur(arrayToXml(getsign($data,$key))); 
        }
        private function wxCur($data,$url="") {
            $url=$url==''?$this->unifiedorder:$url;
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL,$url);
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
            $response=curl_exec($ch);
            if($response)
            {
               curl_close($ch); 
               return json_decode(json_encode(simplexml_load_string($response, 'simplexmlelement', LIBXML_NOCDATA)),true);
            }else{
                $error = curl_errno($ch);
                // echo "curl出错,错误码:$error"."<br>"; 
                // echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
                curl_close($ch);
                return ['return_code'=>'FAIL','return_msg'=>"curl出错,错误码:$error"];
            }
        }
    }
    
    
    
    function postXmlSSLCurl($xml,$cert,$url,$second=30)
    {
        // echo '2222222222222';
        // dump($xml);
        // die;
        
        $ch = curl_init();
        //超时时间
        curl_setopt($ch,CURLOPT_TIMEOUT,$second);
        //这里设置代理,如果有的话
        //curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
        //curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
        curl_setopt($ch,CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
        //设置header
        curl_setopt($ch,CURLOPT_HEADER,FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch,CURLOPT_RETURNTRANSFER,TRUE);
        //设置证书
        //使用证书:cert 与 key 分别属于两个.pem文件
        //默认格式为PEM,可以注释
        curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
        curl_setopt($ch,CURLOPT_SSLCERT, $cert['apiclient_cert']);
        //默认格式为PEM,可以注释
        curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
        curl_setopt($ch,CURLOPT_SSLKEY, $cert['apiclient_key']);
        //post提交方式
        curl_setopt($ch,CURLOPT_POST, true);
        curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
        $response=curl_exec($ch);
        if($response)
        {
           curl_close($ch); 
           return json_decode(json_encode(simplexml_load_string($response, 'simplexmlelement', LIBXML_NOCDATA)),true);
        }else{
            $error = curl_errno($ch);
            // echo "curl出错,错误码:$error"."<br>"; 
            // echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
            curl_close($ch);
            return ['return_code'=>'FAIL','return_msg'=>"curl出错,错误码:$error"];
        }
    }
    function getsign($data,$key,$newkey='sign',$linkstr='key',$ksorts=1) {
        if($ksorts==1){
            ksort($data);
        }
        $buff = "";
        foreach ($data as $k => $v) {
            if($k != $newkey && $v != "" && !is_array($v)){
                $buff .= $k . "=" . $v . "&";
            }
        }
        $buff = trim($buff, "&");
        $buff=$buff."&".$linkstr."=".$key;
        $sign=strtoupper(MD5($buff));
        $data[$newkey]=$sign;
        return $data;
    }
    
    // 签名验证   对比
    // function makeSign($data)
    // {
    //     $config_key='z4YoADhVnknugpG8BN2S4bvJZIV9s7QC';
    //     // 去空
    //     $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=".$config_key;
    //     //签名步骤三:MD5加密
    //     $sign = md5($string_sign_temp);
    //     // 签名步骤四:所有字符转为大写
    //     $result=strtoupper($sign);
    //     return $result;
    // }
    
    
    function arrayToXml($arr) {
        // dump($arr);
        // die;
        $xml = "<xml>";
        foreach ($arr as $key=>$val)
        {
            $xml.="<".$key.">".$val."</".$key.">";
        }
        $xml.="</xml>";
        return $xml;
    }     
  • 相关阅读:
    我说AOP(面向切面编程)--藏在苹果里的五角星
    mysql workbench 一个‘愚蠢’的设计
    .Net MVC Json 日期格式
    es6 import
    asp.net mvc 模型绑定太糙淡了
    asp.net mvc 报错 CS1617: Invalid option ‘6’ for /langversion; must be ISO-1, ISO-2, 3, 4, 5 or Default
    撸代码时到底用var好还是强类型变量好
    iphone5 从ios7升级到最新9.2
    修复win7 只有IE64 能上网 其他浏览器及应用都无法联网
    使用Teleri 导出实体类数组到Excel
  • 原文地址:https://www.cnblogs.com/exo5/p/13992669.html
Copyright © 2011-2022 走看看