Java 使用微信支付
前言百度搜了一下微信支付,都描述的不太好,于是乎打算自己寫一個(gè)案例,希望以后拿來直接改造使用。
因?yàn)樯婕岸S碼的前端顯示,所以有前端的內(nèi)容
一. 準(zhǔn)備工作
所需微信公眾號(hào)信息配置
- APPID:綁定支付的APPID(必須配置)
- MCHID:商戶號(hào)(必須配置)
- KEY:商戶支付密鑰,參考開戶郵件設(shè)置(必須配置)
- APPSECRET:公眾帳號(hào)secert(僅JSAPI支付的時(shí)候需要配置)
我這個(gè)案例用的是尚硅谷一位老師提供的,這里不方便提供出來,需要大家自己找,或者公司提供
二. 構(gòu)建項(xiàng)目架構(gòu)
1.新建maven項(xiàng)目
2.導(dǎo)入依賴
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
|
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version> 2.2 . 1 .RELEASE</version> </parent> <dependencies> <!--spring boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--微信提供的sdk--> <dependency> <groupId>com.github.wxpay</groupId> <artifactId>wxpay-sdk</artifactId> <version> 0.0 . 3 </version> </dependency> <!--發(fā)送http請(qǐng)求--> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <!--模板引擎--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> </dependencies> |
依賴中需要注意的是我導(dǎo)入了微信提供的sdk,以及freemarker模板引擎
3.編寫配置文件application.properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
# 服務(wù)端口 server.port= 8081 # 微信開放平臺(tái) appid wx.pay.app_id= #商戶號(hào) wx.pay.partner= #商戶key wx.pay.partnerkey= #回調(diào)地址 wx.pay.notifyurl: spring.freemarker.tempalte-loader-path=classpath:/templates # 關(guān)閉緩存,及時(shí)刷新,上線生產(chǎn)環(huán)境需要修改為 true spring.freemarker.cache= false spring.freemarker.charset=UTF- 8 spring.freemarker.check-template-location= true spring.freemarker.content-type=text/html spring.freemarker.expose-request-attributes= true spring.freemarker.expose-session-attributes= true spring.freemarker.request-context-attribute=request spring.freemarker.suffix=.ftl spring.mvc. static -path-pattern: / static /** |
4.編寫啟動(dòng)類
1
2
3
4
5
6
7
8
|
@SpringBootApplication @ComponentScan (basePackages = { "com.haiyang.wxpay" }) public class Application { public static void main(String[] args) { SpringApplication.run(Application. class , args); } } |
5.創(chuàng)建常用包c(diǎn)ontroller,service,impl,utils
6.創(chuàng)建兩個(gè)前端需要的文件夾 static和templates
三. 代碼實(shí)現(xiàn)
1. 創(chuàng)建工具類讀取配置文件的參數(shù)
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
|
@Component public class WxPayUtils implements InitializingBean { @Value ( "${wx.pay.app_id}" ) private String appId; @Value ( "${wx.pay.partner}" ) private String partner; @Value ( "${wx.pay.partnerkey}" ) private String partnerKey; @Value ( "${wx.pay.notifyurl}" ) private String notifyUrl; public static String WX_PAY_APP_ID; public static String WX_PAY_PARTNER; public static String WX_PAY_PARTNER_KEY; public static String WX_OPEN_NOTIFY_URL; @Override public void afterPropertiesSet() throws Exception { WX_PAY_APP_ID = appId; WX_PAY_PARTNER = partner; WX_PAY_PARTNER_KEY = partnerKey; WX_OPEN_NOTIFY_URL = notifyUrl; } } |
2. 構(gòu)建工具類發(fā)送http請(qǐng)求
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
/** * http請(qǐng)求客戶端 * * @author qy * */ public class HttpClient { private String url; private Map<String, String> param; private int statusCode; private String content; private String xmlParam; private boolean isHttps; public boolean isHttps() { return isHttps; } public void setHttps( boolean isHttps) { this .isHttps = isHttps; } public String getXmlParam() { return xmlParam; } public void setXmlParam(String xmlParam) { this .xmlParam = xmlParam; } public HttpClient(String url, Map<String, String> param) { this .url = url; this .param = param; } public HttpClient(String url) { this .url = url; } public void setParameter(Map<String, String> map) { param = map; } public void addParameter(String key, String value) { if (param == null ) param = new HashMap<String, String>(); param.put(key, value); } public void post() throws ClientProtocolException, IOException { HttpPost http = new HttpPost(url); setEntity(http); execute(http); } public void put() throws ClientProtocolException, IOException { HttpPut http = new HttpPut(url); setEntity(http); execute(http); } public void get() throws ClientProtocolException, IOException { if (param != null ) { StringBuilder url = new StringBuilder( this .url); boolean isFirst = true ; for (String key : param.keySet()) { if (isFirst) url.append( "?" ); else url.append( "&" ); url.append(key).append( "=" ).append(param.get(key)); } this .url = url.toString(); } HttpGet http = new HttpGet(url); execute(http); } /** * set http post,put param */ private void setEntity(HttpEntityEnclosingRequestBase http) { if (param != null ) { List<NameValuePair> nvps = new LinkedList<NameValuePair>(); for (String key : param.keySet()) nvps.add( new BasicNameValuePair(key, param.get(key))); // 參數(shù) http.setEntity( new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 設(shè)置參數(shù) } if (xmlParam != null ) { http.setEntity( new StringEntity(xmlParam, Consts.UTF_8)); } } private void execute(HttpUriRequest http) throws ClientProtocolException, IOException { CloseableHttpClient httpClient = null ; try { if (isHttps) { SSLContext sslContext = new SSLContextBuilder() .loadTrustMaterial( null , new TrustStrategy() { // 信任所有 public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true ; } }).build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslContext); httpClient = HttpClients.custom().setSSLSocketFactory(sslsf) .build(); } else { httpClient = HttpClients.createDefault(); } CloseableHttpResponse response = httpClient.execute(http); try { if (response != null ) { if (response.getStatusLine() != null ) statusCode = response.getStatusLine().getStatusCode(); HttpEntity entity = response.getEntity(); // 響應(yīng)內(nèi)容 content = EntityUtils.toString(entity, Consts.UTF_8); } } finally { response.close(); } } catch (Exception e) { e.printStackTrace(); } finally { httpClient.close(); } } public int getStatusCode() { return statusCode; } public String getContent() throws ParseException, IOException { return content; } } |
額~有點(diǎn)長(zhǎng)就不放圖片了 代碼都一樣
3. 新建controller
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
@Controller @RequestMapping ( "/wxpay" ) public class WxPayController { @RequestMapping ( "/pay" ) public String createPayQRcode(Model model) throws Exception{ String price = "0.01" ; String no = getOrderNo(); Map m = new HashMap(); m.put( "appid" , WxPayUtils.WX_PAY_APP_ID); m.put( "mch_id" , WxPayUtils.WX_PAY_PARTNER); m.put( "nonce_str" , WXPayUtil.generateNonceStr()); m.put( "body" , "微信支付測(cè)試" ); //主體信息 m.put( "out_trade_no" , no); //訂單唯一標(biāo)識(shí) m.put( "total_fee" , getMoney(price)); //金額 m.put( "spbill_create_ip" , "127.0.0.1" ); //項(xiàng)目的域名 m.put( "notify_url" , WxPayUtils.WX_OPEN_NOTIFY_URL); //回調(diào)地址 m.put( "trade_type" , "NATIVE" ); //生成二維碼的類型 //3 發(fā)送httpclient請(qǐng)求,傳遞參數(shù)xml格式,微信支付提供的固定的地址 HttpClient client = new HttpClient( "https://api.mch.weixin.qq.com/pay/unifiedorder" ); //設(shè)置xml格式的參數(shù) //把xml格式的數(shù)據(jù)加密 client.setXmlParam(WXPayUtil.generateSignedXml(m, WxPayUtils.WX_PAY_PARTNER_KEY)); client.setHttps( true ); //執(zhí)行post請(qǐng)求發(fā)送 client.post(); //4 得到發(fā)送請(qǐng)求返回結(jié)果 //返回內(nèi)容,是使用xml格式返回 String xml = client.getContent(); //把xml格式轉(zhuǎn)換map集合,把map集合返回 Map<String,String> resultMap = WXPayUtil.xmlToMap(xml); //最終返回?cái)?shù)據(jù) 的封裝 Map map = new HashMap(); map.put( "no" , no); map.put( "price" , price); map.put( "result_code" , resultMap.get( "result_code" )); map.put( "code_url" , resultMap.get( "code_url" )); model.addAttribute( "map" ,map); return "pay" ; } @GetMapping ( "queryorder/{no}" ) @ResponseBody public String queryPayStatus( @PathVariable String no) throws Exception{ //1、封裝參數(shù) Map m = new HashMap<>(); m.put( "appid" , WxPayUtils.WX_PAY_APP_ID); m.put( "mch_id" , WxPayUtils.WX_PAY_PARTNER); m.put( "out_trade_no" , no); m.put( "nonce_str" , WXPayUtil.generateNonceStr()); //2 發(fā)送httpclient HttpClient client = new HttpClient( "https://api.mch.weixin.qq.com/pay/orderquery" ); client.setXmlParam(WXPayUtil.generateSignedXml(m, WxPayUtils.WX_PAY_PARTNER_KEY)); client.setHttps( true ); client.post(); //3.得到訂單數(shù)據(jù) String xml = client.getContent(); Map<String, String> resultMap = WXPayUtil.xmlToMap(xml); //4.判斷是否支付成功 if (resultMap.get( "trade_state" ).equals( "SUCCESS" )) { /* 改變數(shù)據(jù)庫中的數(shù)據(jù)等操作 */ return "支付成功"; } return "支付中"; } @GetMapping("success") public String success(){ return "success"; } @RequestMapping("test") public String test(){ return "pay"; } /** * 生成訂單號(hào) * @return */ public static String getOrderNo() { SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); String newDate = sdf.format(new Date()); String result = ""; Random random = new Random(); for (int i = 0; i < 3; i++) { result += random.nextInt(10); } return newDate + result; } /** * 元轉(zhuǎn)換成分 * @param amount * @return */ public static String getMoney(String amount) { if (amount== null ){ return "" ; } // 金額轉(zhuǎn)化為分為單位 // 處理包含, ¥ 或者$的金額 String currency = amount.replaceAll( "\\$|\\¥|\\," , "" ); int index = currency.indexOf( "." ); int length = currency.length(); Long amLong = 0l; if (index == - 1 ){ amLong = Long.valueOf(currency+ "00" ); } else if (length - index >= 3 ){ amLong = Long.valueOf((currency.substring( 0 , index+ 3 )).replace( "." , "" )); } else if (length - index == 2 ){ amLong = Long.valueOf((currency.substring( 0 , index+ 2 )).replace( "." , "" )+ 0 ); } else { amLong = Long.valueOf((currency.substring( 0 , index+ 1 )).replace( "." , "" )+ "00" ); } return amLong.toString(); } } |
值得一提的是 這里我們用的是controller而不是restcontroller,因?yàn)槲覀冃枰故径S碼
4. 在templates文件中新建 訂單支付頁面(二維碼生成的頁面)
注意:文件名必須和生成二維碼方法中返回的字符串名稱一樣 我這里叫 pay
先新建html頁面,然后再將后綴改成ftl(freemarker模板引擎的后綴名)
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
|
<!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <title>Title</title> <script src= "/static/qrcode.js" ></script> <script src= "https://cdn.bootcss.com/jquery/3.4.1/jquery.js" ></script> </head> <center> <div id= "qrcode" ></div> </center> <script type= "text/javascript" > new QRCode(document.getElementById( "qrcode" ), "${map.code_url}" ); // 設(shè)置要生成二維碼的鏈接 </script> <script type= "text/javascript" > var int =self.setInterval( "querystatus()" , 3000 ); function querystatus() { $.get( "/wxpay/queryorder/${map.no}" ,function(data,status){ if (data=== "支付中" ){ console.log( "支付中" ); } else { clearInterval( int ) window.location.href= "/wxpay/success" rel= "external nofollow" } }) } </script> </body> </html> |
再創(chuàng)建支付成功跳轉(zhuǎn)的頁面 文件名要與支付成功方法返回的文件名一樣
1
2
3
4
5
6
7
8
9
10
|
<!DOCTYPE html> < html lang = "en" > < head > < meta charset = "UTF-8" > < title >Title</ title > </ head > < body > < h1 >支付成功</ h1 > </ body > </ html > |
引入 qrcode 生成二維碼的依賴,放入static文件中
這里我提供下載鏈接
鏈接: https://pan.baidu.com/s/15-E3KpRCenAewh0ZaBLnjQ 提取碼: xhs9 復(fù)制這段內(nèi)容后打開百度網(wǎng)盤手機(jī)App,操作更方便哦
引入完成后
最后 我們啟動(dòng)項(xiàng)目來測(cè)試一下
瀏覽器輸入地址
http://localhost:8081/wxpay/pay
發(fā)現(xiàn)二維碼生成成功,并且定時(shí)器也沒問題
之后我們掃碼支付
成功跳轉(zhuǎn)到支付成功頁面 ~nice
四. 總結(jié)
- 首先就是生成二維碼,需要的幾個(gè)主要的參數(shù),訂單號(hào),金額,購買的信息(主體信息),其余的參數(shù)除了一些可以不寫的都是固定的
- 生成二維碼然后展示在頁面上,用的qrcode插件,生成
- 然后設(shè)置定時(shí)器,來實(shí)時(shí)查詢訂單是否支付
- 查詢訂單信息的寫法和生成二維碼的方式差不多 無非就是請(qǐng)求時(shí)少了幾個(gè)參數(shù),必須得帶上訂單號(hào)
- 微信提供的查詢訂單接口返回?cái)?shù)據(jù)中 trade_state 代表支付狀態(tài) notpay沒有支付,seccess表示已成功
- 定時(shí)器檢測(cè)到訂單支付成功就清除定時(shí)器,并且執(zhí)行支付成功之后的操作
實(shí)際項(xiàng)目中遠(yuǎn)沒有這么簡(jiǎn)單,并且所有的數(shù)據(jù)都要從數(shù)據(jù)庫中獲取,在這里我為了方便把價(jià)格固定寫死的
到此這篇關(guān)于Java調(diào)用微信支付功能的方法示例代碼的文章就介紹到這了,更多相關(guān)Java調(diào)用微信支付內(nèi)容請(qǐng)搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!
原文鏈接:https://blog.csdn.net/haiyanghan/article/details/106792920