package cn.lili.modules.payment.kit.plugin.wechat; import cn.hutool.core.net.URLDecoder; import cn.hutool.core.net.URLEncoder; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import cn.lili.cache.Cache; import cn.lili.cache.CachePrefix; import cn.lili.common.enums.ResultCode; import cn.lili.common.enums.ResultUtil; import cn.lili.common.exception.ServiceException; import cn.lili.common.properties.ApiProperties; import cn.lili.common.security.context.UserContext; import cn.lili.common.utils.CurrencyUtil; import cn.lili.common.utils.SnowFlake; import cn.lili.common.utils.StringUtils; import cn.lili.common.vo.ResultMessage; import cn.lili.modules.connect.entity.Connect; import cn.lili.modules.connect.entity.enums.SourceEnum; import cn.lili.modules.connect.service.ConnectService; import cn.lili.modules.member.entity.dto.ConnectQueryDTO; import cn.lili.modules.order.order.entity.dos.Order; import cn.lili.modules.order.order.service.OrderService; import cn.lili.modules.payment.entity.RefundLog; import cn.lili.modules.payment.entity.enums.PaymentMethodEnum; import cn.lili.modules.payment.kit.CashierSupport; import cn.lili.modules.payment.kit.Payment; import cn.lili.modules.payment.kit.core.PaymentHttpResponse; import cn.lili.modules.payment.kit.core.enums.RequestMethodEnums; import cn.lili.modules.payment.kit.core.enums.SignType; import cn.lili.modules.payment.kit.core.kit.*; import cn.lili.modules.payment.kit.core.utils.DateTimeZoneUtil; import cn.lili.modules.payment.kit.dto.PayParam; import cn.lili.modules.payment.kit.dto.PaymentSuccessParams; import cn.lili.modules.payment.kit.params.dto.CashierParam; import cn.lili.modules.payment.kit.plugin.wechat.enums.WechatApiEnum; import cn.lili.modules.payment.kit.plugin.wechat.enums.WechatDomain; import cn.lili.modules.payment.kit.plugin.wechat.model.*; import cn.lili.modules.payment.service.PaymentService; import cn.lili.modules.payment.service.RefundLogService; import cn.lili.modules.system.entity.dos.Setting; import cn.lili.modules.system.entity.dto.WithdrawalSetting; import cn.lili.modules.system.entity.dto.connect.WechatConnectSetting; import cn.lili.modules.system.entity.dto.connect.dto.WechatConnectSettingItem; import cn.lili.modules.system.entity.dto.payment.WechatPaymentSetting; import cn.lili.modules.system.entity.enums.SettingEnum; import cn.lili.modules.system.service.SettingService; import cn.lili.modules.wallet.entity.dos.MemberWithdrawApply; import cn.lili.modules.wallet.entity.dto.TransferResultDTO; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.google.gson.Gson; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; /** * 微信支付 * * @author Chopper * @since 2020/12/21 17:44 */ @Slf4j @Component public class WechatPlugin implements Payment { /** * 收银台 */ @Autowired private CashierSupport cashierSupport; /** * 支付日志 */ @Autowired private PaymentService paymentService; /** * 缓存 */ @Autowired private Cache cache; /** * 退款日志 */ @Autowired private RefundLogService refundLogService; /** * 配置 */ @Autowired private SettingService settingService; /** * 联合登陆 */ @Autowired private ConnectService connectService; /** * 联合登陆 */ @Autowired private OrderService orderService; @Override public ResultMessage h5pay(HttpServletRequest request, HttpServletResponse response1, PayParam payParam) { try { CashierParam cashierParam = cashierSupport.cashierParam(payParam); //支付参数准备 SceneInfo sceneInfo = new SceneInfo(); sceneInfo.setPayer_client_ip(IpKit.getRealIp(request)); H5Info h5Info = new H5Info(); h5Info.setType("WAP"); sceneInfo.setH5_info(h5Info); //支付金额 Integer fen = CurrencyUtil.fen(cashierParam.getPrice()); //第三方付款订单 String outOrderNo = SnowFlake.getIdStr(); //过期时间 String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3); //回传数据 String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8); WechatPaymentSetting setting = wechatPaymentSetting(); String appid = setting.getH5AppId(); if (appid == null) { throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING); } UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel() .setAppid(appid) .setMchid(setting.getMchId()) .setDescription(cashierParam.getDetail()) .setOut_trade_no(outOrderNo) .setTime_expire(timeExpire) .setAttach(attach) .setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(),PaymentMethodEnum.WECHAT)) .setAmount(new Amount().setTotal(fen)).setScene_info(sceneInfo); log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel)); PaymentHttpResponse response = WechatApi.v3( RequestMethodEnums.POST, WechatDomain.CHINA.toString(), WechatApiEnum.H5_PAY.toString(), setting.getMchId(), setting.getSerialNumber(), null, setting.getApiclient_key(), JSONUtil.toJsonStr(unifiedOrderModel) ); updateOrderPayNo(payParam,outOrderNo); return ResultUtil.data(JSONUtil.toJsonStr(response.getBody())); } catch (Exception e) { log.error("微信H5支付错误", e); throw new ServiceException(ResultCode.PAY_ERROR); } } @Override public ResultMessage jsApiPay(HttpServletRequest request, PayParam payParam) { try { Connect connect = connectService.queryConnect( ConnectQueryDTO.builder().userId(UserContext.getCurrentUser().getId()).unionType(SourceEnum.WECHAT_OFFIACCOUNT_OPEN_ID.name()).build() ); if (connect == null) { return null; } Payer payer = new Payer(); payer.setOpenid(connect.getUnionId()); CashierParam cashierParam = cashierSupport.cashierParam(payParam); //支付金额 Integer fen = CurrencyUtil.fen(cashierParam.getPrice()); //第三方付款订单 String outOrderNo = SnowFlake.getIdStr(); //过期时间 String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3); String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8); WechatPaymentSetting setting = wechatPaymentSetting(); String appid = setting.getJsapiAppId(); if (appid == null) { throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING); } UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel() .setAppid(appid) .setMchid(setting.getMchId()) .setDescription(cashierParam.getDetail()) .setOut_trade_no(outOrderNo) .setTime_expire(timeExpire) .setAttach(attach) .setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT)) .setAmount(new Amount().setTotal(fen)) .setPayer(payer); log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel)); PaymentHttpResponse response = WechatApi.v3( RequestMethodEnums.POST, WechatDomain.CHINA.toString(), WechatApiEnum.JS_API_PAY.toString(), setting.getMchId(), setting.getSerialNumber(), null, setting.getApiclient_key(), JSONUtil.toJsonStr(unifiedOrderModel) ); //根据证书序列号查询对应的证书来验证签名结果 boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert()); log.info("verifySignature: {}", verifySignature); log.info("统一下单响应 {}", response); if (verifySignature) { String body = response.getBody(); JSONObject jsonObject = JSONUtil.parseObj(body); String prepayId = jsonObject.getStr("prepay_id"); Map map = WxPayKit.jsApiCreateSign(appid, prepayId, setting.getApiclient_key()); log.info("唤起支付参数:{}", map); updateOrderPayNo(payParam,outOrderNo); return ResultUtil.data(map); } log.error("微信支付参数验证错误,请及时处理"); throw new ServiceException(ResultCode.PAY_ERROR); } catch (Exception e) { log.error("支付异常", e); throw new ServiceException(ResultCode.PAY_ERROR); } } @Override public ResultMessage appPay(HttpServletRequest request, PayParam payParam) { try { CashierParam cashierParam = cashierSupport.cashierParam(payParam); //支付金额 Integer fen = CurrencyUtil.fen(cashierParam.getPrice()); //第三方付款订单 String outOrderNo = SnowFlake.getIdStr(); //过期时间 String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3); String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8); WechatPaymentSetting setting = wechatPaymentSetting(); String appid = setting.getJsapiAppId(); if (appid == null) { throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING); } UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel() .setAppid(appid) .setMchid(setting.getMchId()) .setDescription(cashierParam.getDetail()) .setOut_trade_no(outOrderNo) .setTime_expire(timeExpire) .setAttach(attach) .setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT)) .setAmount(new Amount().setTotal(fen)); log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel)); PaymentHttpResponse response = WechatApi.v3( RequestMethodEnums.POST, WechatDomain.CHINA.toString(), WechatApiEnum.APP_PAY.toString(), setting.getMchId(), setting.getSerialNumber(), null, setting.getApiclient_key(), JSONUtil.toJsonStr(unifiedOrderModel) ); //根据证书序列号查询对应的证书来验证签名结果 boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert()); log.info("verifySignature: {}", verifySignature); log.info("统一下单响应 {}", response); if (verifySignature) { JSONObject jsonObject = JSONUtil.parseObj(response.getBody()); String prepayId = jsonObject.getStr("prepay_id"); Map map = WxPayKit.appPrepayIdCreateSign(appid, setting.getMchId(), prepayId, setting.getApiclient_key(), SignType.MD5); log.info("唤起支付参数:{}", map); updateOrderPayNo(payParam,outOrderNo); return ResultUtil.data(map); } log.error("微信支付参数验证错误,请及时处理"); throw new ServiceException(ResultCode.PAY_ERROR); } catch (Exception e) { log.error("支付异常", e); throw new ServiceException(ResultCode.PAY_ERROR); } } @Override public ResultMessage nativePay(HttpServletRequest request, PayParam payParam) { try { CashierParam cashierParam = cashierSupport.cashierParam(payParam); //支付金额 Integer fen = CurrencyUtil.fen(cashierParam.getPrice()); //第三方付款订单 String outOrderNo = SnowFlake.getIdStr(); //过期时间 String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3); String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8); WechatPaymentSetting setting = wechatPaymentSetting(); String appid = setting.getNativeAppId(); if (appid == null) { throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING); } UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel() .setAppid(appid) .setMchid(setting.getMchId()) .setDescription(cashierParam.getDetail()) .setOut_trade_no(outOrderNo) .setTime_expire(timeExpire) //回传参数 .setAttach(attach) .setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT)) .setAmount(new Amount().setTotal(fen)); log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel)); PaymentHttpResponse response = WechatApi.v3( RequestMethodEnums.POST, WechatDomain.CHINA.toString(), WechatApiEnum.NATIVE_PAY.toString(), setting.getMchId(), setting.getSerialNumber(), null, setting.getApiclient_key(), JSONUtil.toJsonStr(unifiedOrderModel) ); log.info("统一下单响应 {}", response); //根据证书序列号查询对应的证书来验证签名结果 boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert()); log.info("verifySignature: {}", verifySignature); if (verifySignature) { updateOrderPayNo(payParam,outOrderNo); return ResultUtil.data(new JSONObject(response.getBody()).getStr("code_url")); } else { log.error("微信支付参数验证错误,请及时处理"); throw new ServiceException(ResultCode.PAY_ERROR); } } catch (ServiceException e) { log.error("支付异常", e); throw new ServiceException(ResultCode.PAY_ERROR); } catch (Exception e) { log.error("支付异常", e); throw new ServiceException(ResultCode.PAY_ERROR); } } @Override public ResultMessage mpPay(HttpServletRequest request, PayParam payParam) { try { Connect connect = connectService.queryConnect( ConnectQueryDTO.builder().userId(UserContext.getCurrentUser().getId()).unionType(SourceEnum.WECHAT_MP_OPEN_ID.name()).build() ); if (connect == null) { return null; } Payer payer = new Payer(); payer.setOpenid(connect.getUnionId()); CashierParam cashierParam = cashierSupport.cashierParam(payParam); //支付金额 Integer fen = CurrencyUtil.fen(cashierParam.getPrice()); //第三方付款订单 String outOrderNo = SnowFlake.getIdStr(); //过期时间 String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3); //微信小程序,appid 需要单独获取,这里读取了联合登陆配置的appid ,实际场景小程序自动登录,所以这个appid是最为保险的做法 //如果有2开需求,这里需要调整,修改这个appid的获取途径即可 String appid = wechatPaymentSetting().getMpAppId(); if (StringUtils.isEmpty(appid)) { throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING); } String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8); WechatPaymentSetting setting = wechatPaymentSetting(); UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel() .setAppid(appid) .setMchid(setting.getMchId()) .setDescription(cashierParam.getDetail()) .setOut_trade_no(outOrderNo) .setTime_expire(timeExpire) .setAttach(attach) .setNotify_url(notifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT)) .setAmount(new Amount().setTotal(fen)) .setPayer(payer); log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel)); PaymentHttpResponse response = WechatApi.v3( RequestMethodEnums.POST, WechatDomain.CHINA.toString(), WechatApiEnum.JS_API_PAY.toString(), setting.getMchId(), setting.getSerialNumber(), null, setting.getApiclient_key(), JSONUtil.toJsonStr(unifiedOrderModel) ); //根据证书序列号查询对应的证书来验证签名结果 boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert()); log.info("verifySignature: {}", verifySignature); log.info("统一下单响应 {}", response); if (verifySignature) { String body = response.getBody(); JSONObject jsonObject = JSONUtil.parseObj(body); String prepayId = jsonObject.getStr("prepay_id"); Map map = WxPayKit.jsApiCreateSign(appid, prepayId, setting.getApiclient_key()); log.info("唤起支付参数:{}", map); updateOrderPayNo(payParam,outOrderNo); return ResultUtil.data(map); } log.error("微信支付参数验证错误,请及时处理"); throw new ServiceException(ResultCode.PAY_ERROR); } catch (Exception e) { log.error("支付异常", e); throw new ServiceException(ResultCode.PAY_ERROR); } } @Override public void callBack(HttpServletRequest request) { try { verifyNotify(request); } catch (Exception e) { log.error("支付异常", e); } } @Override public void notify(HttpServletRequest request) { try { verifyNotify(request); } catch (Exception e) { log.error("支付异常", e); } } /** * 微信提现 * 文档地址:https://pay.weixin.qq.com/docs/merchant/apis/batch-transfer-to-balance/transfer-batch/initiate-batch-transfer.html * * @param memberWithdrawApply 会员提现申请 */ @Override public TransferResultDTO transfer(MemberWithdrawApply memberWithdrawApply) { try { //获取提现设置 WithdrawalSetting withdrawalSetting = new Gson().fromJson(settingService.get(SettingEnum.WITHDRAWAL_SETTING.name()).getSettingValue(), WithdrawalSetting.class); //获取用户OPENID WechatConnectSetting wechatConnectSetting = new Gson().fromJson(settingService.get(SettingEnum.WECHAT_CONNECT.name()).getSettingValue() , WechatConnectSetting.class); String source = ""; for (WechatConnectSettingItem wechatConnectSettingItem : wechatConnectSetting.getWechatConnectSettingItems()) { if (wechatConnectSettingItem.getAppId().equals(withdrawalSetting.getWechatAppId())) { switch (wechatConnectSettingItem.getClientType()) { case "PC": source = SourceEnum.WECHAT_PC_OPEN_ID.name(); break; case "H5": source = SourceEnum.WECHAT_OFFIACCOUNT_OPEN_ID.name(); break; case "MP": source = SourceEnum.WECHAT_MP_OPEN_ID.name(); break; case "APP": source = SourceEnum.WECHAT_APP_OPEN_ID.name(); break; } } } //获取微信设置 WechatPaymentSetting wechatPaymentSetting = wechatPaymentSetting(); //获取用户openId Connect connect = connectService.queryConnect( ConnectQueryDTO.builder().userId(memberWithdrawApply.getMemberId()) .unionType(source).build() ); //构建提现,发起申请 TransferModel transferModel = new TransferModel() .setAppid(withdrawalSetting.getWechatAppId()) .setOut_batch_no(SnowFlake.createStr("T")) .setBatch_name("用户提现") .setBatch_remark("用户提现") .setTotal_amount(CurrencyUtil.fen(memberWithdrawApply.getApplyMoney())) .setTotal_num(1) .setTransfer_scene_id("1000"); List transferDetailListList = new ArrayList<>(); { TransferDetailInput transferDetailInput = new TransferDetailInput(); transferDetailInput.setOut_detail_no(SnowFlake.createStr("TD")); transferDetailInput.setTransfer_amount(CurrencyUtil.fen(memberWithdrawApply.getApplyMoney())); transferDetailInput.setTransfer_remark("用户提现"); transferDetailInput.setOpenid(connect.getUnionId()); transferDetailListList.add(transferDetailInput); } transferModel.setTransfer_detail_list(transferDetailListList); PaymentHttpResponse response = WechatApi.v3( RequestMethodEnums.POST, WechatDomain.CHINA.toString(), WechatApiEnum.TRANSFER_BATCHES.toString(), wechatPaymentSetting.getMchId(), wechatPaymentSetting.getSerialNumber(), null, wechatPaymentSetting.getApiclient_key(), JSONUtil.toJsonStr(transferModel) ); log.info("微信提现响应 {}", response); String body = response.getBody(); JSONObject jsonObject = JSONUtil.parseObj(body); return TransferResultDTO.builder().result(jsonObject.getStr("batch_id") != null).response(body).build(); //根据自身业务进行接下来的任务处理 } catch (Exception e) { e.printStackTrace(); return TransferResultDTO.builder().result(false).response(e.getMessage()).build(); } } /** * 验证结果,执行支付回调 * * @param request * @throws Exception */ private void verifyNotify(HttpServletRequest request) throws Exception { String timestamp = request.getHeader("Wechatpay-Timestamp"); String nonce = request.getHeader("Wechatpay-Nonce"); String serialNo = request.getHeader("Wechatpay-Serial"); String signature = request.getHeader("Wechatpay-Signature"); log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature); String result = HttpKit.readData(request); log.info("微信支付通知密文 {}", result); WechatPaymentSetting setting = wechatPaymentSetting(); //校验服务器端响应¬ String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp, setting.getApiKey3(), Objects.requireNonNull(getPlatformCert())); log.info("微信支付通知明文 {}", plainText); JSONObject jsonObject = JSONUtil.parseObj(plainText); String payParamStr = jsonObject.getStr("attach"); String payParamJson = URLDecoder.decode(payParamStr, StandardCharsets.UTF_8); PayParam payParam = JSONUtil.toBean(payParamJson, PayParam.class); String tradeNo = jsonObject.getStr("transaction_id"); Double totalAmount = CurrencyUtil.reversalFen(jsonObject.getJSONObject("amount").getDouble("total")); PaymentSuccessParams paymentSuccessParams = new PaymentSuccessParams( PaymentMethodEnum.WECHAT.name(), tradeNo, totalAmount, payParam ); paymentService.success(paymentSuccessParams); log.info("微信支付回调:支付成功{}", plainText); } @Override public void refund(RefundLog refundLog) { try { Amount amount = new Amount().setRefund(CurrencyUtil.fen(refundLog.getTotalAmount())) .setTotal(CurrencyUtil.fen(orderService.getPaymentTotal(refundLog.getOrderSn()))); //退款参数准备 RefundModel refundModel = new RefundModel() .setTransaction_id(refundLog.getPaymentReceivableNo()) .setOut_refund_no(refundLog.getOutOrderNo()) .setReason(refundLog.getRefundReason()) .setAmount(amount) .setNotify_url(refundNotifyUrl(wechatPaymentSetting().getCallbackUrl(), PaymentMethodEnum.WECHAT)); WechatPaymentSetting setting = wechatPaymentSetting(); log.info("微信退款参数 {}", JSONUtil.toJsonStr(refundModel)); PaymentHttpResponse response = WechatApi.v3( RequestMethodEnums.POST, WechatDomain.CHINA.toString(), WechatApiEnum.DOMESTIC_REFUNDS.toString(), setting.getMchId(), setting.getSerialNumber(), null, setting.getApiclient_key(), JSONUtil.toJsonStr(refundModel) ); log.info("微信退款响应 {}", response); //退款申请成功 if (response.getStatus() == 200) { refundLogService.save(refundLog); } else { //退款申请失败 refundLog.setErrorMessage(response.getBody()); refundLogService.save(refundLog); } } catch (Exception e) { log.error("微信退款申请失败", e); } } @Override public void refundNotify(HttpServletRequest request) { String timestamp = request.getHeader("Wechatpay-Timestamp"); String nonce = request.getHeader("Wechatpay-Nonce"); String serialNo = request.getHeader("Wechatpay-Serial"); String signature = request.getHeader("Wechatpay-Signature"); log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature); String result = HttpKit.readData(request); log.info("微信退款通知密文 {}", result); JSONObject ciphertext = JSONUtil.parseObj(result); try { //校验服务器端响应¬ String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp, wechatPaymentSetting().getApiKey3(), Objects.requireNonNull(getPlatformCert())); log.info("微信退款通知明文 {}", plainText); if (("REFUND.SUCCESS").equals(ciphertext.getStr("event_type"))) { log.info("退款成功 {}", plainText); //校验服务器端响应 JSONObject jsonObject = JSONUtil.parseObj(plainText); String transactionId = jsonObject.getStr("transaction_id"); String refundId = jsonObject.getStr("refund_id"); RefundLog refundLog = refundLogService.getOne(new LambdaQueryWrapper().eq(RefundLog::getPaymentReceivableNo, transactionId)); if (refundLog != null) { refundLog.setIsRefund(true); refundLog.setReceivableNo(refundId); refundLogService.saveOrUpdate(refundLog); } } else { log.info("退款失败 {}", plainText); JSONObject jsonObject = JSONUtil.parseObj(plainText); String transactionId = jsonObject.getStr("transaction_id"); String refundId = jsonObject.getStr("refund_id"); RefundLog refundLog = refundLogService.getOne(new LambdaQueryWrapper().eq(RefundLog::getPaymentReceivableNo, transactionId)); if (refundLog != null) { refundLog.setReceivableNo(refundId); refundLog.setErrorMessage(ciphertext.getStr("summary")); refundLogService.saveOrUpdate(refundLog); } } } catch (Exception e) { log.error("微信退款失败", e); } } /** * 获取微信支付配置 * * @return */ private WechatPaymentSetting wechatPaymentSetting() { try { Setting systemSetting = settingService.get(SettingEnum.WECHAT_PAYMENT.name()); WechatPaymentSetting wechatPaymentSetting = JSONUtil.toBean(systemSetting.getSettingValue(), WechatPaymentSetting.class); return wechatPaymentSetting; } catch (Exception e) { log.error("微信支付暂不支持", e); throw new ServiceException(ResultCode.PAY_NOT_SUPPORT); } } /** * 获取平台公钥 * * @return 平台公钥 */ private X509Certificate getPlatformCert() { //获取缓存中的平台公钥,如果有则直接返回,否则去微信请求 String publicCert = cache.getString(CachePrefix.WECHAT_PLAT_FORM_CERT.getPrefix()); if (!StringUtils.isEmpty(publicCert)) { return PayKit.getCertificate(publicCert); } //获取平台证书列表 try { WechatPaymentSetting setting = wechatPaymentSetting(); PaymentHttpResponse response = WechatApi.v3( RequestMethodEnums.GET, WechatDomain.CHINA.toString(), WechatApiEnum.GET_CERTIFICATES.toString(), setting.getMchId(), setting.getSerialNumber(), null, setting.getApiclient_key(), "" ); String body = response.getBody(); log.info("获取微信平台证书body: {}", body); if (response.getStatus() == 200) { JSONObject jsonObject = JSONUtil.parseObj(body); JSONArray dataArray = jsonObject.getJSONArray("data"); //默认认为只有一个平台证书 JSONObject encryptObject = dataArray.getJSONObject(0); JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate"); String associatedData = encryptCertificate.getStr("associated_data"); String cipherText = encryptCertificate.getStr("ciphertext"); String nonce = encryptCertificate.getStr("nonce"); publicCert = getPlatformCertStr(associatedData, nonce, cipherText); long second = (PayKit.getCertificate(publicCert).getNotAfter().getTime() - System.currentTimeMillis()) / 1000; cache.put(CachePrefix.WECHAT_PLAT_FORM_CERT.getPrefix(), publicCert, second); } else { log.error("证书获取失败:{}" + body); throw new ServiceException(ResultCode.WECHAT_CERT_ERROR); } return PayKit.getCertificate(publicCert); } catch (Exception e) { log.error("证书获取失败", e); } return null; } /** * 获取平台证书缓存的字符串 * 下列各个密钥参数 * * @param associatedData 密钥参数 * @param nonce 密钥参数 * @param cipherText 密钥参数 * @return platform key * @throws GeneralSecurityException 密钥获取异常 */ private String getPlatformCertStr(String associatedData, String nonce, String cipherText) throws GeneralSecurityException { AesUtil aesUtil = new AesUtil(wechatPaymentSetting().getApiKey3().getBytes(StandardCharsets.UTF_8)); //平台证书密文解密 //encrypt_certificate 中的 associated_data nonce ciphertext return aesUtil.decryptToString( associatedData.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), cipherText ); } /** * 修改订单支付单号 * @param payParam 支付参数 * @param outOrderNo 订单号 */ private void updateOrderPayNo(PayParam payParam,String outOrderNo ){ if(payParam.getOrderType().equals("ORDER")){ orderService.update(new LambdaUpdateWrapper() .eq(Order::getSn,payParam.getSn()) .set(Order::getPayOrderNo,outOrderNo)); }else if(payParam.getOrderType().equals("TRADE")){ orderService.update(new LambdaUpdateWrapper() .eq(Order::getTradeSn,payParam.getSn()) .set(Order::getPayOrderNo,outOrderNo)); } } }