1、引入包
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.12</version>
</dependency>
2、导入证书,配置商家号等信息
3、初始化微信支付配置,这里会自动加载微信支付用到的类,并且完成签名认证等操作。
@Bean
public Config config() throws IOException {
//nginx转发
requestUrl = properties.getRequestUrl();
String path = CLASS_PATH + certPemPath;
Resource resourceCert = resourceLoader.getResource(path);
String privatePath = CLASS_PATH + privateKeyPath;
Resource resourcePrivate = resourceLoader.getResource(privatePath);
String privateKey = inputStreamToString(resourcePrivate.getInputStream());
X509Certificate certificate = getCertificate(resourceCert.getInputStream());
String merchantSerialNumber = certificate.getSerialNumber().toString(16).toUpperCase();
RSAAutoCertificateConfig config = new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKey(privateKey)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3key)
.build();
return config;
}
/**
* 微信支付对象
* @param config Config
* @return JsapiService
*/
@Bean
public JsapiService jsapiService(Config config){
log.info("==========加载微信支付对象");
JsapiService service = new JsapiService.Builder().config(config).hostName().build();
return service;
}
/**
* 获取证书 将文件流转成证书文件
*
* @param inputStream 证书文件
* @return {@link X509Certificate} 获取证书
*/
public static X509Certificate getCertificate(InputStream inputStream) {
try {
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
cert.checkValidity();
return cert;
} catch (CertificateExpiredException e) {
throw new RuntimeException("证书已过期", e);
} catch (CertificateNotYetValidException e) {
throw new RuntimeException("证书尚未生效", e);
} catch (CertificateException e) {
throw new RuntimeException("无效的证书", e);
}
}
4、nativePay,返回二维码支付连接,前端转成二维码图片
public ResponseEntity<ResultMsg> nativePay(PayOrder order) {
log.info("nativePay");
/*根据规则生成订单号*/
String outTradeNo = "DD"+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmmss"))+String.format("%04d", new Random().nextInt(10000));
order.setOutTradeNo(outTradeNo);
order.setTotalAmount(accRespGuar.getPayable());
order.setStatus(String.valueOf(Transaction.TradeStateEnum.NOTPAY));
orderService.save(order);
PrepayRequest request = new PrepayRequest();
Amount amount = new Amount();
BigDecimal payMoney = order.getTotalAmount();
BigDecimal amountTotal = payMoney.multiply(new BigDecimal("100").setScale(0, RoundingMode.DOWN));
amount.setTotal(amountTotal.intValue());
request.setAmount(amount);
request.setTimeExpire(getExpiredTimeStr());
request.setAppid(appId);
request.setMchid(merchantId);
request.setAttach(order.getOrderId());
request.setDescription(order.getPayContent());
request.setNotifyUrl(properties.getPayNotifyUrl());
//这里生成流水号,后续用这个流水号与微信交互,查询订单状态
request.setOutTradeNo(order.getOutTradeNo());
PrepayResponse response;
try {
response = nativePayService.prepay(request);
} catch (HttpException e) {
log.error("微信下单发送HTTP请求失败,错误信息:{}", e.getHttpRequest());
throw new MsgException("微信下单发送HTTP请求失败,"+e.getMessage(),ServiceEnum.acgp );
} catch (ServiceException e) {
// 服务返回状态小于200或大于等于300,例如500
log.error("微信下单服务状态错误,错误信息:{}", e.getErrorMessage());
throw new MsgException("微信下单服务状态错误,"+e.getMessage(), ServiceEnum.acgp);
} catch (MalformedMessageException e) {
// 服务返回成功,返回体类型不合法,或者解析返回体失败
log.error("服务返回成功,返回体类型不合法,或者解析返回体失败,错误信息:{}", e.getMessage());
throw new MsgException("服务返回成功,返回体类型不合法,或者解析返回体失败"+ e.getMessage(),ServiceEnum.acgp );
}
log.info("nativePay end");
order.setCodeUrl(response.getCodeUrl());
return ReturnUtils.NOSuccess(order);
}
5、小程序支付
public ResponseEntity<ResultMsg> wxAppPay(PayOrder order, SecurityToken token) throws Exception {
log.info("wxAppPay");
/*根据规则生成订单号*/
String outTradeNo = "DD"+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmmss"))+String.format("%04d", new Random().nextInt(10000));
order.setOutTradeNo(outTradeNo);
order.setTotalAmount(accRespGuar.getPayable());
order.setStatus(String.valueOf(Transaction.TradeStateEnum.NOTPAY));
orderService.save(order);
com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest request = new com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest();
com.wechat.pay.java.service.payments.jsapi.model.Amount amount = new com.wechat.pay.java.service.payments.jsapi.model.Amount();
BigDecimal payMoney = order.getTotalAmount();
BigDecimal amountTotal = payMoney.multiply(new BigDecimal("100").setScale(0, RoundingMode.DOWN));
amount.setTotal(amountTotal.intValue());
request.setAmount(amount);
request.setTimeExpire(getExpiredTimeStr());
request.setAppid(appId);
request.setMchid(merchantId);
request.setAttach(order.getOrderId());
request.setDescription(order.getPayContent());
request.setNotifyUrl(properties.getPayNotifyUrl());
//这里生成流水号,后续用这个流水号与微信交互,查询订单状态
request.setOutTradeNo(order.getOutTradeNo());
Payer payer = new Payer();
payer.setOpenid(token.getOpenid());
request.setPayer(payer);
com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse response;
try {
response = jsapiService.prepay(request);
} catch (HttpException e) {
log.error("微信下单发送HTTP请求失败,错误信息:{}", e.getHttpRequest());
throw new MsgException("微信下单发送HTTP请求失败,"+e.getMessage(),ServiceEnum.acgp );
} catch (ServiceException e) {
// 服务返回状态小于200或大于等于300,例如500
log.error("微信下单服务状态错误,错误信息:{}", e.getErrorMessage());
throw new MsgException("微信下单服务状态错误,"+e.getMessage(), ServiceEnum.acgp);
} catch (MalformedMessageException e) {
// 服务返回成功,返回体类型不合法,或者解析返回体失败
log.error("服务返回成功,返回体类型不合法,或者解析返回体失败,错误信息:{}", e.getMessage());
throw new MsgException("服务返回成功,返回体类型不合法,或者解析返回体失败"+ e.getMessage(),ServiceEnum.acgp );
}
log.info("wxAppPay end");
// 需要的全部请求参数
String prepayId = response.getPrepayId();//下单返回的prepayId
long timestamp = Instant.now().getEpochSecond();
String nonceStr = NonceUtil.createNonce(32);;//随机字符串
String packageVal = "prepay_id=" + prepayId;
String message =
appId + "\n" + timestamp + "\n" + nonceStr + "\n" + packageVal + "\n";
log.debug("Message for RequestPayment signatures is[{}]", message);
String sign = signer.sign(message).getSign();
order.setPrepayId(packageVal);
order.setSignType("RSA");
order.setPaySign(sign);
order.setNonceStr(nonceStr);
order.setTimeStamp(String.valueOf(timestamp));
return ReturnUtils.NOSuccess(order);
}
6、支付回调,本地调试可以使用内网穿透工具
public Object payCallBack(HttpServletRequest request) throws IOException {
Map<String,String> resultMap = new HashMap<>();
String wechatPaySerial = request.getHeader("Wechatpay-Serial");
String wechatpayNonce = request.getHeader("Wechatpay-Nonce");
String wechatSignature = request.getHeader("WechatPay-Signature");
String wechatTimestamp = request.getHeader("Wechatpay-Timestamp");
StringBuilder requestBody = new StringBuilder();
BufferedReader reader = request.getReader();
String line;
while ((line = reader.readLine()) != null) {
requestBody.append(line);
}
// 构造 RequestParam
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(wechatPaySerial)
.nonce(wechatpayNonce)
.signature(wechatSignature)
.timestamp(wechatTimestamp)
.body(requestBody.toString())
.build();
try {
// 以支付通知回调为例,验签、解密并转换成 Transaction
Transaction transaction = notificationParser.parse(requestParam, Transaction.class);
/*更新订单状态*/
orderService.update(new UpdateWrapper<PayOrder>().set("status",transaction.getTradeState()).eq("out_trade_no",transaction.getOutTradeNo()));
PayPaymentRecord paymentRecord = new PayPaymentRecord();
String paymentNo = "ZF"+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmmss"))+String.format("%04d", new Random().nextInt(10000));
PayOrder payOrder = orderService.getOne(new LambdaQueryWrapper<PayOrder>().eq(PayOrder::getOutTradeNo, transaction.getOutTradeNo()));
paymentRecord.setOrderId(payOrder.getOrderId());
paymentRecord.setPaymentStatus(Transaction.TradeStateEnum.SUCCESS.toString());
paymentRecord.setPayerTotal(new BigDecimal(transaction.getAmount().getPayerTotal()).divide(BigDecimalUtils.NUM_100));
paymentRecord.setPaymentTime(transaction.getSuccessTime());
paymentRecord.setPaymentNo(paymentNo);
paymentRecordService.save(paymentRecord);
/*处理业务*/
} catch (ValidationException e) {
// 签名验证失败,返回 401 UNAUTHORIZED 状态码
log.error("签名验证失败 sign verification failed", e);
resultMap.put("code","ERROR");
resultMap.put("message",e.getMessage());
return resultMap;
}
/* // 如果处理失败,应返回 4xx/5xx 的状态码,例如 500 INTERNAL_SERVER_ERROR
if (*//* process error *//*) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
}*/
resultMap.put("code","SUCCESS");
resultMap.put("message","");
return resultMap;
}
7、官方api地址
JSAPI下单 - JSAPI支付 | 微信支付商户文档中心
GitHub - wechatpay-apiv3/wechatpay-java: 微信支付 APIv3 的官方 Java Library