• 微信支付


    每一个关于支付的文章,都是一篇血泪史,不知到为什么每次接接口总会遇到点问题。还好都走过来了

    1.支付测试 (需要完成两步)

    a.支付调用

    这个微信的demo中有一个测试的例子,直接拿过来用就可以了,将里面的参数换成自己真实的参数,这里我遇到的问题就是,appkey = appSignKey  ,微信产品经理没有发给我们,微信后台又找不到,所以耽搁了一些时间。

    注意事项:

              1.一定要将测试代码放在测试目录的一级目录下进行测试 。如,测试目录是www.xxxx.com/pay/ ,那你的代码文件wxtest.php 要放在 www.xxxx.com/pay/wxtest.php

              2.一定要在微信中测试,在网页上测试,点击按钮是没有反映的。将www.xxxx.com/pay/wxtest.php,链接到微信中。

              3.一定要将测试的微信帐号加入白名单(确定自己已有微信支付)

    代码如下:

    <!DOCTYPE html>
    <html>
        <head>
            <title>公众号支付测试网页</title>
            <script language="javascript" src="http://xxx/website/weixin/Paywx/js/jquery.js"></script>
            <script  language="javascript" src="http://xxx/website/weixin/Paywx/js/lazyloadv3.js"></script>
            <script language="javascript" src="http://xxx/website/weixin/Paywx/js/md5.js"></script>
            <script language="javascript" src="http://xxx/website/weixin/Paywx/js/sha1.js"></script>
            <script Language="javascript">
    
    		
                //商家测试请修改此四个参数,并将页面放在支付授权目录下,在申请了支付的公众账号访问此页面,方可调起支付。
                //修改开始
                function getPartnerId()
                {//替换partnerid
                    return "xxxxx";
                }
                
                function getPartnerKey()
                {//替换partnerkey
                    return "xxxxx";
                }
    			
                function getAppId()
                {//替换appid
                    return "xxxxx";
                }
                
                function getAppKey()
                {//替换appkey
                 return "xxxxx";
                }		
    			//修改到此结束
    		
                //辅助函数
                function Trim(str,is_global)
                {
                    var result;
                    result = str.replace(/(^s+)|(s+$)/g,"");
                    if(is_global.toLowerCase()=="g") result = result.replace(/s/g,"");
                    return result;
                }
                function clearBr(key)
                {
                    key = Trim(key,"g");
                    key = key.replace(/</?.+?>/g,"");
                    key = key.replace(/[
    ]/g, "");
                    return key;
                }
                
                //获取随机数
                function getANumber()
                {
                    var date = new Date();
                    var times1970 = date.getTime();
                    var times = date.getDate() + "" + date.getHours() + "" + date.getMinutes() + "" + date.getSeconds();
                    var encrypt = times * times1970;
                    if(arguments.length == 1){
                        return arguments[0] + encrypt;
                    }else{
                        return encrypt;
                    }
                    
                }
    
                //以下是package组包过程:
                
                var oldPackageString;//记住package,方便最后进行整体签名时取用
               
                function getPackage()
                {
                    var banktype = "WX";
                    var body = "江诗丹顿";//商品名称信息,这里由测试网页填入。
                    var fee_type = "1";//费用类型,这里1为默认的人民币
                    var input_charset = "UTF-8";//字符集,可以使用GBK或者UTF-8
                    var notify_url = "http://www.qq.com";//支付成功后将通知该地址
                    var out_trade_no = ""+getANumber();//订单号,商户需要保证该字段对于本商户的唯一性
                    var partner = getPartnerId();//测试商户号
                    var spbill_create_ip = "127.0.0.1";//用户浏览器的ip,这个需要在前端获取。这里使用127.0.0.1测试值
                    var total_fee = 1;//总金额。
                    var partnerKey = getPartnerKey();//这个值和以上其他值不一样是:签名需要它,而最后组成的传输字符串不能含有它。这个key是需要商户好好保存的。
                    
                    //首先第一步:对原串进行签名,注意这里不要对任何字段进行编码。这里是将参数按照key=value进行字典排序后组成下面的字符串,在这个字符串最后拼接上key=XXXX。由于这里的字段固定,因此只需要按照这个顺序进行排序即可。
                    var signString = "bank_type="+banktype+"&body="+body+"&fee_type="+fee_type+"&input_charset="+input_charset+"&notify_url="+notify_url+"&out_trade_no="+out_trade_no+"&partner="+partner+"&spbill_create_ip="+spbill_create_ip+"&total_fee="+total_fee+"&key="+partnerKey;
                    
                    var md5SignValue =  ("" + CryptoJS.MD5(signString)).toUpperCase();
                    //然后第二步,对每个参数进行url转码,如果您的程序是用js,那么需要使用encodeURIComponent函数进行编码。
                    
                    
                    banktype = encodeURIComponent(banktype);
                    body=encodeURIComponent(body);
                    fee_type=encodeURIComponent(fee_type);
                    input_charset = encodeURIComponent(input_charset);
                    notify_url = encodeURIComponent(notify_url);
                    out_trade_no = encodeURIComponent(out_trade_no);
                    partner = encodeURIComponent(partner);
                    spbill_create_ip = encodeURIComponent(spbill_create_ip);
                    total_fee = encodeURIComponent(total_fee);
                    
                    //然后进行最后一步,这里按照key=value除了sign外进行字典序排序后组成下列的字符串,最后再串接sign=value
                    var completeString = "bank_type="+banktype+"&body="+body+"&fee_type="+fee_type+"&input_charset="+input_charset+"&notify_url="+notify_url+"&out_trade_no="+out_trade_no+"&partner="+partner+"&spbill_create_ip="+spbill_create_ip+"&total_fee="+total_fee;
                    completeString = completeString + "&sign="+md5SignValue;
                    
                    
                    oldPackageString = completeString;//记住package,方便最后进行整体签名时取用
                    
                    return completeString;
                }
                
                
                //下面是app进行签名的操作:
                
                var oldTimeStamp ;//记住timestamp,避免签名时的timestamp与传入的timestamp时不一致
                var oldNonceStr ; //记住nonceStr,避免签名时的nonceStr与传入的nonceStr不一致
                         
                function getTimeStamp()
                {
                    var timestamp=new Date().getTime();
                    var timestampstring = timestamp.toString();//一定要转换字符串
                    oldTimeStamp = timestampstring;
                    return timestampstring;
                }
                
                function getNonceStr()
                {
                    var $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
                    var maxPos = $chars.length;
                    var noceStr = "";
                    for (i = 0; i < 32; i++) {
                        noceStr += $chars.charAt(Math.floor(Math.random() * maxPos));
                    }
                    oldNonceStr = noceStr;
                    return noceStr;
                }
                
                function getSignType()
                {
                    return "SHA1";
                }
                
                function getSign()
                {
                    var app_id = getAppId().toString();
                    var app_key = getAppKey().toString();
                    var nonce_str = oldNonceStr;
                    var package_string = oldPackageString;
                    var time_stamp = oldTimeStamp;
                    //第一步,对所有需要传入的参数加上appkey作一次key=value字典序的排序
                    var keyvaluestring = "appid="+app_id+"&appkey="+app_key+"&noncestr="+nonce_str+"&package="+package_string+"&timestamp="+time_stamp;
                    sign = CryptoJS.SHA1(keyvaluestring).toString();
                    return sign;
                }
                
                
                
                
                </script>
            <meta http-equiv="content-type" content="text/html;charset=utf-8"/>
            <!--<meta id="viewport" name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1; user-scalable=no;" />-->
            
            <style>
                
                
                body { margin:0;padding:0;background:#eae9e6; }
                body,p,table,td,th { font-size:14px;font-family:helvetica,Arial,Tahoma; }
                h1 { font-family:Baskerville,HelveticaNeue-Bold,helvetica,Arial,Tahoma; }
                a { text-decoration:none;color:#385487;}
                
                
                .container {  }
                .title { }
                #content {padding:30px 20px 20px;color:#111;box-shadow:0 1px 4px #ccc; background:#f7f2ed;  }
                .seeAlso { padding:15px 20px 30px; }
                
                .headpic div { margin:20px 0 0;}
                .headpic img { display:block;}
                
                .title h1 { font-size:22px;font-weight:bold;padding:0;margin:0;line-height:1.2;color:#1f1f1f; }
                .title p { color:#aaa;font-size:12px;margin:5px 0 0;padding:0;font-weight:bold;}
                .pic { margin:20px 0; }
                .articlecontent img { display:block;clear:both;box-shadow:0px 1px 3px #999; margin:5px auto;}
                .articlecontent p { text-indent: 2em; font-family:Georgia,helvetica,Arial,Tahoma;line-height:1.4; font-size:16px; margin:20px 0;  }
                
                
                .seeAlso h3 { font-size:16px;color:#a5a5a5;}
                .seeAlso ul { margin:0;padding:0; }
                .seeAlso li {  font-size:16px;list-style-type:none;border-top:1px solid #ccc;padding:2px 0;}
                .seeAlso li a { border-bottom:none;display:block;line-height:1.1; padding:13px 0; }
                
                .clr{ clear:both;height:1px;overflow:hidden;}
                
                
                .fontSize1 .title h1 { font-size:20px; }
                .fontSize1 .articlecontent p {  font-size:14px; }
                .fontSize1 .weibo .nickname,.fontSize1 .weibo .comment  { font-size:11px; }
                .fontSize1 .moreOperator { font-size:14px; }
                
                .fontSize2 .title h1 { font-size:22px; }
                .fontSize2 .articlecontent p {  font-size:16px; }
                .fontSize2 .weibo .nickname,.fontSize2 .weibo .comment  { font-size:13px; }
                .fontSize2 .moreOperator { font-size:16px; }
                
                .fontSize3 .title h1 { font-size:24px; }
                .fontSize3 .articlecontent p {  font-size:18px; }
                .fontSize3 .weibo .nickname,.fontSize3 .weibo .comment  { font-size:15px; }
                .fontSize3 .moreOperator { font-size:18px; }
                
                .fontSize4 .title h1 { font-size:26px; }
                .fontSize4 .articlecontent p {  font-size:20px; }
                .fontSize4 .weibo .nickname,.fontSize4 .weibo .comment  { font-size:16px; }
                .fontSize4 .moreOperator { font-size:20px; }
                
                .jumptoorg { display:block;margin:16px 0 16px; }
                .jumptoorg a {  }
                
                .moreOperator a { color:#385487; }
                
                .moreOperator .share{ border-top:1px solid #ddd; }
                
                .moreOperator .share a{ display:block;border:1px solid #ccc;border-radius:4px;margin:20px 0;border-bottom-style:solid;background:#f8f7f1;color:#000; }
                
                .moreOperator .share a span{ display:block;padding:10px 10px;border-radius:4px;text-align:center;border-top:1px solid #eee;border-bottom:1px solid #eae9e3;font-weight:bold; }
                
                .moreOperator .share a:hover,
                .moreOperator .share a:active { background:#efedea; }
                @media only screen and (-webkit-min-device-pixel-ratio: 2) {
                }
                </style>
            <script language="javascript">
                function auto_remove(img){
                    div=img.parentNode.parentNode;div.parentNode.removeChild(div);
                    img.onerror="";
                    return true;
                }
                
                function changefont(fontsize){
                    if(fontsize < 1 || fontsize > 4)return;
                    $('#content').removeClass().addClass('fontSize' + fontsize);
                }
                
                
                
                
                // 当微信内置浏览器完成内部初始化后会触发WeixinJSBridgeReady事件。
                document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() {
                                          //公众号支付
                                          $('#getBrandWCPayRequest').click(function(e){
                                                                                 
                                                                                 WeixinJSBridge.invoke('getBrandWCPayRequest',{
                                                                                                       "appId" : getAppId(), //公众号名称,由商户传入
                                                                                                       "timeStamp" : getTimeStamp(), //时间戳
                                                                                                       "nonceStr" : getNonceStr(), //随机串
                                                                                                       "package" : getPackage(),//扩展包
                                                                                                       "signType" : getSignType(), //微信签名方式:1.sha1
                                                                                                       "paySign" : getSign() //微信签名
                                                                                                       },function(res){
                                                                                                       if(res.err_msg == "get_brand_wcpay_request:ok" ) {
    
    alert (res.err_msg);
    var paysign = getSign();
    timestamp = getTimeStamp();
    url= "https://api.weixin.qq.com/pay/delivernotify?access_token=ONmUWirhnDG0UhVvcM3D6amz-XzsMr7hOCb4L59Jqrc5GJ0waRlbPQ7C-JiFbG2KFIN248T_GdmAxiOUuXDxxA";
    var str = '{"appid" : getAppId(),"openid" : "oarYcs5sNGIhUn615Gqb8l6GFYmU","transid" : "1219798201201408013230834317","out_trade_no" : "test000001","deliver_timestamp" : "<%=timestamp%>","deliver_status" : "1","deliver_msg" : "ok","app_signature" : "<%=paysign%>","sign_method" : "sha1"}';
    /*
    WeixinJSBridge.invoke('getBrandWCPayRequest',{
         "appid" : getAppId(), //公众号名称,由商户传入
         "openid" : "oarYcs5sNGIhUn615Gqb8l6GFYmU",
         "transid" : "1219798201201408013230834317",
         "out_trade_no" : "test000001",
         "deliver_timeStamp" : getTimeStamp(), //时间戳
        "deliver_status" : "1",
        "deliver_msg" : "ok",
         "app_signature" : getSign(), //微信签名
        "sign_method" : "sha1"
         },function(res){
                if(res.err_msg == "get_brand_wcpay_request:ok" ) {
                    alert(res.err_msg);
                }
         }
    )
    */
                                                                                                       }else{
    jQuery.post(url,str); 
                                                                                                           alert(res.err_msg);
                                                                                                       }
                                                                                                       // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
                                                                                                       //因此微信团队建议,当收到ok返回时,向商户后台询问是否收到交易成功的通知,若收到通知,前端展示交易成功的界面;若此时未收到通知,商户后台主动调用查询订单接口,查询订单的当前状态,并反馈给前端展示相应的界面。
                                                                                                       
                                                                                                       }); 
                                                                                 
                                                                                 });
                                          
                                          
                                          
                                          WeixinJSBridge.log('yo~ ready.');
                                          
                                          }, false)
                
                if(jQuery){
                    jQuery(function(){
                           
                           var width = jQuery('body').width() * 0.87;
                           jQuery('img').error(function(){
                                               var self = jQuery(this);
                                               var org = self.attr('data-original1');
                                               self.attr("src", org);
                                               self.error(function(){
                                                          auto_remove(this);
                                                          });
                                               });
                           jQuery('img').each(function(){
                                              var self = jQuery(this);
                                              var w = self.css('width');
                                              var h = self.css('height');
                                              w = w.replace('px', '');
                                              h = h.replace('px', '');
                                              if(w <= width){
                                              return;
                                              }
                                              var new_w = width;
                                              var new_h = Math.round(h * width / w);
                                              self.css({'width' : new_w + 'px', 'height' : new_h + 'px'});
                                              self.parents('div.pic').css({'width' : new_w + 'px', 'height' : new_h + 'px'});
                                              });
                           });
                }
                
                </script>
        </head>
        <body>
    	
            <div class="WCPay">
                <p>微信支付JSAPI测试页面a</p>
                <p>请将您申请公众账号支付权限的四个参数替换页面中的参数:partnerid、partnerkey、appid、appkey</p>
                <p>将此页面放在的支付授权测试目录下,测试微信号需添加白名单,并在公众账号内发起访问此页面<p>
    			<p>即可检查公众账号支付功能是否正常</p>
                <p></p>
            <a id="getBrandWCPayRequest" href="javascript:void(0);"><h1 class="title">点击提交测试</h1></a>        </div>
            
           
    </body>
    </html>
    

      

    测试成功的表现是跳到微信支付输入密码的页面,如下:

     

    js返回一个成功的弹窗。 

    提示get_brand_wcpay_request:ok

    b.发货通知

    没有遇到太大困难,代码如下:

    <?php
    include_once("/Wxpay/WxPayHelper.php");
    $timestamp = strtotime(date("Y-m-d H:i:s"));
    $timestamp = time();
    $strtoken = "http://www.xxx.cn/token/index.php";     //我是通过这个链接获取到的我的微信公众帐号的access_token  ,因个人情况不同
    $token = file_get_contents($strtoken);
    
    $obj['appid']               = "xxxxxx";
    $obj['appkey']              = "xxxxxxx";
    $obj['openid']              = "xxxxxx";   //测试用户的openId 
    $obj['transid']             = "1219798201201408043365444992";
    $obj['out_trade_no']        = "test000001";
    $obj['deliver_timestamp']   = $timestamp;
    $obj['deliver_status']      = "1";
    $obj['deliver_msg']         = "ok";
    
    
    $WxPayHelper = new WxPayHelper();
     //get_biz_sign函数受保护,需要先取消一下,否则会报错,需要将调用方法的Protect  去掉
    $app_signature  = $WxPayHelper->get_biz_sign($obj);
    
     $jsonmenu = '
     {
         "appid" : "'.$obj['appid'].'",
         "openid" : "'.$obj['openid'].'",
         "transid" : "'.$obj['transid'].'",
         "out_trade_no" : "'.$obj['out_trade_no'].'",
         "deliver_timestamp" : "'.$timestamp.'",
         "deliver_status" : "'.$obj['deliver_status'].'",
         "deliver_msg" : "'.$obj['deliver_msg'].'",
         "app_signature" : "'.$app_signature.'",
         "sign_method" : "sha1"
     }';
    
    $url= "https://api.weixin.qq.com/pay/delivernotify?access_token=".$token;
    $result = https_request($url, $jsonmenu);
    var_dump($result);
    
    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);
         curl_close($curl);
         return $output;
     }
    
    ?>
    

    这样去微信后台支付测试便可以点击发布,这样就进入了全网发布状态,正式目录下就可以进行开发和测试了 

    2.正式环境下调用微信支付接口

    这里,我遇到了问题,本来 是调用的微信官方demo中的例子进行的开发,可是遇到了一系列的问题。

    a.access_denied,  说明目录有问题,不过确实我们申请的域名有问题,然后就一封一封邮件的与微信方沟通,然后把我们的微信正式目录修改正确,这样算是近了一步

    b.这里需要特别提示下,有时候真的不是代码问题,在我们改过目录后,微信中的地址调用可以了(同事开发的),之前和我报同样的错误,而支付这块还是不行,后来还是与微信e-mail ,不知他们改了什么,就好了一点

    c.接着出现了安卓走通了,但是iphone 提示  get_brand_wcpay_request:fail,估计是参数穿的不正确,但是我是掉的demo的方法,需要自己传的参数也反复检查过,没有问题,可是就是过不去,后来还是与微信方面沟通,我传给微信的所有参数(包括demo中自己获取的参数)传给他们的技术支持,他提示我是timestamp 的问题,文档中强调过一句话,传给微信的参数必须都是,字符串类型,所以在demo中生成的$tempstamp 两侧加上引号,就ok了,事实证明微信的demo有些问题。当然动手能力强的人通过反复测试,研究他们的方法,是可以自己解决的,而我是靠技术支持给我提出

    这样支付接口就算调通了。

    代码日下:

    <?php
    include_once("Wxpay/WxPayHelper.php");
    $commonUtil = new CommonUtil();
    $wxPayHelper = new WxPayHelper();
    $body = $_GET['ordsubject'];
    $trade_no = $_GET['trade_no'];
    $total_fee = $_GET['ordtotal_fee'] * 100;
    $ordshow_url = $_GET['ordshow_url'];
    //$total_fee = "1";
    //var_dump($_GET);
    $addr_ip = $_SERVER["REMOTE_ADDR"];
    $wxPayHelper -> setParameter("bank_type", "WX");
    $wxPayHelper -> setParameter("body", $body);
    $wxPayHelper -> setParameter("partner", PARTNER);
    $wxPayHelper -> setParameter("out_trade_no", $trade_no);
    $wxPayHelper -> setParameter("total_fee", $total_fee);
    $wxPayHelper -> setParameter("fee_type", "1");
    $wxPayHelper -> setParameter("notify_url",NOTIFY_URL);
    $wxPayHelper -> setParameter("spbill_create_ip",$addr_ip);
    $wxPayHelper -> setParameter("input_charset", "UTF-8");
    ?>
    <html>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta content="application/xhtml+xml;charset=UTF-8" http-equiv="Content-Type">
    <meta content="no-cache,must-revalidate" http-equiv="Cache-Control">
    <meta content="no-cache" http-equiv="pragma">
    <meta content="0" http-equiv="expires">
    <meta content="telephone=no, address=no" name="format-detection">
    <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height" />
    
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <!-- apple devices fullscreen -->
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
    <script language="javascript">
    function callpay()
    {
    	WeixinJSBridge.invoke('getBrandWCPayRequest',<?php echo $wxPayHelper -> create_biz_package()  ?>,function(res){
    	    WeixinJSBridge.log(res.err_msg);
    	    //alert(res.err_code+res.err_desc+res.err_msg);
            if(res.err_msg == "get_brand_wcpay_request:ok"){
                location.href="成功跳转页";
            
            }else{
                location.href="失败跳转页";
            
            }
    	});
        
            
    }
    
    </script>
    <body>
    <br/>
    <br/>
    <br/>
    <br/>
    <br/>
    <br/>
    <button type="button" onclick="callpay()" style="200px;height:200px;">wx pay test</button>
    </body>
    </html>
    

      

    3.处理回调函数

    在支付成功后,微信会返回给当前页面一个js  alert  "get_brand_wcpay_request:ok",同时会回调,之前提交过的notify_url ,并传一些参数过去。我们需要通过这些参数来处理订单等程序,并在接收到参数后,返回给微信一个字符串 “success”,告诉微信 不用在回调这个链接了,不然微信的补单机制会反复调用这个链接。

    代码如下:

    function  notice(){
    if(isset($_GET) && !empty($_GET)){ if($_GET["trade_state"] == "0"){ //写日志 foreach($_GET as $k=>$v){ $p .= $k."=".$v." "; } $post = $GLOBALS["HTTP_RAW_POST_DATA"]; file_put_contents("notice.txt",date("Y-m-d H:i:s")." ".$p." ".$post." ", //写个日志文件 $out_trade_no = $_GET["out_trade_no"]; $parameter = array( "out_trade_no" => $out_trade_no, //商户订单编号; ); if(!checkorderstatus($out_trade_no)){ //判断订单是否处理过,如果处理过就不进行更改状态 orderhandle($parameter); //进行订单处理,并传送从支付宝返回的参数; } echo "success"; } }
    }
    }

    这样支付和通知接口基本完成了,像发货通知、订单查询、告警通知接口,我们基本用不到,就没有接

    还有一条需要注意的地方就是,微信不允许使用post、get传值,我是这样做的,a页面通过post向b页面传送参数,b页面接收到参数后,在整合后,调用微信接口,传给微信。但是遇到的问题是,b页面可以接受到参数,但是无法调用微信接口,点击按钮没有反应。后来我是将post传值改为header("location:xxxx&id=1");location后面拼接参数的形式来传参给b页面,这样就没有问题了。

      

  • 相关阅读:
    git 分支建立及合并
    git push 冲突
    Ubuntu 16.04下安装64位谷歌Chrome浏览器
    Nginx+uswgi+Django部署
    Deepin下python安装uwsgi报错: Python.h:没有那个文件或目录
    Deepin系统更新apt-get源
    语义化的理解
    尝试Vue3.0
    Vue3.0响应式实现
    Vue2.0响应式原理以及重写数组方法
  • 原文地址:https://www.cnblogs.com/xiaoyueer/p/3908553.html
Copyright © 2020-2023  润新知