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 * @author xiaochen * @date 2022-03-22 12:47 */ public Map 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.getV3().getNotifyPayUrl(); 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("获取支付数据错误!"); } } public Map jsApiInsurance(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.getV3().getInsuranceNotifyPayUrl(); 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 // * @author xiaochen // * @date 2022-03-22 12:47 // */ // public Map 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 verifyNotify(HttpServletRequest request, TypeReference valueTypeRef) throws Exception { return verifyNotify(request,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 * @author xiaochen */ @Override public Map refund(WxPaymentRefundModel refundModel) { refundModel.setNotify_url(this.config.getV3().getNotifyRefundUrl()); 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); } }