|
@@ -0,0 +1,260 @@
|
|
|
+package com.rongwei.zhsw.system.wechat.impl;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import com.rongwe.zhsw.system.domain.SwBillingRecordDo;
|
|
|
+import com.rongwe.zhsw.system.domain.SwEnterpriseConfigInfoDo;
|
|
|
+import com.rongwe.zhsw.system.domain.SwRefundRequestRecordDO;
|
|
|
+import com.rongwe.zhsw.system.vo.PrepayNoticeVo;
|
|
|
+import com.rongwe.zhsw.system.vo.RefundInitiateVo;
|
|
|
+import com.rongwe.zhsw.system.vo.WeChatRefundNoticeVo;
|
|
|
+import com.rongwei.commonservice.service.impl.RedisServiceImpl;
|
|
|
+import com.rongwei.rwadmincommon.system.vo.SysUserVo;
|
|
|
+import com.rongwei.rwcommon.base.BaseDo;
|
|
|
+import com.rongwei.rwcommon.base.R;
|
|
|
+import com.rongwei.rwcommon.base.exception.CustomException;
|
|
|
+import com.rongwei.rwcommon.utils.Constants;
|
|
|
+import com.rongwei.rwcommonentity.commonservers.domain.TenantDo;
|
|
|
+import com.rongwei.zhsw.system.config.WeChatAboutApiPara;
|
|
|
+import com.rongwei.zhsw.system.dao.CommonBusinessDao;
|
|
|
+import com.rongwei.zhsw.system.service.impl.SwBillingRecordServiceImpl;
|
|
|
+import com.rongwei.zhsw.system.service.impl.SwEnterpriseConfigInfoServiceImpl;
|
|
|
+import com.rongwei.zhsw.system.service.impl.SwRefundRequestRecordServiceImpl;
|
|
|
+import com.rongwei.zhsw.system.utils.WxRefundApi;
|
|
|
+import com.rongwei.zhsw.system.utils.ZHSWCommonUtils;
|
|
|
+import com.rongwei.zhsw.system.wechat.RefundService;
|
|
|
+import com.wechat.pay.java.core.exception.ServiceException;
|
|
|
+import org.apache.commons.lang.StringUtils;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.web.context.request.RequestContextHolder;
|
|
|
+import org.springframework.web.context.request.ServletRequestAttributes;
|
|
|
+
|
|
|
+import javax.crypto.Cipher;
|
|
|
+import javax.crypto.spec.GCMParameterSpec;
|
|
|
+import javax.crypto.spec.SecretKeySpec;
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
+import java.io.IOException;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.util.Base64;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.List;
|
|
|
+import java.util.UUID;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+import static com.rongwei.zhsw.system.wechat.impl.PayMentServiceImpl.TAG_LENGTH_BIT;
|
|
|
+
|
|
|
+/**
|
|
|
+ * RefundServiceImppl class
|
|
|
+ *
|
|
|
+ * @author XH
|
|
|
+ * @date 2025/03/26
|
|
|
+ */
|
|
|
+@Service
|
|
|
+public class RefundServiceImpl implements RefundService {
|
|
|
+
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(RefundServiceImpl.class);
|
|
|
+ @Autowired
|
|
|
+ private SwEnterpriseConfigInfoServiceImpl swEnterpriseConfigInfoService;
|
|
|
+ @Autowired
|
|
|
+ private WeChatAboutApiPara weChatAboutApiPara;
|
|
|
+ @Autowired
|
|
|
+ private SwBillingRecordServiceImpl swBillingRecordService;
|
|
|
+ @Autowired
|
|
|
+ private SwRefundRequestRecordServiceImpl swRefundRequestRecordService;
|
|
|
+ @Autowired
|
|
|
+ private RedisServiceImpl redisService;
|
|
|
+ @Autowired
|
|
|
+ private CommonBusinessDao commonBusinessDao;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public R refundByRecord(RefundInitiateVo vo) {
|
|
|
+ log.info("发起小程序退款申请:{}", vo.getRefundRequestId());
|
|
|
+ String refundRequestId = vo.getRefundRequestId();
|
|
|
+ if (StringUtils.isBlank(refundRequestId)) {
|
|
|
+ log.error("参数异常");
|
|
|
+ throw new CustomException("缴费记录id为空,参数异常");
|
|
|
+ }
|
|
|
+
|
|
|
+ SwEnterpriseConfigInfoDo swEnterpriseConfigInfoDo = swEnterpriseConfigInfoService.getOne(new LambdaQueryWrapper<SwEnterpriseConfigInfoDo>().eq(BaseDo::getDeleted, "0"));
|
|
|
+ if (swEnterpriseConfigInfoDo == null) {
|
|
|
+ log.error("无法获取企业配置信息");
|
|
|
+ throw new CustomException("无法获取企业配置信息");
|
|
|
+ }
|
|
|
+ String enterpriseno = swEnterpriseConfigInfoDo.getEnterpriseno();
|
|
|
+ String merchantid = swEnterpriseConfigInfoDo.getMerchantid();
|
|
|
+ String merchantname = swEnterpriseConfigInfoDo.getMerchantname();
|
|
|
+ String merchantprivatekey = swEnterpriseConfigInfoDo.getMerchantprivatekey();
|
|
|
+ String merchantserialnumber = swEnterpriseConfigInfoDo.getMerchantserialnumber();
|
|
|
+ String merchantsecretkey = swEnterpriseConfigInfoDo.getMerchantsecretkey();
|
|
|
+
|
|
|
+ if (StringUtils.isBlank(enterpriseno)) {
|
|
|
+ log.error("支付参数:企业编号 为空");
|
|
|
+ throw new CustomException("商户支付配置错误,请联系系统管理员!");
|
|
|
+ }
|
|
|
+ if (StringUtils.isBlank(merchantid)) {
|
|
|
+ log.error("支付参数:商户ID 为空");
|
|
|
+ throw new CustomException("商户支付配置错误,请联系系统管理员!");
|
|
|
+ }
|
|
|
+ if (StringUtils.isBlank(merchantname)) {
|
|
|
+ log.error("支付参数:商户名称 为空");
|
|
|
+ throw new CustomException("商户支付配置错误,请联系系统管理员!");
|
|
|
+ }
|
|
|
+ if (StringUtils.isBlank(merchantprivatekey)) {
|
|
|
+ log.error("支付参数:商户私钥 为空");
|
|
|
+ throw new CustomException("商户支付配置错误,请联系系统管理员!");
|
|
|
+ }
|
|
|
+ if (StringUtils.isBlank(merchantserialnumber)) {
|
|
|
+ log.error("支付参数:商户序列号 为空");
|
|
|
+ throw new CustomException("商户支付配置错误,请联系系统管理员!");
|
|
|
+ }
|
|
|
+ if (StringUtils.isBlank(merchantsecretkey)) {
|
|
|
+ log.error("支付参数:商户密钥 为空");
|
|
|
+ throw new CustomException("商户支付配置错误,请联系系统管理员!");
|
|
|
+ }
|
|
|
+ // 退款申请
|
|
|
+ SwRefundRequestRecordDO swRefundRequestRecordDO = swRefundRequestRecordService.getById(refundRequestId);
|
|
|
+ if (swRefundRequestRecordDO.getPaymentrecordid() == null) {
|
|
|
+ log.error("无法获取退款申请");
|
|
|
+ throw new CustomException("无法获取退款申请");
|
|
|
+ }
|
|
|
+ if ("3".equals(swRefundRequestRecordDO.getRefundtype())) {
|
|
|
+ log.error("退款成功无法退款");
|
|
|
+ throw new CustomException("退款成功无法退款");
|
|
|
+ }
|
|
|
+ // 缴费记录
|
|
|
+ SwBillingRecordDo swBillingRecordDo = swBillingRecordService.getById(swRefundRequestRecordDO.getPaymentrecordid());
|
|
|
+ if (swBillingRecordDo == null) {
|
|
|
+ log.error("无法获取缴费记录");
|
|
|
+ throw new CustomException("无法获取缴费记录");
|
|
|
+ }
|
|
|
+ String wechatpayordernumber = swBillingRecordDo.getWechatpayordernumber();
|
|
|
+ String merchantpaymentnumber = swBillingRecordDo.getMerchantpaymentnumber();
|
|
|
+ if (StringUtils.isBlank(wechatpayordernumber) || StringUtils.isBlank(merchantpaymentnumber)) {
|
|
|
+ log.error("微信订单号或商户支付编号为空");
|
|
|
+ throw new CustomException("退款申请异常");
|
|
|
+ }
|
|
|
+ SysUserVo currentUser = ZHSWCommonUtils.getCurrentUser();
|
|
|
+ String dskey = currentUser.getTenantDo().getDskey();
|
|
|
+ WxRefundApi wxRefundApi = new WxRefundApi(merchantid, merchantprivatekey, merchantserialnumber, merchantsecretkey,
|
|
|
+ weChatAboutApiPara.getRefundCallbackUrl());
|
|
|
+ wxRefundApi.initMerchant();
|
|
|
+ // 生成订单号 规则
|
|
|
+ String nonceStr = UUID.randomUUID().toString().replaceAll("-", "");
|
|
|
+ try {
|
|
|
+ wxRefundApi.create(wechatpayordernumber, merchantpaymentnumber,
|
|
|
+ swRefundRequestRecordDO.getRefundamount().multiply(BigDecimal.valueOf(100)).longValue(),
|
|
|
+ swRefundRequestRecordDO.getPaidin().multiply(BigDecimal.valueOf(100)).longValue(),
|
|
|
+ swRefundRequestRecordDO.getRefundreason(),
|
|
|
+ nonceStr, dskey);
|
|
|
+ } catch (ServiceException e) {
|
|
|
+ if (e.getHttpStatusCode() == 403) {
|
|
|
+ // 获取异常原因
|
|
|
+ String errorMessage = e.getErrorMessage();
|
|
|
+ // 将商户单号保存之退款记录表
|
|
|
+ swRefundRequestRecordService.update(new LambdaUpdateWrapper<SwRefundRequestRecordDO>()
|
|
|
+ .eq(SwRefundRequestRecordDO::getId, refundRequestId)
|
|
|
+ .set(SwRefundRequestRecordDO::getRefundstatus, 4)
|
|
|
+ .set(SwRefundRequestRecordDO::getHasbeenrefunded, errorMessage));
|
|
|
+ return R.error("退款申请失败,原因是:"+errorMessage);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 将商户单号保存之退款记录表
|
|
|
+ swRefundRequestRecordService.update(new LambdaUpdateWrapper<SwRefundRequestRecordDO>()
|
|
|
+ .eq(SwRefundRequestRecordDO::getId, refundRequestId)
|
|
|
+ .set(SwRefundRequestRecordDO::getRefundstatus, 2)
|
|
|
+ .set(SwRefundRequestRecordDO::getMerchantrefundnumber, nonceStr));
|
|
|
+ return R.ok("退款申请已提交,请稍后查看退款结果");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 退款申请成功回调函数
|
|
|
+ *
|
|
|
+ * @param prepayNoticeVo
|
|
|
+ * @param dsKey
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public R refundNotice(PrepayNoticeVo prepayNoticeVo, String dsKey) {
|
|
|
+ log.info("微信退款申请成功回调函数: {},: {}", prepayNoticeVo, dsKey);
|
|
|
+ if (StringUtils.isBlank(dsKey) || StringUtils.isNotBlank(dsKey)) {
|
|
|
+ throw new CustomException("asdasda");
|
|
|
+ }
|
|
|
+ List<String> dsKeys;
|
|
|
+ if (StringUtils.isBlank(dsKey)) {
|
|
|
+ List<TenantDo> tenantList = (List<TenantDo>) redisService.getRedisCatchObj("allTenants");
|
|
|
+ // 获取 所有的主库信息
|
|
|
+ dsKeys = tenantList.stream().map(TenantDo::getDskey).collect(Collectors.toList());
|
|
|
+ } else {
|
|
|
+ dsKeys = Collections.singletonList(dsKey);
|
|
|
+ }
|
|
|
+ dsKeys.remove("incontrol");
|
|
|
+ // 获取所有商户的密钥
|
|
|
+ List<SwEnterpriseConfigInfoDo> secretKeyList = commonBusinessDao.getSecretKey(dsKeys);
|
|
|
+ String analysisStr = null;
|
|
|
+ String deKey = null;
|
|
|
+ for (SwEnterpriseConfigInfoDo swEnterpriseConfigInfoDo : secretKeyList) {
|
|
|
+ try {
|
|
|
+ byte[] apiV3Key = swEnterpriseConfigInfoDo.getMerchantsecretkey().getBytes("UTF8");
|
|
|
+ byte[] nonce = prepayNoticeVo.getResource().getNonce().getBytes("UTF8");
|
|
|
+ String ciphertext = prepayNoticeVo.getResource().getCiphertext();
|
|
|
+ byte[] associatedData = prepayNoticeVo.getResource().getAssociated_data().getBytes("UTF8");
|
|
|
+ Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
|
|
+ SecretKeySpec key = new SecretKeySpec(apiV3Key, "AES");
|
|
|
+ GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
|
|
|
+ cipher.init(Cipher.DECRYPT_MODE, key, spec);
|
|
|
+ cipher.updateAAD(associatedData);
|
|
|
+ analysisStr = new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
|
|
|
+ deKey = swEnterpriseConfigInfoDo.getTenantid();
|
|
|
+ if (StringUtils.isNotBlank(analysisStr)) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("当前租户:{}解密异常", swEnterpriseConfigInfoDo.getTenantid());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (StringUtils.isBlank(analysisStr)) {
|
|
|
+ log.error("回调函数解析异常");
|
|
|
+ return R.error();
|
|
|
+ }
|
|
|
+ ObjectMapper mapper = new ObjectMapper();
|
|
|
+ WeChatRefundNoticeVo weChatRefundNoticeVo;
|
|
|
+ try {
|
|
|
+ weChatRefundNoticeVo = mapper.readValue(analysisStr, WeChatRefundNoticeVo.class);
|
|
|
+ } catch (IOException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ log.error("JSON转换异常");
|
|
|
+ return R.error();
|
|
|
+ }
|
|
|
+
|
|
|
+ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
|
|
+ HttpServletRequest request = attributes.getRequest();
|
|
|
+ // 设置租户信息 自动切库
|
|
|
+ request.setAttribute(Constants.SAAS_LOGIN_TOKEN, deKey);
|
|
|
+
|
|
|
+ String refundId = weChatRefundNoticeVo.getRefundId();
|
|
|
+ String outRefundNo = weChatRefundNoticeVo.getOutRefundNo();
|
|
|
+ SwRefundRequestRecordDO swRefundRequestRecordDO = swRefundRequestRecordService.getOne(new LambdaQueryWrapper<SwRefundRequestRecordDO>()
|
|
|
+ .eq(BaseDo::getDeleted, "0").eq(SwRefundRequestRecordDO::getMerchantrefundnumber, outRefundNo));
|
|
|
+ if (swRefundRequestRecordDO == null) {
|
|
|
+ log.error("退款记录失败");
|
|
|
+ throw new CustomException("退款失败");
|
|
|
+ }
|
|
|
+ if ("SUCCESS".equals(weChatRefundNoticeVo.getRefundStatus())) {
|
|
|
+ log.error("退款失败");
|
|
|
+ // 更新缴费记录相关信息
|
|
|
+ swRefundRequestRecordService.getBaseMapper().updateWeChatRefundInfo(deKey, outRefundNo,
|
|
|
+ refundId, analysisStr, weChatRefundNoticeVo.getSuccessTime(), "4");
|
|
|
+ return R.ok();
|
|
|
+ }
|
|
|
+ // 更新缴费记录相关信息
|
|
|
+ swRefundRequestRecordService.getBaseMapper().updateWeChatRefundInfo(deKey, outRefundNo,
|
|
|
+ refundId, analysisStr, weChatRefundNoticeVo.getSuccessTime(), "3");
|
|
|
+
|
|
|
+ swRefundRequestRecordService.refundApplication(swRefundRequestRecordDO.getId());
|
|
|
+ return R.ok();
|
|
|
+ }
|
|
|
+}
|