springboot对接微信小程序V3微信支付功能(支付、退款、查询)
- 1. 程序开发前的准备工作,获取微信支付相关的参数
-
- 1.1 在application.yml文件中填写配置信息
- 1.2 获取配置文件中的参数
- 2. 在pom.xml中引入相关依赖
- 3. 实现微信支付相关方法
-
- 3.1 获取平台证书
- 3.2 微信支付
- 3.3 微信支付回调
- 3.4 微信退款
- 3.5 订单查询
1. 程序开发前的准备工作,获取微信支付相关的参数
- appId:小程序appid
- appSecret:小程序的secret
- mchId:商户号
- keyPath:商户私钥路径(apiclient_key.pem)
- certPath:证书路径(apiclient_cert.pem)
- platFormPath:平台证书(cert.pem)
注 : 需要通过写程序生成平台证书(见v3Get()方法)
- apiKey3:apiv3密钥
- serialnumber:商户证书序列号
- notifyUrl:回调地址
注 : 回调地址需要是线上能访问到的地址,本地的地址是无效的
1.1 在application.yml文件中填写配置信息
pay:
wechat:
appId: xxx
appSecret: xxx
mchId: xxx
keyPath: D:\xx\xx\WxPay\apiclient_key.pem
certPath: D:\xx\xx\\WxPay\apiclient_cert.pem
platFormPath: D:\xx\xx\\WxPay\cert.pem
apiKey3: xxx
serialnumber: xxx
notifyUrl: http://xxx:xx/wx/pay/payNotify
1.2 获取配置文件中的参数
@Value("${pay.wechat.appId}")
String appid;
@Value("${pay.wechat.appSecret}")
String secret;
@Value("${pay.wechat.mchId}")
String mchid;
@Value("${pay.wechat.apiKey3}")
String mchKey;
@Value("${pay.wechat.notifyUrl}")
String notifyUrl;
@Value("${pay.wechat.certPath}")
String certPath;
@Value("${pay.wechat.keyPath}")
String certKeyPath;
@Value("${pay.wechat.platFormPath}")
String platFormPath;
@Value("${pay.wechat.serialnumber}")
String serialNumber;
2. 在pom.xml中引入相关依赖
<dependency>
<groupId>com.github.javen205groupId>
<artifactId>IJPay-WxPayartifactId>
<version>2.7.4version>
dependency>
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-coreartifactId>
<version>5.8.16version>
dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3groupId>
<artifactId>wechatpay-apache-httpclientartifactId>
<version>0.4.7version>
dependency>
3. 实现微信支付相关方法
3.1 获取平台证书
public String v3Get() {
try {
IJPayHttpResponse response = WxPayApi.v3(
RequestMethod.GET,
WxDomain.CHINA.toString(),
WxApiType.GET_CERTIFICATES.toString(),
mchid,
serialNumber,
null,
certKeyPath,
""
);
String serialNumber = response.getHeader("Wechatpay-Serial");
String body = response.getBody();
int status = response.getStatus();
log.info("serialNumber: {}", serialNumber);
log.info("status: {}", status);
log.info("body: {}", body);
int isOk = 200;
if (status == isOk) {
JSONObject jsonObject = JSONUtil.parseObj(body);
JSONArray dataArray = jsonObject.getJSONArray("data");
JSONObject encryptObject = dataArray.getJSONObject(0);
JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
String associatedData = encryptCertificate.getStr("associated_data");
String cipherText = encryptCertificate.getStr("ciphertext");
String nonce = encryptCertificate.getStr("nonce");
String serialNo = encryptObject.getStr("serial_no");
final String platSerialNo = savePlatformCert(associatedData, nonce, cipherText, platFormPath);
log.info("平台证书序列号: {} serialNo: {}", platSerialNo, serialNo);
}
boolean verifySignature = WxPayKit.verifySignature(response, platFormPath);
System.out.println("verifySignature:" + verifySignature);
return body;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private String savePlatformCert(String associatedData, String nonce, String cipherText, String certPath) {
try {
AesUtil aesUtil = new AesUtil(mchKey.getBytes(StandardCharsets.UTF_8));
String publicKey = aesUtil.decryptToString(
associatedData.getBytes(StandardCharsets.UTF_8),
nonce.getBytes(StandardCharsets.UTF_8),
cipherText
);
cn.hutool.core.io.file.FileWriter writer = new FileWriter(certPath);
writer.write(publicKey);
X509Certificate certificate = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes()));
return certificate.getSerialNumber().toString(16).toUpperCase();
} catch (Exception e) {
e.printStackTrace();
return e.getMessage();
}
}
3.2 微信支付
public String wxPay(String openId, String payId, int amount) {
try {
String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
.setAppid(appid)
.setMchid(mchid)
.setDescription("门票账单支付")
.setOut_trade_no(payId)
.setTime_expire(timeExpire)
.setNotify_url(notifyUrl)
.setAmount(new Amount().setTotal(amount))
.setPayer(new Payer().setOpenid(openId));
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
IJPayHttpResponse response = WxPayApi.v3(
RequestMethod.POST,
WxDomain.CHINA.toString(),
WxApiType.JS_API_PAY.toString(),
mchid,
serialNumber,
null,
certKeyPath,
JSONUtil.toJsonStr(unifiedOrderModel)
);
log.info("统一下单响应 {}", response);
boolean verifySignature = WxPayKit.verifySignature(response, platFormPath);
log.info("verifySignature: {}", verifySignature);
if (response.getStatus() == HttpStatus.HTTP_OK && verifySignature) {
String body = response.getBody();
JSONObject jsonObject = JSONUtil.parseObj(body);
String prepayId = jsonObject.getStr("prepay_id");
Map<String, String> map = WxPayKit.jsApiCreateSign(appid, prepayId, certKeyPath);
log.info("唤起支付参数:{}", map);
String jsonStr = JSONUtil.toJsonStr(map);
return jsonStr;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
3.3 微信支付回调
public void payNotify(HttpServletRequest request, HttpServletResponse response) {
Map<String, String> map = new HashMap<>(12);
try {
String timestamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String serialNo = request.getHeader("Wechatpay-Serial");
String signature = request.getHeader("Wechatpay-Signature");
log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
String result = HttpKit.readData(request);
log.info("支付通知密文 {}", result);
String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
mchKey, platFormPath);
log.info("支付通知明文 {}", plainText);
if (StrUtil.isNotEmpty(plainText)) {
response.setStatus(200);
map.put("code", "SUCCESS");
map.put("message", "SUCCESS");
} else {
response.setStatus(500);
map.put("code", "ERROR");
map.put("message", "签名错误");
}
response.setHeader("Content-type", ContentType.JSON.toString());
response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
response.flushBuffer();
} catch (Exception e) {
e.printStackTrace();
}
}
3.4 微信退款
public String payRefund(int amount, int qty, String outTradeNo, String outRefundNo) {
log.info("进入退款接口------>" );
log.info("执行操作的 原支付交易对应的商户订单号:{}", outTradeNo );
try {
log.info("商户退款单号: {}", outRefundNo);
List<RefundGoodsDetail> list = new ArrayList<>();
RefundGoodsDetail refundGoodsDetail = new RefundGoodsDetail()
.setMerchant_goods_id(outRefundNo)
.setGoods_name("取消订单")
.setUnit_price(amount)
.setRefund_amount(amount)
.setRefund_quantity(qty);
list.add(refundGoodsDetail);
RefundModel refundModel = new RefundModel()
.setOut_refund_no(outRefundNo)
.setReason("取消订单")
.setAmount(new RefundAmount().setRefund(1).setTotal(1).setCurrency("CNY"))
.setGoods_detail(list);
if (outTradeNo != null && !outTradeNo.equals("")) {
refundModel.setOut_trade_no( outTradeNo );
}
log.info("退款参数: {}", JSONUtil.toJsonStr(refundModel));
IJPayHttpResponse response = WxPayApi.v3(
RequestMethod.POST,
WxDomain.CHINA.toString(),
WxApiType.DOMESTIC_REFUNDS.toString(),
mchid,
serialNumber,
null,
certKeyPath,
JSONUtil.toJsonStr(refundModel)
);
log.info("退款响应 {}", response);
boolean verifySignature = WxPayKit.verifySignature(response, platFormPath);
log.info("verifySignature: {}", verifySignature);
String body = response.getBody();
JSONObject jsonObject = JSONUtil.parseObj(body);
log.info("响应数据:" + body);
if(response.getStatus()==200){
System.out.println("==================================");
System.out.println("退款成功!");
}
if (verifySignature) {
return response.getBody();
}
} catch (Exception e) {
e.printStackTrace();
return e.getMessage();
}
return null;
}
3.5 订单查询
public String queryPayResult(String payId) throws Exception{
WechatPayHttpClientBuilder builder = getWechatPayHttpClientBuilder();
CloseableHttpClient httpClient = builder.build();
String result = "";
try {
URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/"+ payId +"?mchid=" + mchid);
HttpGet httpGet = new HttpGet(uriBuilder.build());
httpGet.addHeader("Accept", "application/json");
CloseableHttpResponse response = httpClient.execute(httpGet);
result = EntityUtils.toString(response.getEntity());
log.info("支付信息:" + result);
JSONObject jsonObject = JSONUtil.parseObj(result);
String code = jsonObject.getStr("trade_state");
log.info("支付结果:" + code);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private WechatPayHttpClientBuilder getWechatPayHttpClientBuilder() throws Exception{
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
new FileInputStream(certKeyPath));
CertificatesManager certificatesManager = CertificatesManager.getInstance();
certificatesManager.putMerchant(mchid, new WechatPay2Credentials(mchid,
new PrivateKeySigner(serialNumber, merchantPrivateKey)), mchKey.getBytes(StandardCharsets.UTF_8));
Verifier verifier = certificatesManager.getVerifier(mchid);
X509Certificate certificate = verifier.getValidCertificate();
List<X509Certificate> certificates = new ArrayList<>();
certificates.add(certificate);
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(mchid, serialNumber, merchantPrivateKey)
.withWechatPay(certificates);
return builder;
}