# 微信扫码支付
以下主要是针对微信支付V3接口进行的整合,且为Native支付
# 前期准备工作
1️⃣:获取微信相关数据
#绑定支付的appid
#商户id
#商户支付秘钥wechatKey,这个需要V3的
#证书序列号
#私钥文件(.pem文件)
#下面给出了相应的链接
1
2
3
4
5
6
2
3
4
5
6
⚠️:==这里千万别用微信服务商去注册,原因见下图==
上面的参数都获取到后,就可以进行springboot的整合了
2️⃣:导入maven坐标
<!--微信支付-->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.6</version>
</dependency>
1
2
3
4
5
6
2
3
4
5
6
package com.test.test.api.wechat.pay;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.partnerpayments.nativepay.model.Transaction;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.Amount;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* @author xiaobo
* @date 2023/3/8
*/
@Controller
@Slf4j
@RequestMapping("/wechat")
public class WechatPayApi extends BaseController {
private static final String APP_ID = "";
private static final String MERCHANT_ID = "";
private static final String WECHAT_KEY = "";
private static final String MERCHANT_SERIAL_NUMBER = "";
private static final String NOTIFY_URL = "";
public static NativePayService service;
public static Config config = null;
public static final String WECHAT_PAY_SIGNATURE = "Wechatpay-Signature";
public static final String WECHAT_PAY_TIMESTAMP = "Wechatpay-Timestamp";
public static final String WECHAT_PAY_SIGNATURE_TYPE = "Wechatpay-Signature-Type";
public static final String WECHAT_PAY_SERIAL = "Wechatpay-Serial";
public static final String WECHAT_PAY_NONCE = "Wechatpay-Nonce";
/**
* Native支付预下单
*/
@RequestMapping("prepay")
@ResponseBody
public RespResult wechatPay(double total_amount) {
PrepayRequest prepayRequest = new PrepayRequest();
Amount amount = new Amount();
// total为分
amount.setTotal((int) (total_amount * 100));
prepayRequest.setAmount(amount);
prepayRequest.setMchid(MERCHANT_ID);
prepayRequest.setAppid(APP_ID);
prepayRequest.setNotifyUrl(NOTIFY_URL);
prepayRequest.setDescription("账户充值");
// 这里的订单号要保证唯一,这种方法不推荐(因为并发高的情况下会出现订单号不唯一)
prepayRequest.setOutTradeNo(new SimpleDateFormat("yyyyMMddhhmmss").format(new Date()) + RandomUtil.randomNumbers(4));
PrepayResponse prepayResponse = service.prepay(prepayRequest);
// TODO 数据库处理订单(这里应该相应的生成订单)
// 返回成功,以及相应的二维码地址(前端需要进行转换为二维码)
return RespResult.success(prepayResponse.getCodeUrl(), jsonObject);
}
return RespResult.fail("获取微信支付码失败!");
}
@ResponseBody
@PostMapping("success")
public RespResult success(HttpServletRequest request) throws IOException {
// 读取请求头内容
String serialNumber = request.getHeader(WECHAT_PAY_SERIAL);
String nonce = request.getHeader(WECHAT_PAY_NONCE);
String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
String signType = request.getHeader(WECHAT_PAY_SIGNATURE_TYPE);
log.error(serialNumber, nonce, signature, timestamp);
// 获取请求体内容
ServletInputStream servletInputStream = request.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(servletInputStream));
StringBuffer stringBuffer = new StringBuffer();
String s;
while ((s = bufferedReader.readLine()) != null) {
stringBuffer.append(s);
}
// 构造 RequestParam
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(serialNumber)
.nonce(nonce)
.signature(signature)
.timestamp(timestamp)
// 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048
.signType(signType)
.body(stringBuffer.toString())
.build();
// 初始化 NotificationParser
NotificationParser parser = new NotificationParser((NotificationConfig) config);
// 验签并解密报文
Transaction decryptObject = parser.parse(requestParam, Transaction.class);
log.error(decryptObject.toString());
Map params = new HashMap(3);
Map resultMap = (Map) JSON.parse(stringBuffer.toString());
// TODO 你的业务逻辑处理
return RespResult.success("成功!");
}
return RespResult.success("失败!");
}
@PostConstruct
public static NativePayService initializeWechatConfiguration() throws FileNotFoundException {
// 初始化商户配置
String privateKeyPath = Objects.requireNonNull(KuocaiCdnBootApplication.class.getResource("/apiclient_key.pem")).getPath();
if (config == null) {
config =
new RSAAutoCertificateConfig.Builder()
.merchantId(MERCHANT_ID)
// 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
.privateKeyFromPath(privateKeyPath)
.merchantSerialNumber(MERCHANT_SERIAL_NUMBER)
.apiV3Key(WECHAT_KEY)
.build();
}
if (service == null) {
service = new NativePayService.Builder().config(config).build();
}
log.info("初始化微信配置成功~~~~~~");
// 初始化服务
return service;
}
}
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
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
==注意:这里的回调接口一定是域名下的接口,且可以访问,后面没参数==
相应的微信V3接口文档说明如下