导入依赖:
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.12</version>
</dependency>
证书位置:
代码:
package com.rongtong.modules.sys.controller;
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.core.util.PemUtil;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;
import lombok.Data;
import lombok.SneakyThrows;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.wechat.pay.java.core.http.Constant.*;
@RestController
public class WxPayController {
/**
* 回调地址
*/
private final String notifyUrl = "https://pay.weixin.qq.com/notify";
/**
* 小程序appid
*/
private final String appid = "wx75c957248b5****";
/**
* 支付商户号 APP 网页 小程序可以绑定一个
*/
private final String mchId = "168848****";
/**
* 商户证书序列号
*/
private final String merchantSerialNumber = "566518841E4C8A6QQ9AD79F6713D7CF488D****";
/**
* 商户证书序列号
*/
private final String apiV3Key = "AYTAkNOQQEwDwYDVQQHDAhTa1VuW****";
/**
* 证书路径
*/
private final String certPath = "cert/apiclient_key.pem";
@Data
public class WxPayVO {
// 预支付交易会话标识小程序下单接口返回的prepay_id参数值
private String prepayId;
// 随机字符串
private String nonceStr;
// 时间戳
private Long timeStamp;
// 签名
private String paySign;
// 商户号
private String partnerId;
}
/**
* JSAPI下单
*/
@SneakyThrows
@GetMapping("/pay")
public void wxPay() {
// 商户订单号,需要保持唯一性(只能是字母或者数字,不能包含有其他字符)
String outTradeNo = System.currentTimeMillis() + "";
// 商品价格,单位:分
Integer totalPrice = 1;
// 用户openid
String openid = "o0djK5CksPX1RHo0YKs-uwzE****";
// 使用自动更新平台证书的RSA配置
// 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
Config config = new RSAAutoCertificateConfig.Builder()
.merchantId(mchId)
.privateKeyFromPath(this.getApiClientKeyPath())
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3Key)
.build();
// 构建service
JsapiService service = new JsapiService.Builder().config(config).build();
// request.setXxx(val)设置所需参数,具体参数可见Request定义
PrepayRequest request = new PrepayRequest();
Amount amount = new Amount();
amount.setTotal(totalPrice);
request.setAmount(amount);
request.setAppid(appid);
request.setMchid(mchId);
request.setDescription("商品描述");
request.setNotifyUrl(notifyUrl);
request.setOutTradeNo(outTradeNo);
request.setAttach("");
Payer payer = new Payer();
payer.setOpenid(openid);
request.setPayer(payer);
// 调用下单方法,得到应答
PrepayResponse response = service.prepay(request);
WxPayVO vo = new WxPayVO();
Long timeStamp = System.currentTimeMillis() / 1000;
vo.setTimeStamp(timeStamp);
String substring = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
vo.setNonceStr(substring);
String signatureStr = Stream.of(appid, String.valueOf(timeStamp), substring, "prepay_id=" + response.getPrepayId())
.collect(Collectors.joining("\n", "", "\n"));
String sign = this.getSign(signatureStr, this.getApiClientKeyPath());
vo.setPaySign(sign);
vo.setPrepayId("prepay_id=" + response.getPrepayId());
System.out.println("WxPayRespVO=" + vo);
}
/**
* 微信支付回调
*/
@SneakyThrows
@PostMapping("/notify")
public void wxPayNotify(HttpServletRequest request) {
//读取请求体的信息
ServletInputStream inputStream = request.getInputStream();
StringBuffer stringBuffer = new StringBuffer();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s;
//读取回调请求体
while ((s = bufferedReader.readLine()) != null) {
stringBuffer.append(s);
}
String s1 = stringBuffer.toString();
String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
String nonce = request.getHeader(WECHAT_PAY_NONCE);
String signType = request.getHeader("Wechatpay-Signature-Type");
String serialNo = request.getHeader(WECHAT_PAY_SERIAL);
String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
NotificationConfig config = new RSAAutoCertificateConfig.Builder()
.merchantId(mchId)
.privateKeyFromPath(this.getApiClientKeyPath())
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3Key)
.build();
NotificationParser parser = new NotificationParser(config);
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(serialNo)
.nonce(nonce)
.signature(signature)
.timestamp(timestamp)
.signType(signType)
.body(s1)
.build();
Transaction parse = parser.parse(requestParam, Transaction.class);
// 商户订单号
String outTradeNo = parse.getOutTradeNo();
System.out.println("outTradeNo=" + outTradeNo);
// 业务逻辑
}
/**
* 获取证书路径
* 在 jar 包中,无法直接获取到证书的路径,需要将证书写入到临时文件中
*
* @return
*/
public String getApiClientKeyPath() {
String dbPath = null;
URL resource = WxPayController.class.getClassLoader().getResource(certPath);
try {
Path tempFile = Files.createTempFile("temp_", ".crt");
Files.copy(resource.openStream(), tempFile, StandardCopyOption.REPLACE_EXISTING);
dbPath = tempFile.toFile().getPath();
} catch (IOException e) {
e.printStackTrace();
}
return dbPath;
}
@SneakyThrows
private String getSign(String signatureStr, String privateKey) {
String replace = privateKey.replace("\\n", "\n");
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKeyFromPath(replace);
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(merchantPrivateKey);
sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
return Base64Utils.encodeToString(sign.sign());
}
}