微信支付Java开发服务端详解编程语言

想要调用微信支付第一就是获取openid:
获取openid的方法:
首先你需要在前台获取到code,

在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(服务号获得高级接口后,默认拥有scope参数中的snsapi_base和snsapi_userinfo),引导关注者打开如下页面:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
若提示“该链接无法访问”,请检查参数是否填写错误,是否拥有scope参数对应的授权作用域权限。

scope两种参数的说明:
1、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
2、以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。
3、用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。这个接口,包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。

在公众号的自定义菜单中设置的url为
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=URL&response_type=code&scope=snsapi_userinfo&state=STATE&connect_redirect=1#wechat_redirect
其中APPID为公众号ID,URL是你要访问的网址,注意网页要进行URLEncoder编码。

如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数redirect_uri?state=STATE

code说明 :
code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。

这样在前台就可以直接获取到url当中的code值,

var code = getQueryString("code"); 
 
function getQueryString(name) { 
            var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"); 
            var r = window.location.search.substr(1).match(reg); 
            if (r != null) 
                return unescape(r[2]); 
            return null; 
        }

获取到了code的值后,就可以去请求openid了。
尤其注意:由于公众号的secret和获取到的access_token安全级别都非常高,必须只保存在服务器,不允许传给客户端。后续刷新access_token、通过access_token获取用户信息等步骤,也必须从服务器发起。

获取code后,请求以下链接获取access_token:
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

正确时返回的JSON数据包如下:

{
“access_token”:”ACCESS_TOKEN”,
“expires_in”:7200,
“refresh_token”:”REFRESH_TOKEN”,
“openid”:”OPENID”,
“scope”:”SCOPE”
}

这样就获取到了access_token和openid了。
有了openid,我们就可以去获取到pre_id。
获取pre_id的接口地址为:https://api.mch.weixin.qq.com/pay/unifiedorder

请求参数详见微信支付官方文档
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

其中注意attach参数,这个是附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。这个字段可以传我们的业务数据,用来后续的业务处理。

notify_url参数,这个回调地址一定要写外网能直接访问的地址(可以直接把网址放到浏览器上,看看是不是能直接访问),如果写内网地址是收不到回调信息的哦。

spbill_create_ip参数,这个参数笔者也不清楚应该怎么传,笔者就传的192.168.1.1,如果你们传这个不好使我要找我。

sign参数,详情见微信支付签名生成规则:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
其实微信给的demo中就有现成的sign生成方法
首先先把所有参数放入一个map中

public Map<String, Object> toMap() 
    { 
        Map<String, Object> map = new HashMap<String, Object>(); 
        Field[] fields = this.getClass().getDeclaredFields(); 
        for (Field field : fields) 
        { 
            Object obj; 
            try 
            { 
                obj = field.get(this); 
                if (obj != null) 
                { 
                    map.put(field.getName(), obj); 
                } 
            } 
            catch (IllegalArgumentException e) 
            { 
                e.printStackTrace(); 
            } 
            catch (IllegalAccessException e) 
            { 
                e.printStackTrace(); 
            } 
        } 
        return map; 
    } 
 
String sign = Signature.getSign(toMap()); 
 
public static String getSign(Map<String,Object> map){ 
        ArrayList<String> list = new ArrayList<String>(); 
        for(Map.Entry<String,Object> entry:map.entrySet()){ 
            if(entry.getValue()!=""){ 
                list.add(entry.getKey() + "=" + entry.getValue() + "&"); 
            } 
        } 
        int size = list.size(); 
        String [] arrayToSort = list.toArray(new String[size]); 
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER); 
        StringBuilder sb = new StringBuilder(); 
        for(int i = 0; i < size; i ++) { 
            sb.append(arrayToSort[i]); 
        } 
        String result = sb.toString(); 
        result += "key=" + Configure.getKey();//自己保管的私钥 
        //Util.log("Sign Before MD5:" + result); 
        result = MD5.MD5Encode(result).toUpperCase(); 
        //Util.log("Sign Result:" + result); 
        return result; 
    }

然后把这个sign放入参数中就可以啦。

请求成功后,微信会返回一段xml数据信息,例如:

