最近做了微信公眾號支付的開發,由于是第一次做也摸索了幾天的時間,也只是達到了實現功能的水平,并沒有太多考慮到性能問題,所以這篇文章比較適合初學者。
微信公眾號支付的總體其實很簡單,大致就分為三步。第一步需要獲取用戶授權;第二步調用統一下單接口獲取預支付id;第三步H5調起微信支付的內置的js。下面介紹具體每一步的開發流程。
一 首先要明確微信公眾號支付屬于網頁版支付,所以相較于app的直接調取微信支付要多一步微信授權。也就是需要獲取用戶的openid。微信公眾號使用的交易類型是JSAPI,所以統一下單接口的文檔明確的寫到
因此我們必須去獲取openid,同時也可以處理一些我們需要的邏輯。獲取用戶授權有兩種方式:1.scope=snsapi_base;2.scope=snsapi_userinfo.我使用的是snsapi_base
Scope為snsapi_base
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=http%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60&response_type=code&scope=snsapi_base&state=123#wechat_redirect
Scope為snsapi_userinfo
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect
微信的官方文檔也有對各個參數的詳細說明,我就關鍵的參數仔細的說明一下。首先appid就不多說了就是你微信公眾號的appid固定寫死的,redirect_uri這個參數是最重要的,這個地址是訪問你處理的接口地址。你可以在這個鏈接上拼接上你所需要的參數,一般你是要把訂單的金額傳到這個接口里的,訪問這個鏈接的時候微信會給你code你需要用它去獲取openid,記得要對其進行urlencode處理。state參數可以理解為擴展字段,其他的參數都是固定寫法就不在多做介紹了。下面是獲取openid的代碼片段。
1
2
3
4
5
6
7
8
9
10
|
//獲取openId HttpClientUtil util = HttpClientUtil.getInstance(); Map<String, String> map = new HashMap<String, String>(); map.put( "appid" , WxPayConfig.APPID); map.put( "secret" , WxPayConfig.APPSECRET); map.put( "code" , code); map.put( "grant_type" , WxPayConfig.GRANT_TYPE); String returnStr = util.doPostRetString( "https://api.weixin.qq.com/sns/oauth2/access_token" , null ,map); logger.info( "returnStr:[" + returnStr + "]" ); AccessToken at = JSON.parseObject(returnStr, AccessToken. class ); |
AccessToken.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
public class AccessToken { private String access_token; private String expires_in; private String refresh_token; private String openid; private String scope; private String unionid; public String getAccess_token() { return access_token; } public void setAccess_token(String access_token) { this .access_token = access_token; } public String getExpires_in() { return expires_in; } public void setExpires_in(String expires_in) { this .expires_in = expires_in; } public String getRefresh_token() { return refresh_token; } public void setRefresh_token(String refresh_token) { this .refresh_token = refresh_token; } public String getOpenid() { return openid; } public void setOpenid(String openid) { this .openid = openid; } public String getScope() { return scope; } public void setScope(String scope) { this .scope = scope; } public String getUnionid() { return unionid; } public void setUnionid(String unionid) { this .unionid = unionid; } @Override public String toString() { return "AccessToken [access_token=" + access_token + ", expires_in=" + expires_in + ", refresh_token=" + refresh_token + ", openid=" + openid + ", scope=" + scope + ", unionid=" + unionid + "]" ; } } |
二 我們獲取了openid后,就可以進行下一步的統一下單的開發了。微信上統一下單接口的文檔寫的比較詳細了,具體的參數含義我就不多做介紹了。下面直接貼最直觀的代碼,特別提醒的是一定要注意簽名的正確。簽名所使用的key并不是AppSecret而是你申請時自己定義的商戶key。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
//統一下單 WxPaySendData data = new WxPaySendData(); data.setAppid(WxPayConfig.APPID); data.setAttach( "微信支付" ); data.setBody( "微信公眾號支付" ); data.setMch_id(WxPayConfig.MCHID); data.setNonce_str(nonceStr); data.setNotify_url(WxPayConfig.NOTIFY_URL); data.setOut_trade_no(tradeNo); data.setTotal_fee(( int )(fee* 100 )); //單位:分 data.setTrade_type( "JSAPI" ); data.setSpbill_create_ip(ip); data.setOpenid(at.getOpenid()); String returnXml = UnifiedorderService.unifiedOrder(data,WxPayConfig.KEY); WxPayReturnData reData = new WxPayReturnData(); XStream xs1 = new XStream( new DomDriver()); xs1.alias( "xml" , WxPayReturnData. class ); reData = (WxPayReturnData) xs1.fromXML(returnXml); |
UnifiedorderService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
public class UnifiedorderService { private final static Logger logger = LoggerFactory.getLogger(UnifiedorderService. class ); public static String unifiedOrder(WxPaySendData data,String key){ //統一下單支付 String returnXml = null ; try { //生成sign簽名 SortedMap<Object,Object> parameters = new TreeMap<Object,Object>(); parameters.put( "appid" , data.getAppid()); parameters.put( "attach" , data.getAttach()); parameters.put( "body" , data.getBody()); parameters.put( "mch_id" , data.getMch_id()); parameters.put( "nonce_str" , data.getNonce_str()); parameters.put( "notify_url" , data.getNotify_url()); parameters.put( "out_trade_no" , data.getOut_trade_no()); parameters.put( "total_fee" , data.getTotal_fee()); parameters.put( "trade_type" , data.getTrade_type()); parameters.put( "spbill_create_ip" , data.getSpbill_create_ip()); parameters.put( "openid" , data.getOpenid()); parameters.put( "device_info" , data.getDevice_info()); logger.info( "SIGN:" +WxSign.createSign(parameters,key)); data.setSign(WxSign.createSign(parameters,key)); XStream xs = new XStream( new DomDriver( "UTF-8" , new XmlFriendlyNameCoder( "-_" , "_" ))); xs.alias( "xml" , WxPaySendData. class ); String xml = xs.toXML(data); logger.info( "統一下單xml為:\n" + xml); HttpClientUtil util = HttpClientUtil.getInstance(); returnXml = util.doPostForString( "https://api.mch.weixin.qq.com/pay/unifiedorder" , null , xml); logger.info( "返回結果:" + returnXml); } catch (Exception e) { e.printStackTrace(); } return returnXml; } } |
WxSign
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
public class WxSign { private static String characterEncoding = "UTF-8" ; @SuppressWarnings ( "rawtypes" ) public static String createSign(SortedMap<Object,Object> parameters,String key){ StringBuffer sb = new StringBuffer(); Set es = parameters.entrySet(); //所有參與傳參的參數按照accsii排序(升序) Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); String k = (String)entry.getKey(); Object v = entry.getValue(); if ( null != v && ! "" .equals(v) && ! "sign" .equals(k) && ! "key" .equals(k)) { sb.append(k + "=" + v + "&" ); } } sb.append( "key=" + key); String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); return sign; } public static String getNonceStr() { Random random = new Random(); return MD5Util.MD5Encode(String.valueOf(random.nextInt( 10000 )), "UTF-8" ); } public static String getTimeStamp() { return String.valueOf(System.currentTimeMillis() / 1000 ); } } |
最后要提一下的是NOTIFY_URL回調地址,接收微信支付異步通知回調地址。
三 通過上面的操作我們獲得了預支付交易會話標識prepay_id,這樣我們就可以進行最后一步的操作了。使用H5調起支付api。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//H5調起支付 attr.addAttribute( "appId" , reData.getAppid()); attr.addAttribute( "timeStamp" , WxSign.getTimeStamp()); attr.addAttribute( "nonceStr" , reData.getNonce_str()); attr.addAttribute( "package" , "prepay_id=" +reData.getPrepay_id()); attr.addAttribute( "signType" , "MD5" ); SortedMap<Object,Object> signMap = new TreeMap<Object,Object>(); signMap.put( "appId" , reData.getAppid()); signMap.put( "timeStamp" , WxSign.getTimeStamp()); signMap.put( "nonceStr" , reData.getNonce_str()); signMap.put( "package" , "prepay_id=" +reData.getPrepay_id()); signMap.put( "signType" , "MD5" ); logger.info( "PaySIGN:" +WxSign.createSign(signMap,WxPayConfig.KEY)); attr.addAttribute( "paySign" , WxSign.createSign(signMap,WxPayConfig.KEY)); |
將需要的參數傳給頁面后,使用微信提供方法調起支付。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
<script> function getUrlParam(name) { //構造一個含有目標參數的正則表達式對象 var reg = new RegExp( "(^|&)" + name + "=([^&]*)(&|$)" ); //匹配目標參數 var r = window.location.search.substr(1).match(reg); //返回參數值 if (r != null ) return unescape(r[2]); return null ; } function onBridgeReady() { var appId = getUrlParam( 'appId' ); var timeStamp = getUrlParam( 'timeStamp' ); var nonceStr = getUrlParam( 'nonceStr' ); var Package = getUrlParam( 'package' ); var signType = getUrlParam( 'signType' ); var paySign = getUrlParam( 'paySign' ); WeixinJSBridge.invoke( 'getBrandWCPayRequest' , { "appId" : appId, //"wx2421b1c4370ec43b", //公眾號名稱,由商戶傳入 "timeStamp" : timeStamp, //"1395712654", //時間戳,自1970年以來的秒數 "nonceStr" : nonceStr, //"e61463f8efa94090b1f366cccfbbb444", //隨機串 "package" : Package, //"prepay_id=u802345jgfjsdfgsdg888", "signType" : signType, //"MD5", //微信簽名方式: "paySign" : paySign, //"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信簽名 }, function (res) { // 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功后返回 ok,但并不保證它絕對可靠。 //alert(res.err_msg); if (res.err_msg == "get_brand_wcpay_request:ok" ) { alert( "支付成功" ); } if (res.err_msg == "get_brand_wcpay_request:cancel" ) { alert( "交易取消" ); } if (res.err_msg == "get_brand_wcpay_request:fail" ) { alert( "支付失敗" ); } }); } function callPay() { 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> |
在返回結果的地方可以自定義一些自己的返回頁面。
總結:由于我也是第一次做,寫這篇文章是想記錄一下自己的工作成果,和分享給一下也是新手的朋友們可以有一些幫助,最后希望有好的見解朋友可以留言討論,大家一起學習進步。
以上就是關于java開發微信公眾支付的所有內容了,希望大家能夠喜歡。