package com.ruoyi.system.wxPay.utils;
|
|
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.ruoyi.system.utils.wx.tools.WxUtils;
|
import com.ruoyi.system.wxPay.model.WeixinPayProperties;
|
import com.ruoyi.system.wxPay.model.WxCloseOrderModel;
|
import com.ruoyi.system.wxPay.model.WxPaymentRefundModel;
|
import com.ruoyi.system.wxPay.resp.NotifyV3PayDecodeRespBody;
|
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
|
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
|
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
|
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
|
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
|
import lombok.Getter;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
|
import javax.servlet.http.HttpServletRequest;
|
import java.security.PrivateKey;
|
import java.util.Map;
|
|
/**
|
* @author xiaochen
|
* @ClassName WxWifiV3Pay
|
* @Description
|
* @date 2021-11-13 21:10
|
*/
|
@Slf4j
|
public class WxV3Pay extends WxAbstractPay {
|
@Getter
|
private WeixinPayProperties config;
|
@Getter
|
private Verifier verifier;
|
private WechatPayHttpClientBuilder builder;
|
@Getter
|
private CloseableHttpClient httpClient;
|
private PrivateKeySigner privateKeySigner;
|
private WechatPay2Validator validator;
|
@Getter
|
private PrivateKey privateKey;
|
|
/**
|
* 初始化
|
*
|
* @param config
|
*/
|
public WxV3Pay(WeixinPayProperties config) {
|
// 检查v3支付配置信息
|
if (WxUtils.getLogger().isDebugEnabled()) {
|
WxUtils.debug("开始检查v3支付配置信息....");
|
}
|
try {
|
this.config = config;
|
checkWxConfig();
|
this.privateKey = PemUtil.loadPrivateKey(config.getV3().getPrivateKeyStream());
|
this.privateKeySigner = new PrivateKeySigner(config.getV3().getMchSerialNo(), privateKey);
|
this.validator = new WechatPay2Validator(verifier);
|
this.builder = WechatPayHttpClientBuilder.create()
|
.withMerchant(config.getMchId(), config.getV3().getMchSerialNo(), this.privateKey)
|
.withValidator(this.validator);
|
this.httpClient = this.builder.build();
|
} catch (Exception e) {
|
// 打印异常信息
|
e.printStackTrace();
|
WxUtils.warn("检查v3支付配置信息出现错误,直连商户商户号:{},{}", config.getMchId(), e.getMessage());
|
return;
|
}
|
if (WxUtils.getLogger().isDebugEnabled()) {
|
WxUtils.debug("检查v3支付配置信息完成,未出现异常....");
|
}
|
}
|
|
/**
|
* 检查支付配置信息
|
*
|
* @throws Exception
|
*/
|
private void checkWxConfig() throws Exception {
|
if (this.config == null) {
|
throw new Exception("config is null");
|
}
|
if (config.getMchId() == null || config.getMchId().trim().length() == 0) {
|
throw new Exception("MchID in config is empty");
|
}
|
if (config.getV3().getMchSerialNo() == null) {
|
throw new Exception("mchSerialNo in config is empty");
|
}
|
if (config.getV3().getPrivateKeyStream() == null) {
|
throw new Exception("cert stream in config is empty");
|
}
|
if (this.config.getHttpConnectTimeoutMs() < 10) {
|
throw new Exception("http connect timeout is too small");
|
}
|
if (this.config.getHttpReadTimeoutMs() < 10) {
|
throw new Exception("http read timeout is too small");
|
}
|
}
|
|
/**
|
* jsApi下单
|
*
|
* @param tradeNo 订单号
|
* @param amount 金额 分
|
* @param openid openid
|
* @param description 订单描述
|
* @return java.util.Map<java.lang.String, java.lang.Object>
|
* @author xiaochen
|
* @date 2022-03-22 12:47
|
*/
|
public Map<String, Object> jsApi(String tradeNo, Long amount, String openid, String description,String attach) {
|
JsapiPrepay client = new JsapiPrepay(
|
this.config.getMchId(), // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756
|
this.config.getV3().getMchSerialNo(), // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053
|
this.config.getV3().getPrivateKeyPath(), // 商户API证书私钥文件路径,本地文件路径
|
this.config.getV3().getPublicKeyId(), // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816
|
this.config.getV3().getPublicKeyPath() // 微信支付公钥文件路径,本地文件路径
|
);
|
JsapiPrepay.DirectAPIv3JsapiPrepayRequest request = new JsapiPrepay.DirectAPIv3JsapiPrepayRequest();
|
request.appid = this.config.getAppId();
|
request.mchid = this.config.getMchId();
|
request.description = description;
|
request.outTradeNo = tradeNo;
|
// request.timeExpire = "2018-06-08T10:34:56+08:00";
|
request.attach = attach;
|
request.notifyUrl = this.config.getCallBackUrl();
|
request.goodsTag = "WXG";
|
request.supportFapiao = false;
|
request.amount = new JsapiPrepay.CommonAmountInfo();
|
request.amount.total = amount;
|
request.amount.currency = "CNY";
|
request.payer = new JsapiPrepay.JsapiReqPayerInfo();
|
request.payer.openid = openid;
|
try {
|
JsapiPrepay.DirectAPIv3JsapiPrepayResponse response = client.run(request);
|
return wxTuneUp(this.privateKeySigner, this.config.getAppId(), response.prepayId);
|
} catch (WXPayUtility.ApiException e) {
|
throw new RuntimeException("获取支付数据错误!");
|
}
|
}
|
// /**
|
// * jsApi下单
|
// *
|
// * @param tradeNo 订单号
|
// * @param amount 金额 分
|
// * @param openid openid
|
// * @param description 订单描述
|
// * @return java.util.Map<java.lang.String, java.lang.Object>
|
// * @author xiaochen
|
// * @date 2022-03-22 12:47
|
// */
|
// public Map<String, Object> jsApi(String tradeNo, Integer amount, String openid, String description) {
|
// WxPaymentInfoModel requestBody = WxPaymentInfoModel.builder()
|
// .mchid(this.config.getMchId())
|
// .appid(this.config.getAppId())
|
// .description(description)
|
// .out_trade_no(tradeNo)
|
//// .attach("")
|
// .amount(WxPaymentInfoModel.Amount.builder().total(amount).build())
|
// .payer(WxPaymentInfoModel.Payer.builder().openid(openid).build())
|
// .build();
|
// // 封装基础数据
|
// String reqBody = buildBaseParam(requestBody
|
// , this.config.getV3().getNotifyPayUrl());
|
// //请求URL
|
// HttpEntityEnclosingRequestBase httpPost = requestPost(
|
// "/v3/pay/transactions/jsapi"
|
// , this.config.getHttpReadTimeoutMs()
|
// , this.config.getHttpConnectTimeoutMs()
|
// , reqBody);
|
// String repBody = result(httpClient, httpPost);
|
// ObjectMapper om = new ObjectMapper();
|
// try {
|
// JsonNode rootNode = om.readTree(repBody);
|
// String prepayId = rootNode.path("prepay_id").asText();
|
// return wxTuneUp(this.privateKeySigner, requestBody.getAppid(), prepayId);
|
// } catch (JsonProcessingException e) {
|
// throw new RuntimeException("获取支付数据错误!");
|
// }
|
// }
|
|
|
/**
|
* 接收回调
|
*
|
* @param request
|
* @return
|
* @throws Exception
|
*/
|
@Override
|
public <T> T verifyNotify(HttpServletRequest request, TypeReference<T> valueTypeRef) throws Exception {
|
return verifyNotify(request, this.verifier, this.config.getV3().getApiKey(), valueTypeRef);
|
}
|
|
/**
|
* 订单查询
|
*
|
* @param out_trade_no
|
* @return com.abl.biz.center.payment.wx.v3.NotifyV3PayDecodeRespBody
|
* @author xiaochen
|
* @date 2021-12-20 16:47
|
*/
|
@Override
|
public NotifyV3PayDecodeRespBody query(String out_trade_no) {
|
String url =
|
String.format("/v3/pay/transactions/out-trade-no/%s", out_trade_no) + String.format("?mchid=%s", this.getConfig().getMchId());
|
return query(this.httpClient, this.config.getHttpReadTimeoutMs(), this.config.getHttpConnectTimeoutMs(), url);
|
}
|
|
/**
|
* 退款
|
*
|
* @param refundModel
|
* @return java.util.Map<java.lang.String, java.lang.Object>
|
* @author xiaochen
|
*/
|
@Override
|
public Map<String, Object> refund(WxPaymentRefundModel refundModel) {
|
refundModel.setNotify_url(this.config.getV3().getNotifyRefundUrl() + refundModel.getNotify_url());
|
return refund(this.httpClient, "/v3/refund/domestic/refunds", this.config.getHttpReadTimeoutMs(), this.config.getHttpConnectTimeoutMs(), refundModel);
|
}
|
|
|
/**
|
* 关闭订单
|
* @param out_trade_no
|
* @return
|
*/
|
@Override
|
public String close(String out_trade_no) {
|
String uri = String.format("/v3/pay/transactions/out-trade-no/%s/close", out_trade_no);
|
WxCloseOrderModel wxCloseOrderModel = new WxCloseOrderModel();
|
wxCloseOrderModel.setMchid(this.config.getMchId());
|
return close(this.httpClient, uri, this.config.getHttpReadTimeoutMs(), this.config.getHttpConnectTimeoutMs(), wxCloseOrderModel);
|
}
|
}
|