| | |
| | | req.setReturnUrl(addChargingOrder.getReturnUrl()); |
| | | req.setOpenId(appUser.getWxOpenid()); |
| | | req.setProfitSharing(buildIcbcProfitSharing(site)); |
| | | IcbcPaymentResp data = icbcPaymentClient.payment(req).getData(); |
| | | R<IcbcPaymentResp> result = icbcPaymentClient.payment(req); |
| | | if (result == null || R.isError(result)) { |
| | | String reason = result == null ? "工行支付服务无响应" : firstNotBlank(result.getMsg(), "工行支付服务调用失败"); |
| | | log.error("调起工行支付失败,订单号:{},原因:{}", chargingOrder.getCode(), reason); |
| | | throw new RuntimeException("调起工行支付失败:" + reason); |
| | | } |
| | | IcbcPaymentResp data = result.getData(); |
| | | if (isIcbcPaymentCreated(data)) { |
| | | if (StringUtils.hasLength(data.getTradeNo())) { |
| | | chargingOrder.setRechargeSerialNumber(data.getTradeNo()); |
| | |
| | | } |
| | | return AjaxResult.success(data); |
| | | } |
| | | throw new RuntimeException("调起工行支付失败"); |
| | | String reason = buildIcbcPaymentFailReason(data); |
| | | log.error("调起工行支付失败,订单号:{},工行返回:{}", chargingOrder.getCode(), reason); |
| | | throw new RuntimeException("调起工行支付失败:" + reason); |
| | | } |
| | | if (Integer.valueOf(2).equals(addChargingOrder.getPaymentType())) { |
| | | AliPaymentReq req = new AliPaymentReq(); |
| | |
| | | * @param chargingOrder |
| | | */ |
| | | public void editUserTag(TChargingOrder chargingOrder) { |
| | | if (chargingOrder == null || chargingOrder.getAppUserId() == null) { |
| | | return; |
| | | } |
| | | //处理用户标签数据 |
| | | List<TUserTag> data = userTagClient.getAllUserTag().getData(); |
| | | R<List<TUserTag>> tagResult = userTagClient.getAllUserTag(); |
| | | List<TUserTag> data = tagResult == null ? null : tagResult.getData(); |
| | | if (data == null || data.isEmpty()) { |
| | | return; |
| | | } |
| | | //累计充电次数 |
| | | List<TUserTag> userTagList1 = data.stream().filter(s -> s.getStandardCondition() == 1).collect(Collectors.toList()); |
| | | List<TUserTag> userTagList1 = data.stream().filter(s -> Integer.valueOf(1).equals(s.getStandardCondition())).collect(Collectors.toList()); |
| | | for (TUserTag tUserTag : userTagList1) { |
| | | Integer times = JSON.parseObject(tUserTag.getConditions()).getInteger("times"); |
| | | JSONObject conditions = JSON.parseObject(tUserTag.getConditions()); |
| | | Integer times = conditions == null ? null : conditions.getInteger("times"); |
| | | if (times == null || times <= 0 || tUserTag.getCreateTime() == null) { |
| | | continue; |
| | | } |
| | | TAppUserTag appUserTag = new TAppUserTag(); |
| | | appUserTag.setAppUserId(chargingOrder.getAppUserId()); |
| | | appUserTag.setUserTagId(tUserTag.getId()); |
| | |
| | | } |
| | | } |
| | | //充电频率 |
| | | List<TUserTag> userTagList2 = data.stream().filter(s -> s.getStandardCondition() == 2).collect(Collectors.toList()); |
| | | List<TUserTag> userTagList2 = data.stream().filter(s -> Integer.valueOf(2).equals(s.getStandardCondition())).collect(Collectors.toList()); |
| | | for (TUserTag tUserTag : userTagList2) { |
| | | TAppUserTag appUserTag = new TAppUserTag(); |
| | | appUserTag.setAppUserId(chargingOrder.getAppUserId()); |
| | | appUserTag.setUserTagId(tUserTag.getId()); |
| | | TAppUserTag data1 = appUserTagClient.getUserTag(appUserTag).getData(); |
| | | |
| | | Integer day = JSON.parseObject(tUserTag.getConditions()).getInteger("day"); |
| | | Integer times = JSON.parseObject(tUserTag.getConditions()).getInteger("times"); |
| | | JSONObject conditions = JSON.parseObject(tUserTag.getConditions()); |
| | | Integer day = conditions == null ? null : conditions.getInteger("day"); |
| | | Integer times = conditions == null ? null : conditions.getInteger("times"); |
| | | if (day == null || day <= 0 || times == null || times <= 0) { |
| | | continue; |
| | | } |
| | | LocalDateTime endTime = LocalDateTime.now(); |
| | | endTime.plusDays(day); |
| | | String start = tUserTag.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); |
| | | String start = endTime.minusDays(day).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); |
| | | String end = endTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); |
| | | long count1 = this.count(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getAppUserId, chargingOrder.getAppUserId()) |
| | | .eq(TChargingOrder::getRechargePaymentStatus, 2).between(TChargingOrder::getStartTime, start, end).eq(TChargingOrder::getDelFlag, 0)); |
| | |
| | | |
| | | @Override |
| | | public R<PageInfo<PayOrderDto>> payOrderQuery(PayOrderQueryDto payOrderQueryDto) { |
| | | if (payOrderQueryDto == null) { |
| | | payOrderQueryDto = new PayOrderQueryDto(); |
| | | } |
| | | PageInfo<PayOrderDto> pageInfo = new PageInfo<>(payOrderQueryDto.getPageCurr(), payOrderQueryDto.getPageSize()); |
| | | List<PayOrderDto> list = this.baseMapper.payOrderQuery(pageInfo, payOrderQueryDto); |
| | | if (list == null || list.isEmpty()) { |
| | | pageInfo.setRecords(Collections.emptyList()); |
| | | return R.ok(pageInfo); |
| | | } |
| | | for (PayOrderDto payOrderDto : list) { |
| | | if (payOrderDto.getType() == 2 || payOrderDto.getType() == 3) { |
| | | if (payOrderDto == null) { |
| | | continue; |
| | | } |
| | | if (Integer.valueOf(2).equals(payOrderDto.getType()) || Integer.valueOf(3).equals(payOrderDto.getType())) { |
| | | payOrderDto.setFinalAmount(payOrderDto.getOrderAmount()); |
| | | } |
| | | if (payOrderDto.getType() == 1 && payOrderDto.getStatus() == 3) { |
| | | if (Integer.valueOf(1).equals(payOrderDto.getType()) && Integer.valueOf(3).equals(payOrderDto.getStatus())) { |
| | | payOrderDto.setFinalAmount(payOrderDto.getOrderAmount()); |
| | | payOrderDto.setOrderAmount(payOrderDto.getOrderAmount()); |
| | | payOrderDto.setPaymentAmount(payOrderDto.getOrderAmount()); |
| | |
| | | if (order == null) { |
| | | return R.fail("未查询到支付成功的充电订单"); |
| | | } |
| | | BigDecimal refundAmount = money(payOrderQueryDto.getRefundAmount()); |
| | | BigDecimal refundableAmount = money(order.getPaymentAmount()).subtract(money(order.getRefundAmount())).setScale(2, RoundingMode.HALF_UP); |
| | | if (refundAmount.compareTo(refundableAmount) > 0) { |
| | | return R.fail("退款金额不能超过剩余可退金额"); |
| | | RLock lock = redissonClient.getLock("SPECIAL_REFUND_ORDER_LOCK:" + order.getId()); |
| | | if (!lock.tryLock()) { |
| | | return R.fail("该订单正在退款处理中,请勿重复提交"); |
| | | } |
| | | BigDecimal refundedThirdAmount = money(order.getThirdRefundAmount()); |
| | | BigDecimal refundableThirdAmount = money(order.getThirdPayAmount()).subtract(refundedThirdAmount); |
| | | if (refundableThirdAmount.compareTo(BigDecimal.ZERO) < 0) { |
| | | refundableThirdAmount = BigDecimal.ZERO; |
| | | try { |
| | | order = this.getById(order.getId()); |
| | | if (order == null) { |
| | | return R.fail("充电订单不存在"); |
| | | } |
| | | BigDecimal refundAmount = money(payOrderQueryDto.getRefundAmount()); |
| | | BigDecimal refundableAmount = money(order.getPaymentAmount()).subtract(money(order.getRefundAmount())).setScale(2, RoundingMode.HALF_UP); |
| | | if (refundAmount.compareTo(refundableAmount) > 0) { |
| | | return R.fail("退款金额不能超过剩余可退金额"); |
| | | } |
| | | BigDecimal refundedThirdAmount = money(order.getThirdRefundAmount()); |
| | | BigDecimal refundableThirdAmount = money(order.getThirdPayAmount()).subtract(refundedThirdAmount); |
| | | if (refundableThirdAmount.compareTo(BigDecimal.ZERO) < 0) { |
| | | refundableThirdAmount = BigDecimal.ZERO; |
| | | } |
| | | BigDecimal thirdRefundAmount = isBalancePayMode(order.getPayMethod()) |
| | | ? refundAmount.min(refundableThirdAmount).setScale(2, RoundingMode.HALF_UP) |
| | | : refundAmount; |
| | | BigDecimal balanceRefundAmount = refundAmount.subtract(thirdRefundAmount).setScale(2, RoundingMode.HALF_UP); |
| | | if (balanceRefundAmount.compareTo(BigDecimal.ZERO) < 0) { |
| | | balanceRefundAmount = BigDecimal.ZERO; |
| | | } |
| | | TChargingOrderRefund refund = buildSpecialChargingRefund(order, payOrderQueryDto, refundAmount, balanceRefundAmount, thirdRefundAmount); |
| | | if (thirdRefundAmount.compareTo(BigDecimal.ZERO) == 0) { |
| | | refund.setRefundSerialNumber("BALANCE"); |
| | | finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount); |
| | | return R.ok(); |
| | | } |
| | | Integer payType = order.getRechargePaymentType(); |
| | | boolean success = false; |
| | | if (Integer.valueOf(1).equals(payType)) { |
| | | R shareRefund = refundIcbcShareBeforeMainRefund(order, refund, thirdRefundAmount); |
| | | if (shareRefund.getCode() != 200) { |
| | | return shareRefund; |
| | | } |
| | | success = refundByIcbcAndFinish(order, refund, thirdRefundAmount, SPECIAL_REFUND_TITLE, true, refundAmount, balanceRefundAmount); |
| | | } else if (Integer.valueOf(2).equals(payType)) { |
| | | RefundReq dto = new RefundReq(); |
| | | dto.setOutTradeNo(order.getCode()); |
| | | dto.setOutRequestNo(refund.getRefundCode()); |
| | | dto.setRefundAmount(thirdRefundAmount.toString()); |
| | | dto.setRefundReason(SPECIAL_REFUND_TITLE); |
| | | RefundResp resp = aliPaymentClient.refund(dto).getData(); |
| | | if (resp != null) { |
| | | refund.setRefundSerialNumber(resp.getTradeNo()); |
| | | refund.setRefundTime(LocalDateTime.now()); |
| | | finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount); |
| | | success = true; |
| | | } |
| | | } else if (Integer.valueOf(3).equals(payType)) { |
| | | RefundReq dto = new RefundReq(); |
| | | dto.setOutTradeNo(order.getRechargeSerialNumber()); |
| | | dto.setOutRequestNo(refund.getRefundCode()); |
| | | dto.setRefundAmount(thirdRefundAmount.toString()); |
| | | dto.setRefundReason(SPECIAL_REFUND_TITLE); |
| | | RefundResp resp = h5AliPaymentClient.refund(dto).getData(); |
| | | if (resp != null && "10000".equals(resp.getCode())) { |
| | | refund.setRefundSerialNumber(resp.getTradeNo()); |
| | | refund.setRefundTime(LocalDateTime.now()); |
| | | finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount); |
| | | success = true; |
| | | } |
| | | } |
| | | return success ? R.ok() : R.fail("退款失败,请检查支付渠道或稍后重试"); |
| | | } finally { |
| | | if (lock.isHeldByCurrentThread()) { |
| | | lock.unlock(); |
| | | } |
| | | } |
| | | BigDecimal thirdRefundAmount = isBalancePayMode(order.getPayMethod()) |
| | | ? refundAmount.min(refundableThirdAmount).setScale(2, RoundingMode.HALF_UP) |
| | | : refundAmount; |
| | | BigDecimal balanceRefundAmount = refundAmount.subtract(thirdRefundAmount).setScale(2, RoundingMode.HALF_UP); |
| | | if (balanceRefundAmount.compareTo(BigDecimal.ZERO) < 0) { |
| | | balanceRefundAmount = BigDecimal.ZERO; |
| | | } |
| | | TChargingOrderRefund refund = buildSpecialChargingRefund(order, payOrderQueryDto, refundAmount, balanceRefundAmount, thirdRefundAmount); |
| | | if (thirdRefundAmount.compareTo(BigDecimal.ZERO) == 0) { |
| | | refund.setRefundSerialNumber("BALANCE"); |
| | | finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount); |
| | | } |
| | | |
| | | private R refundIcbcShareBeforeMainRefund(TChargingOrder order, TChargingOrderRefund refund, BigDecimal thirdRefundAmount) { |
| | | if (order == null || refund == null || thirdRefundAmount == null || thirdRefundAmount.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return R.ok(); |
| | | } |
| | | Integer payType = order.getRechargePaymentType(); |
| | | boolean success = false; |
| | | if (Integer.valueOf(1).equals(payType) || "ICBC".equalsIgnoreCase(payOrderQueryDto.getRefundChannel())) { |
| | | success = refundByIcbcAndFinish(order, refund, thirdRefundAmount, SPECIAL_REFUND_TITLE, true, refundAmount, balanceRefundAmount); |
| | | } else if (Integer.valueOf(2).equals(payType)) { |
| | | RefundReq dto = new RefundReq(); |
| | | dto.setOutTradeNo(order.getCode()); |
| | | dto.setOutRequestNo(refund.getRefundCode()); |
| | | dto.setRefundAmount(thirdRefundAmount.toString()); |
| | | dto.setRefundReason(SPECIAL_REFUND_TITLE); |
| | | RefundResp resp = aliPaymentClient.refund(dto).getData(); |
| | | if (resp != null) { |
| | | refund.setRefundSerialNumber(resp.getTradeNo()); |
| | | refund.setRefundTime(LocalDateTime.now()); |
| | | finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount); |
| | | success = true; |
| | | if (!"ICBC".equals(order.getSourceName())) { |
| | | return R.ok(); |
| | | } |
| | | List<TChargingOrderShareRecord> shareRecords = chargingOrderShareRecordMapper.selectList( |
| | | new LambdaQueryWrapper<TChargingOrderShareRecord>() |
| | | .eq(TChargingOrderShareRecord::getChargingOrderId, order.getId()) |
| | | .eq(TChargingOrderShareRecord::getPayChannel, "ICBC") |
| | | .eq(TChargingOrderShareRecord::getNeedOnlineShare, true) |
| | | .eq(TChargingOrderShareRecord::getOnlineShareStatus, "SUCCESS") |
| | | .eq(TChargingOrderShareRecord::getDelFlag, false) |
| | | .orderByAsc(TChargingOrderShareRecord::getId)); |
| | | if (shareRecords == null || shareRecords.isEmpty()) { |
| | | return R.ok(); |
| | | } |
| | | BigDecimal thirdPayAmount = getRefundableThirdPayAmount(order); |
| | | if (thirdPayAmount.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return R.fail("订单已分账但第三方支付金额异常,禁止退款"); |
| | | } |
| | | BigDecimal cumulativeThirdRefund = money(order.getThirdRefundAmount()).add(thirdRefundAmount).min(thirdPayAmount).setScale(2, RoundingMode.HALF_UP); |
| | | for (TChargingOrderShareRecord shareRecord : shareRecords) { |
| | | BigDecimal shareAmount = money(shareRecord.getShareAmount()); |
| | | if (shareAmount.compareTo(BigDecimal.ZERO) <= 0) { |
| | | continue; |
| | | } |
| | | } else if (Integer.valueOf(3).equals(payType)) { |
| | | RefundReq dto = new RefundReq(); |
| | | dto.setOutTradeNo(order.getRechargeSerialNumber()); |
| | | dto.setOutRequestNo(refund.getRefundCode()); |
| | | dto.setRefundAmount(thirdRefundAmount.toString()); |
| | | dto.setRefundReason(SPECIAL_REFUND_TITLE); |
| | | RefundResp resp = h5AliPaymentClient.refund(dto).getData(); |
| | | if (resp != null && "10000".equals(resp.getCode())) { |
| | | refund.setRefundSerialNumber(resp.getTradeNo()); |
| | | refund.setRefundTime(LocalDateTime.now()); |
| | | finishManualChargingRefund(order, refund, refundAmount, balanceRefundAmount, thirdRefundAmount); |
| | | success = true; |
| | | BigDecimal targetRefundAmount = shareAmount.multiply(cumulativeThirdRefund).divide(thirdPayAmount, 2, RoundingMode.HALF_UP).min(shareAmount).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal refundedShareAmount; |
| | | try { |
| | | refundedShareAmount = queryIcbcShareRefundAmount(order, shareRecord); |
| | | } catch (ServiceException e) { |
| | | return R.fail(e.getMessage()); |
| | | } |
| | | BigDecimal shareRefundAmount = targetRefundAmount.subtract(refundedShareAmount).setScale(2, RoundingMode.HALF_UP); |
| | | if (shareRefundAmount.compareTo(BigDecimal.ZERO) <= 0) { |
| | | continue; |
| | | } |
| | | R result = refundIcbcShareRecord(order, shareRecord, refund, shareRefundAmount); |
| | | if (result.getCode() != 200) { |
| | | return result; |
| | | } |
| | | } |
| | | return success ? R.ok() : R.fail("退款失败,请检查支付渠道或稍后重试"); |
| | | return R.ok(); |
| | | } |
| | | |
| | | private BigDecimal queryIcbcShareRefundAmount(TChargingOrder order, TChargingOrderShareRecord shareRecord) { |
| | | try { |
| | | IcbcSubOrderQueryReq queryReq = IcbcAdminFundOperationSupport.buildSubOrderQueryRequest(order, shareRecord); |
| | | R<IcbcSubOrderQueryResp> queryResult = icbcPaymentClient.querySubOrder(queryReq); |
| | | IcbcSubOrderQueryResp queryResp = queryResult == null ? null : queryResult.getData(); |
| | | if (queryResult == null || queryResult.getCode() != 200 || queryResp == null || !"SUCCESS".equals(queryResp.getStatus())) { |
| | | throw new ServiceException(queryResult == null ? "工行子订单查询无响应" : firstNotBlank(queryResult.getMsg(), "工行子订单查询失败")); |
| | | } |
| | | return parseIcbcAmount(queryResp.getOrderRefundAmnt()); |
| | | } catch (IllegalArgumentException e) { |
| | | throw new ServiceException(e.getMessage()); |
| | | } catch (ServiceException e) { |
| | | throw e; |
| | | } catch (Exception e) { |
| | | throw new ServiceException("工行子订单查询异常:" + firstNotBlank(e.getMessage(), "未知异常")); |
| | | } |
| | | } |
| | | |
| | | private R refundIcbcShareRecord(TChargingOrder order, TChargingOrderShareRecord shareRecord, TChargingOrderRefund refund, BigDecimal shareRefundAmount) { |
| | | String suffix = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")); |
| | | String retSubOrderId = ("SPSUB" + refund.getRefundCode() + shareRecord.getId()).substring(0, Math.min(50, ("SPSUB" + refund.getRefundCode() + shareRecord.getId()).length())); |
| | | String outtrxSerialNo = ("SPSUBREF" + suffix + shareRecord.getId()).substring(0, Math.min(50, ("SPSUBREF" + suffix + shareRecord.getId()).length())); |
| | | try { |
| | | IcbcSubOrderRefundReq refundReq = IcbcAdminFundOperationSupport.buildSubOrderRefundRequest( |
| | | order, shareRecord, shareRefundAmount, retSubOrderId, outtrxSerialNo); |
| | | refundReq.setMerSpInfor("特殊退款分账退回:" + order.getCode()); |
| | | R<IcbcSubOrderRefundResp> refundResult = icbcPaymentClient.refundSubOrder(refundReq); |
| | | IcbcSubOrderRefundResp refundResp = refundResult == null ? null : refundResult.getData(); |
| | | if (refundResult == null || refundResult.getCode() != 200 || refundResp == null || !"SUCCESS".equals(refundResp.getStatus())) { |
| | | String message = refundResult == null ? "工行分账退款无响应" : firstNotBlank(refundResult.getMsg(), refundResp == null ? null : refundResp.getRawResponse(), "工行分账退款失败"); |
| | | log.error("特殊退款分账退回失败,订单号:{},退款单号:{},分账记录:{},金额:{},原因:{}", |
| | | order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount, message); |
| | | return R.fail("分账退回失败:" + message); |
| | | } |
| | | log.info("特殊退款分账退回成功,订单号:{},退款单号:{},分账记录:{},金额:{}", |
| | | order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount); |
| | | return R.ok(); |
| | | } catch (IllegalArgumentException | ServiceException e) { |
| | | log.error("特殊退款分账退回失败,订单号:{},退款单号:{},分账记录:{},金额:{}", |
| | | order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount, e); |
| | | return R.fail("分账退回失败:" + e.getMessage()); |
| | | } catch (Exception e) { |
| | | log.error("特殊退款分账退回异常,订单号:{},退款单号:{},分账记录:{},金额:{}", |
| | | order.getCode(), refund.getRefundCode(), shareRecord.getId(), shareRefundAmount, e); |
| | | return R.fail("分账退回异常:" + firstNotBlank(e.getMessage(), "未知异常")); |
| | | } |
| | | } |
| | | |
| | | private BigDecimal parseIcbcAmount(String amount) { |
| | | if (!StringUtils.hasLength(amount)) { |
| | | return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | String value = amount.trim(); |
| | | try { |
| | | if (value.contains(".")) { |
| | | return money(new BigDecimal(value)); |
| | | } |
| | | return new BigDecimal(value).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP); |
| | | } catch (NumberFormatException e) { |
| | | throw new ServiceException("工行金额格式异常:" + amount); |
| | | } |
| | | } |
| | | |
| | | private R validateOperationPassword(PayOrderRefundDto payOrderQueryDto) { |
| | |
| | | } |
| | | } |
| | | |
| | | private String buildIcbcPaymentFailReason(IcbcPaymentResp resp) { |
| | | if (resp == null) { |
| | | return "工行支付服务未返回支付数据"; |
| | | } |
| | | String reason = firstNotBlank(resp.getRawResponse(), resp.getStatus()); |
| | | if (!StringUtils.hasLength(reason)) { |
| | | return "工行支付服务返回数据不完整"; |
| | | } |
| | | if (reason.length() > 500) { |
| | | return reason.substring(0, 500); |
| | | } |
| | | return reason; |
| | | } |
| | | |
| | | private void triggerChargingOrderShare(TChargingOrder chargingOrder, Site site) { |
| | | if (!isNeedChargingOrderShare(chargingOrder)) { |
| | | return; |