<xml> 
   <return_code><![CDATA[SUCCESS]]></return_code> 
   <return_msg><![CDATA[OK]]></return_msg> 
   <appid><![CDATA[wx2421b1c4370ec43b]]></appid> 
   <mch_id><![CDATA[10000100]]></mch_id> 
   <nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str> 
   <openid><![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]]></openid> 
   <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign> 
   <result_code><![CDATA[SUCCESS]]></result_code> 
   <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id> 
   <trade_type><![CDATA[JSAPI]]></trade_type> 
</xml>

这里提供一个把XML转换成Map的方法:

public static Map<String,Object> getMapFromXML(String xmlString) throws ParserConfigurationException, IOException, SAXException { 
 
        //这里用Dom的方式解析回包的最主要目的是防止API新增回包字段 
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 
        DocumentBuilder builder = factory.newDocumentBuilder(); 
        InputStream is =  Util.getStringStream(xmlString); 
        Document document = builder.parse(is); 
 
        //获取到document里面的全部结点 
        NodeList allNodes = document.getFirstChild().getChildNodes(); 
        Node node; 
        Map<String, Object> map = new HashMap<String, Object>(); 
        int i=0; 
        while (i < allNodes.getLength()) { 
            node = allNodes.item(i); 
            if(node instanceof Element){ 
                map.put(node.getNodeName(),node.getTextContent()); 
            } 
            i++; 
        } 
        return map; 
 
    }

到这里我们拿到了prepay_id,这里顺便说一下,如果是扫码支付前面的trade_type参数为NATIVE,再返回的时候会有一个code_url,在前台直接用这个url生成二维码图片就可以了。

下面继续说公众号支付。
前台H5调用微信支付的参数说明见:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6

这里什么时间戳,随机字符串最后在服务端生成再传到前台来。

String timestamp = Long.toString(System.currentTimeMillis() / 1000); // 必填,生成签名的时间戳   
String nonceStr = UUID.randomUUID().toString(); // 必填,生成签名的随机串  

注意package参数为prepay_id=PRERAY_ID一定要带上prepay_id=。
同时生成签名的时候也注意要带着prepay_id=,这块算个坑。

具体前台调起微信支付的代码直接复制微信文档

function onBridgeReady(){ 
   WeixinJSBridge.invoke( 
       'getBrandWCPayRequest', { 
           "appId":"wx2421b1c4370ec43b",     //公众号名称,由商户传入      
           "timeStamp":"1395712654",         //时间戳,自1970年以来的秒数      
           "nonceStr":"e61463f8efa94090b1f366cccfbbb444", //随机串      
           "package":"prepay_id=u802345jgfjsdfgsdg888",      
           "signType":"MD5",         //微信签名方式:      
           "paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名  
       }, 
       function(res){      
           if(res.err_msg == "get_brand_wcpay_request:ok" ) {}     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。  
       } 
   );  
} 
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(); 
}

这里注意支付成功后那里的判断res.err_msg == “get_brand_wcpay_request:ok”,坑爹的是那里的:是中文的分号,我说回调咋一直都不进呢!!
还有补充,要注意在商户后台配置支付授权目录,否则支付页面是出不来的,我当时没配是一闪而过。

至此能够成功调起微信支付,支付成功后会向notify_url发送支付信息。例如:

<xml> 
  <appid><![CDATA[wx2421b1c4370ec43b]]></appid> 
  <attach><![CDATA[支付测试]]></attach> 
  <bank_type><![CDATA[CFT]]></bank_type> 
  <fee_type><![CDATA[CNY]]></fee_type> 
  <is_subscribe><![CDATA[Y]]></is_subscribe> 
  <mch_id><![CDATA[10000100]]></mch_id> 
  <nonce_str><![CDATA[5d2b6c2a8db53831f7eda20af46e531c]]></nonce_str> 
  <openid><![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]]></openid> 
  <out_trade_no><![CDATA[1409811653]]></out_trade_no> 
  <result_code><![CDATA[SUCCESS]]></result_code> 
  <return_code><![CDATA[SUCCESS]]></return_code> 
  <sign><![CDATA[B552ED6B279343CB493C5DD0D78AB241]]></sign> 
  <sub_mch_id><![CDATA[10000100]]></sub_mch_id> 
  <time_end><![CDATA[20140903131540]]></time_end> 
  <total_fee>1</total_fee> 
  <trade_type><![CDATA[JSAPI]]></trade_type> 
  <transaction_id><![CDATA[1004400740201409030005092168]]></transaction_id> 
</xml>

具体参数说明见:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
其中attach参数就是传输的业务数据,这样就可以进行业务处理了。

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/13978.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论