New file |
| | |
| | | package com.ruoyi.system.wxPay.utils; |
| | | |
| | | import com.fasterxml.jackson.core.JsonProcessingException; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.JsonNode; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | 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.WxPaymentInfoModel; |
| | | 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.WechatPay2Credentials; |
| | | import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator; |
| | | import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager; |
| | | import com.wechat.pay.contrib.apache.httpclient.util.PemUtil; |
| | | import lombok.Getter; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; |
| | | import org.apache.http.impl.client.CloseableHttpClient; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.nio.charset.StandardCharsets; |
| | | 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); |
| | | // 获取证书管理器实例 |
| | | CertificatesManager certificatesManager = CertificatesManager.getInstance(); |
| | | // 向证书管理器增加需要自动更新平台证书的商户信息 |
| | | certificatesManager.putMerchant(config.getMchId(), new WechatPay2Credentials(config.getMchId(), |
| | | this.privateKeySigner), config.getV3().getApiKey().getBytes(StandardCharsets.UTF_8)); |
| | | // 从证书管理器中获取verifier |
| | | this.verifier = certificatesManager.getVerifier(config.getMchId()); |
| | | 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, 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()) |
| | | // 分不分账 |
| | | // .settle_info(WxPaymentInfoModel.SettleInfo.builder().profit_sharing(true).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); |
| | | } |
| | | } |