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 # 后续生成的平台证书路径
# apiv3秘钥
apiKey3: xxx
# 商户证书序列号
serialnumber: xxx
# 回调地址
notifyUrl: http://xxx:xx/wx/pay/payNotify # 线上地址
1.2 获取配置文件中的参数
/**微信小程序appid**/
@Value("${pay.wechat.appId}")
String appid;
/**微信小程序secretId**/
@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中引入相关依赖
<!-- https://mvnrepository.com/artifact/com.github.javen205/IJPay-WxPay -->
<dependency>
<groupId>com.github.javen205</groupId>
<artifactId>IJPay-WxPay</artifactId>
<version>2.7.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-core -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.8.16</version>
</dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.7</version>
</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));
// 平台证书密文解密
// encrypt_certificate 中的 associated_data nonce ciphertext
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) {
//商户订单号(随机字符串)
// String orderSn = PayKit.generateStr();
try {
String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
// APPID
.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);
// 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
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 {
// String outRefundNo = PayKit.generateStr();
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("取消订单")
//.setNotify_url(wechatPayV3Bean.getDomain().concat("/wechat/operation/pay/refundNotify")) //退款异步回调通知
.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); //转换为JSON
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));
// ... 若有多个商户号,可继续调用putMerchant添加商户信息
// 从证书管理器中获取verifier
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);
// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
return builder;
}
文章来源地址https://www.toymoban.com/news/detail-662821.html
文章来源:https://www.toymoban.com/news/detail-662821.html
到了这里,关于springboot实现微信小程序V3微信支付功能的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!