展示图:
对接的完整流程如下
首先是配置
1
2
3
4
|
常量:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/**微信支付统一下单接口*/
public static final String unifiedOrderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder" ;
public static String SUCCESSxml = "<xml> \r\n" +
"\r\n" +
" <return_code><![CDATA[SUCCESS]]></return_code>\r\n" +
" <return_msg><![CDATA[OK]]></return_msg>\r\n" +
" </xml> \r\n" +
"" ;
public static String ERRORxml = "<xml> \r\n" +
"\r\n" +
" <return_code><![CDATA[FAIL]]></return_code>\r\n" +
" <return_msg><![CDATA[invalid sign]]></return_msg>\r\n" +
" </xml> \r\n" +
"" ;
|
工具类准备:
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
|
package com.jc.utils.util;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.text.SimpleDateFormat;
import java.util.*;
public class CommUtils {
private static Logger logger = LoggerFactory.getLogger(CommUtils. class );
// 连接超时时间,默认10秒
private static int socketTimeout = 60000 ;
// 传输超时时间,默认30秒
private static int connectTimeout= 60000 ;
/**
* Util工具类方法
* 获取一定长度的随机字符串,范围0-9,a-z
* @param length:指定字符串长度
* @return 一定长度的随机字符串
*/
public static String getRandomStringByLength( int length) {
String base = "abcdefghijklmnopqrstuvwxyz0123456789" ;
Random random = new Random();
StringBuffer sb = new StringBuffer();
for ( int i = 0 ; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
/**
* 获取订单号
* @return
*/
public static String getOrderNo(){
SimpleDateFormat ft = new SimpleDateFormat( "yyyyMMddHHmmss" );
String time = ft.format( new Date());
int mathCode = ( int ) ((Math.random() * 9 + 1 ) * 10000 ); // 5位随机数
String resultCode = time+mathCode;
return resultCode;
}
/**
* Util工具类方法
* 获取真实的ip地址
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader( "X-Forwarded-For" );
if (StringUtils.isNotEmpty(ip) && ! "unKnown" .equalsIgnoreCase(ip)) {
//多次反向代理后会有多个ip值,
int index = ip.indexOf( "," );
if (index != - 1 ) {
return ip.substring( 0 , index);
} else {
return ip;
}
}
ip = request.getHeader( "X-Real-IP" );
if (StringUtils.isNotEmpty(ip) && ! "unKnown" .equalsIgnoreCase(ip)) {
return ip;
}
return request.getRemoteAddr();
}
/**
* 签名字符串
* @param text 需要签名的字符串
* @param key 密钥
* @param input_charset 编码格式
* @return 签名结果
*/
public static String sign(String text, String key, String input_charset) {
text = text + "&key=" + key;
System.out.println(text);
return DigestUtils.md5Hex(getContentBytes(text, input_charset));
}
/**
* 签名字符串
* @param text 需要签名的字符串
* @param sign 签名结果
* @param key 密钥
* @param input_charset 编码格式
* @return 签名结果
*/
public static boolean verify(String text, String sign, String key, String input_charset) {
text = text + key;
String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));
if (mysign.equals(sign)) {
return true ;
} else {
return false ;
}
}
/**
* @param content
* @param charset
* @return
* @throws UnsupportedEncodingException
*/
public static byte [] getContentBytes(String content, String charset) {
if (charset == null || "" .equals(charset)) {
return content.getBytes();
}
try {
return content.getBytes(charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException( "MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
}
}
/**
* 生成6位或10位随机数 param codeLength(多少位)
* @return
*/
public static String createCode( int codeLength) {
String code = "" ;
for ( int i = 0 ; i < codeLength; i++) {
code += ( int ) (Math.random() * 9 );
}
return code;
}
@SuppressWarnings ( "unused" )
private static boolean isValidChar( char ch) {
if ((ch >= '0' && ch <= '9' ) || (ch >= 'A' && ch <= 'Z' ) || (ch >= 'a' && ch <= 'z' ))
return true ;
if ((ch >= 0x4e00 && ch <= 0x7fff ) || (ch >= 0x8000 && ch <= 0x952f ))
return true ; // 简体中文汉字编码
return false ;
}
/**
* 除去数组中的空值和签名参数
* @param sArray 签名参数组
* @return 去掉空值与签名参数后的新签名参数组
*/
public static Map<String, String> paraFilter(Map<String, String> sArray) {
Map<String, String> result = new HashMap<>();
if (sArray == null || sArray.size() <= 0 ) {
return result;
}
for (String key : sArray.keySet()) {
String value = sArray.get(key);
if (value == null || value.equals( "" ) || key.equalsIgnoreCase( "sign" )
|| key.equalsIgnoreCase( "sign_type" )) {
continue ;
}
result.put(key, value);
}
return result;
}
/**
* 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
* @param params 需要排序并参与字符拼接的参数组
* @return 拼接后字符串
*/
public static String createLinkString(Map<String, String> params) {
List<String> keys = new ArrayList<>(params.keySet());
Collections.sort(keys);
String prestr = "" ;
for ( int i = 0 ; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
if (i == keys.size() - 1 ) { // 拼接时,不包括最后一个&字符
prestr = prestr + key + "=" + value;
} else {
prestr = prestr + key + "=" + value + "&" ;
}
}
return prestr;
}
/**
*
* @param requestUrl 请求地址
* @param requestMethod 请求方法
* @param outputStr 参数
*/
public static String httpRequest(String requestUrl,String requestMethod,String outputStr){
logger.warn( "请求报文:" +outputStr);
StringBuffer buffer = null ;
try {
URL url = new URL(requestUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod(requestMethod);
conn.setDoOutput( true );
conn.setDoInput( true );
conn.connect();
//往服务器端写内容
if ( null !=outputStr){
OutputStream os=conn.getOutputStream();
os.write(outputStr.getBytes( "utf-8" ));
os.close();
}
// 读取服务器端返回的内容
InputStream is = conn.getInputStream();
InputStreamReader isr = new InputStreamReader(is, "utf-8" );
BufferedReader br = new BufferedReader(isr);
buffer = new StringBuffer();
String line = null ;
while ((line = br.readLine()) != null ) {
buffer.append(line);
}
br.close();
} catch (Exception e){
e.printStackTrace();
}
logger.warn( "返回报文:" +buffer.toString());
return buffer.toString();
}
/**
* POST请求
* @param url 请求url
* @param xmlParam 请求参数
* @param apiclient 证书
* @param mch_id 商户号
* @return
* @throws Exception
*/
public static String post(String url, String xmlParam,String apiclient,String mch_id) throws Exception {
logger.warn( "请求报文:" +xmlParam);
StringBuilder sb = new StringBuilder();
try {
KeyStore keyStore = KeyStore.getInstance( "PKCS12" );
FileInputStream instream = new FileInputStream( new File(apiclient));
try {
keyStore.load(instream, mch_id.toCharArray());
} finally {
instream.close();
}
// 证书
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, mch_id.toCharArray()).build();
// 只允许TLSv1协议
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext, new String[]{ "TLSv1" }, null , SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
//创建基于证书的httpClient,后面要用到
CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(sslsf).build();
HttpPost httpPost = new HttpPost(url); //退款接口
StringEntity reqEntity = new StringEntity(xmlParam, "UTF-8" );
// 设置类型
reqEntity.setContentType( "application/x-www-form-urlencoded" );
httpPost.setEntity(reqEntity);
CloseableHttpResponse response = client.execute(httpPost);
try {
HttpEntity entity = response.getEntity();
System.out.println(response.getStatusLine());
if (entity != null ) {
BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(entity.getContent(), "UTF-8" ));
String text = "" ;
while ((text = bufferedReader.readLine()) != null ) {
sb.append(text);
}
}
EntityUtils.consume(entity);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
logger.warn( "返回报文:" +sb.toString());
return sb.toString();
}
public static String urlEncodeUTF8(String source){
String result=source;
try {
result=java.net.URLEncoder.encode(source, "UTF-8" );
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
/**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
* @param strxml
* @return
* @throws org.jdom2.JDOMException
* @throws IOException
*/
public static Map doXMLParse(String strxml) throws Exception {
if ( null == strxml || "" .equals(strxml)) {
return null ;
}
Map m = new HashMap();
InputStream in = String2Inputstream(strxml);
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "" ;
List children = e.getChildren();
if (children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = getChildrenText(children);
}
m.put(k, v);
}
in.close();
return m;
}
/**
* 获取子结点的xml
* @param children
* @return String
*/
public static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if (!children.isEmpty()) {
Iterator it = children.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append( "<" + name + ">" );
if (!list.isEmpty()) {
sb.append(getChildrenText(list));
}
sb.append(value);
sb.append( "</" + name + ">" );
}
}
return sb.toString();
}
public static InputStream String2Inputstream(String str) {
return new ByteArrayInputStream(str.getBytes());
}
}
|
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
|
package com.jch.mng.controller;
import com.jch.boot.component.CommonInfo;
import com.jch.boot.component.Result;
import com.jch.boot.component.ServiceCommonInfo;
import com.jch.mng.dto.input.gzh.WxPayDto;
import com.jch.mng.service.WxPayService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Created by xxs on 2021/7/30 10:54
*
* @Description 公众号微信支付
* @Version 2.9
*/
@RestController
@RequestMapping ( "/wxPay" )
public class WxPayController {
@Autowired
private WxPayService payService;
/**
* @Author: xxs
* @param dto
* @param request
* @Date: 2021/7/30 11:55
* @Description: 公众号微信支付
* @Version: 2.9
* @Return: com.jch.boot.component.Result<java.lang.String>
*/
@PostMapping ( "/pay" )
public Result<String> pay( @RequestBody WxPayDto dto, HttpServletRequest request) throws Exception {
ServiceCommonInfo<Object> result = payService.pay(dto,request);
return CommonInfo.controllerBack(result);
}
/**
* @Author: xxs
* @param request
* @param response
* @Date: 2021/7/30 11:55
* @Description: 支付回调
* @Version: 2.9
* @Return: void
*/
@PostMapping ( "/notify" )
public void notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
payService.notify(request,response);
}
}
|
service接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package com.jch.mng.service;
import com.jch.boot.component.ServiceCommonInfo;
import com.jch.mng.dto.input.gzh.WxPayDto;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Created by xxs on 2021/7/30 9:56
*
* @Description
* @Version 2.9
*/
public interface WxPayService {
ServiceCommonInfo<Object> pay(WxPayDto dto, HttpServletRequest request) throws Exception;
void notify(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
|
接口实现:
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
|
package com.jch.mng.service.impl;
import com.alibaba.fastjson.JSON;
import com.jc.utils.util.CommUtils;
import com.jch.boot.component.ServiceCommonInfo;
import com.jch.mng.constant.WeChatConstants;
import com.jch.mng.dto.input.gzh.WxPayDto;
import com.jch.mng.service.WxPayService;
import com.jch.mng.utils.DoubleUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
/**
* Created by xxs on 2021/7/30 9:56
*
* @Description
* @Version 2.9
*/
@Service
public class WxPayServiceImpl implements WxPayService {
public String appId;
public String mch_id;
public String notify_url;
public String key;
@Value ( "${gzh.appid}" )
public void setAppId(String appId) {
this .appId = appId;
}
@Value ( "${wxPay.mchId}" )
public void setMch_id(String mch_id) {
this .mch_id = mch_id;
}
@Value ( "${wxPay.notifyUrl}" )
public void setNotify_url(String notify_url) {
this .notify_url = notify_url;
}
@Value ( "${wxPay.key}" )
public void setKey(String key) {
this .key = key;
}
private static Logger logger = LoggerFactory.getLogger(WxPayServiceImpl. class );
/**
* @Author: xxs
* @param dto
* @param request
* @Date: 2021/7/30 11:01
* @Description: 微信支付
* @Version: 2.9
* @Return: com.jch.boot.component.ServiceCommonInfo<java.lang.Object>
*/
@Override
public ServiceCommonInfo<Object> pay(WxPayDto dto, HttpServletRequest request) throws Exception {
logger.info( "公众号微信支付, 入参:{}" , JSON.toJSONString(dto));
String openid = dto.getOpenid();
String outTradeNo = dto.getOutTradeNo();
String body = dto.getBody();
Double totalFee = dto.getTotalFee();
String nonce_str = CommUtils.getRandomStringByLength( 32 );
String spbill_create_ip = CommUtils.getIpAddr(request);
Map<String, String> packageParams = new HashMap<>();
packageParams.put( "appid" , appId);
packageParams.put( "mch_id" ,mch_id);
packageParams.put( "nonce_str" , nonce_str);
packageParams.put( "body" , body);
packageParams.put( "out_trade_no" , outTradeNo);
double t = DoubleUtil.parseDouble(totalFee); //保留两位小数
int aDouble = Integer.parseInt( new java.text.DecimalFormat( "0" ).format(t* 100 ));
packageParams.put( "total_fee" , aDouble+ "" );
packageParams.put( "spbill_create_ip" , spbill_create_ip);
packageParams.put( "notify_url" , notify_url);
packageParams.put( "trade_type" , "JSAPI" );
packageParams.put( "openid" , openid);
packageParams = CommUtils.paraFilter(packageParams);
String prestr = CommUtils.createLinkString(packageParams);
String sign = CommUtils.sign(prestr, key, "utf-8" ).toUpperCase();
logger.info( "统一下单请求签名:" + sign );
String xml = "<xml version='1.0' encoding='gbk'>" + "<appid>" + appId + "</appid>"
+ "<body><![CDATA[" + body + "]]></body>"
+ "<mch_id>" + mch_id + "</mch_id>"
+ "<nonce_str>" + nonce_str + "</nonce_str>"
+ "<notify_url>" + notify_url+ "</notify_url>"
+ "<openid>" + openid + "</openid>"
+ "<out_trade_no>" + outTradeNo + "</out_trade_no>"
+ "<spbill_create_ip>" + spbill_create_ip + "</spbill_create_ip>"
+ "<total_fee>" + aDouble+ "" + "</total_fee>"
+ "<trade_type>" + "JSAPI" + "</trade_type>"
+ "<sign>" + sign + "</sign>"
+ "</xml>" ;
String result = CommUtils.httpRequest(WeChatConstants.unifiedOrderUrl, "POST" , xml);
Map map = CommUtils.doXMLParse(result);
Object return_code = map.get( "return_code" );
logger.info( "统一下单返回return_code:" + return_code );
if (return_code == "SUCCESS" || return_code.equals(return_code)){
Map<String,String> resultMap= new HashMap<String, String>();
String prepay_id = (String) map.get( "prepay_id" );
resultMap.put( "appId" , appId);
Long timeStamp = System.currentTimeMillis() / 1000 ;
resultMap.put( "timeStamp" , timeStamp + "" );
resultMap.put( "nonceStr" , nonce_str);
resultMap.put( "package" , "prepay_id=" + prepay_id);
resultMap.put( "signType" , "MD5" );
logger.info( "参与paySign签名数据, 入参:{}" , JSON.toJSONString(resultMap));
String linkString = CommUtils.createLinkString(resultMap);
String paySign = CommUtils.sign(linkString, key, "utf-8" ).toUpperCase();
logger.info( "获取到paySign:" +paySign);
resultMap.put( "paySign" , paySign);
return ServiceCommonInfo.success( "ok" , resultMap);
}
return ServiceCommonInfo.serviceFail( "支付失败" , null );
}
/**
* @Author: xxs
* @param request
* @param response
* @Date: 2021/7/31 15:17
* @Description: 微信支付回调
* @Version: 2.9
* @Return: void
*/
@Override
public void notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
logger.info( "进入支付回调啦啦啦啦*-*" );
String resXml = "" ;
BufferedReader br = new BufferedReader( new InputStreamReader((ServletInputStream) request.getInputStream()));
String line = null ;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null ) {
sb.append(line);
}
br.close();
String notityXml = sb.toString();
logger.info( "支付回调返回数据:" +notityXml);
Map map = CommUtils.doXMLParse(notityXml);
Object returnCode = map.get( "return_code" );
Object result_code = map.get( "result_code" );
if ( "SUCCESS" .equals(returnCode) && "SUCCESS" .equals(result_code)) {
Map<String, String> validParams = CommUtils.paraFilter(map); //回调验签时需要去除sign和空值参数
String validStr = CommUtils.createLinkString(validParams); //把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
String sign = CommUtils.sign(validStr, key , "utf-8" ).toUpperCase(); //拼装生成服务器端验证的签名
logger.info( "支付回调生成签名:" +sign);
String transaction_id = (String) map.get( "transaction_id" );
String order_no = (String) map.get( "out_trade_no" );
String time_end = (String) map.get( "time_end" );
String total_fee = (String) map.get( "total_fee" );
//签名验证,并校验返回的订单金额是否与商户侧的订单金额一致
if (sign.equals(map.get( "sign" ))) {
logger.info( "支付回调验签通过" );
//通知微信服务器已经支付成功
resXml = WeChatConstants.SUCCESSxml;
} else {
logger.info( "微信支付回调失败!签名不一致" );
}
} else {
resXml = WeChatConstants.ERRORxml;
}
System.out.println(resXml);
logger.info( "微信支付回调返回数据:" +resXml);
logger.info( "微信支付回调数据结束" );
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
}
}
|
前端页面:
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
|
<!DOCTYPE html>
<html lang= "en" >
<head>
<meta charset= "UTF-8" >
<title>Title</title>
<meta content= "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;" name= "viewport" />
</head>
<body>
body: <input type= "text" class= "inp-body" ><br>
outTradeNo: <input type= "text" class= "inp-outTradeNo" ><br>
totalFee: <input type= "text" class= "inp-totalFee" ><br>
openid: <input type= "text" class= "inp-openid" ><br>
<button onclick= "handleWxPay()" >支付</button>
</body>
</html>
<script src= "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" ></script>
<script>
function handleWxPay(){
let obj = {
"body" :$( ".inp-body" ).val(),
"outTradeNo" :$( ".inp-outTradeNo" ).val(),
"totalFee" :$( ".inp-totalFee" ).val(),
"openid" :$( ".inp-openid" ).val(),
}
$.ajax({
type: "POST" ,
url: "微信支付接口地址" ,
data:JSON.stringify(obj),
beforeSend: function (request) {
request.setRequestHeader( "Content-Type" , "application/json" );
},
success: result=> {
let obj = JSON.parse(result.data)
onBridgeReady(obj)
}
});
}
function onBridgeReady(obj){
WeixinJSBridge.invoke(
'getBrandWCPayRequest' , obj,
function (res){
alert(JSON.stringify(res))
if (res.err_msg == "get_brand_wcpay_request:ok" ){
// 使用以上方式判断前端返回,微信团队郑重提示:
//res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
}
});
}
</script>
|
访问前端页面记得加依赖:
1
2
3
4
|
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
|
访问页面需要写控制类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package com.jch.mng.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletResponse;
/**
* Created by xxs on 2021/7/31 12:29
*
* @Description
* @Version 2.9
*/
@Controller
public class TestPageController {
@RequestMapping ( "/wxPayTest" )
public String test(HttpServletResponse response) {
return "wxPay" ;
}
}
|
运行项目访问。
部署项目到服务器,用手机访问即可拉起支付。
附:名词解释
商户号:微信支付分配的商户号。支付审核通过后,申请人邮箱会收到腾讯下发的开户邮件, 邮件中包含商户平台的账号、密码等重要信息。
appid:商户通过微信管理后台,申请服务号、订阅号、小程序或APP应用成功之后,微信会为每个应用分配一个唯一标识id。
openid:用户在公众号内的身份标识,一旦确认,不会再变;同一用户在不同公众号拥有不同的openid。商户后台系统通过登录授权、支付通知、查询订单等API可获取到用户的openid。主要用途是判断同一个用户,对用户发送客服消息、模版消息等。
微信管理后台:微信有很多管理平台,容易混淆,我们主要关注下面三个平台:
1. 微信公众平台 微信公众账号申请入口和管理后台。商户可以在公众平台提交基本资料、业务资料、财务资料申请开通微信支付功能。帐号分类:服务号、订阅号、小程序、企业微信(也叫企业号,类似于企业OA)。
2. 微信商户平台 微信支付相关的商户功能集合,包括参数配置、支付数据查询与统计、在线退款、代金券或立减优惠运营等功能。
3. 微信开放平台 商户APP接入微信支付开放接口的申请入口,通过此平台可申请微信APP支付。
签名:商户后台和微信支付后台根据相同的密钥和算法生成一个结果,用于校验双方身份合法性。签名的算法 由微信支付制定并公开,常用的签名方式有:MD5、SHA1、SHA256、HMAC等。
密钥:作为签名算法中的盐,需要在微信平台及商户业务系统各存一份,要妥善保管。 key设置路径:微信商户平台(http://pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置。
总结
到此这篇关于springboot对接微信支付的文章就介绍到这了,更多相关springboot对接微信支付内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!
原文链接:https://blog.csdn.net/qq_43560721/article/details/119276262