| | |
| | | import com.ruoyi.order.api.vo.PlatformStopChargingReplyVO; |
| | | import com.ruoyi.order.dto.*; |
| | | import com.ruoyi.order.mapper.TChargingOrderMapper; |
| | | import com.ruoyi.order.mapper.TChargingOrderShareRecordMapper; |
| | | import com.ruoyi.order.mapper.TSettlementConfirmMapper; |
| | | import com.ruoyi.order.service.TChargingOrderAccountingStrategyService; |
| | | import com.ruoyi.order.service.TChargingOrderRefundService; |
| | |
| | | import com.ruoyi.other.api.vo.ExchangeCodeConsumeResultVO; |
| | | import com.ruoyi.payment.api.feignClient.AliPaymentClient; |
| | | import com.ruoyi.payment.api.feignClient.H5AliPaymentClient; |
| | | import com.ruoyi.payment.api.feignClient.IcbcPaymentClient; |
| | | import com.ruoyi.payment.api.feignClient.WxPaymentClient; |
| | | import com.ruoyi.payment.api.model.*; |
| | | import com.ruoyi.payment.api.vo.*; |
| | |
| | | private AliPaymentClient aliPaymentClient; |
| | | @Resource |
| | | private H5AliPaymentClient h5AliPaymentClient; |
| | | @Resource |
| | | private IcbcPaymentClient icbcPaymentClient; |
| | | @Resource |
| | | private TChargingOrderShareRecordMapper chargingOrderShareRecordMapper; |
| | | |
| | | @Resource |
| | | private AppCouponClient appCouponClient; |
| | |
| | | changeUserBalance(chargingOrder.getAppUserId(), balanceRefundAmount); |
| | | recordBalanceRefund(chargingOrder, balanceRefundAmount, refund.getRefundCode()); |
| | | } |
| | | triggerChargingOrderShare(chargingOrder); |
| | | } |
| | | |
| | | private boolean refundByIcbcAndFinish(TChargingOrder chargingOrder, TChargingOrderRefund refund, BigDecimal thirdRefundAmount, |
| | | String reason, boolean finishManual, BigDecimal refundAmount, BigDecimal balanceRefundAmount) { |
| | | IcbcRefundReq req = new IcbcRefundReq(); |
| | | req.setOutTradeNo(chargingOrder.getCode()); |
| | | req.setOrderId(chargingOrder.getRechargeSerialNumber()); |
| | | req.setOutRefundNo(refund.getRefundCode()); |
| | | req.setRefundAmount(thirdRefundAmount); |
| | | req.setTotalAmount(getRefundableThirdPayAmount(chargingOrder)); |
| | | req.setReason(reason); |
| | | R<IcbcRefundResp> result = icbcPaymentClient.refund(req); |
| | | IcbcRefundResp resp = result == null ? null : result.getData(); |
| | | if (result == null || result.getCode() != 200 || resp == null || !"SUCCESS".equals(resp.getStatus())) { |
| | | log.error("工行退款失败,订单号:{},退款单号:{},返回:{}", chargingOrder.getCode(), refund.getRefundCode(), result == null ? null : result.getMsg()); |
| | | return false; |
| | | } |
| | | if (finishManual) { |
| | | refund.setRefundSerialNumber(firstNotBlank(resp.getRefundId(), resp.getOutRefundNo())); |
| | | refund.setRefundTime(LocalDateTime.now()); |
| | | finishManualChargingRefund(chargingOrder, refund, refundAmount, balanceRefundAmount, thirdRefundAmount); |
| | | } else { |
| | | chargingOrderRefundService.save(refund); |
| | | chargingOrderStartupFailureWxRefund(refund.getRefundCode(), firstNotBlank(resp.getRefundId(), resp.getOutRefundNo()), "SUCCESS", resp.getRefundTime()); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | private boolean isIcbcOrder(TChargingOrder chargingOrder) { |
| | | return chargingOrder != null && "ICBC".equals(chargingOrder.getSourceName()); |
| | | } |
| | | |
| | | /** |
| | | * 判断订单是否为白名单订单 |
| | |
| | | .eq(TChargingOrder::getRechargePaymentStatus, 1).eq(TChargingOrder::getDelFlag, 0)); |
| | | for (TChargingOrder tChargingOrder : list) { |
| | | Integer rechargePaymentType = tChargingOrder.getRechargePaymentType(); |
| | | if (1 == rechargePaymentType) { |
| | | if ("ICBC".equals(tChargingOrder.getSourceName())) { |
| | | IcbcPaymentResp data = icbcPaymentClient.query(tChargingOrder.getCode()).getData(); |
| | | if (null != data && isIcbcRemovableStatus(data.getStatus(), tChargingOrder)) { |
| | | this.removeById(tChargingOrder.getId()); |
| | | } |
| | | } |
| | | if (!"ICBC".equals(tChargingOrder.getSourceName()) && 1 == rechargePaymentType) { |
| | | NotifyV3PayDecodeRespBody data = wxPaymentClient.queryOrderInfo(tChargingOrder.getCode()).getData(); |
| | | if (null != data) { |
| | | String trade_state = data.getTrade_state(); |
| | |
| | | } |
| | | } |
| | | } |
| | | if (2 == rechargePaymentType) { |
| | | if (!"ICBC".equals(tChargingOrder.getSourceName()) && 2 == rechargePaymentType) { |
| | | AliQueryOrder data = aliPaymentClient.query(tChargingOrder.getCode()).getData(); |
| | | if (null != data) { |
| | | //支付失败,删除无效的订单 |
| | |
| | | } |
| | | } |
| | | } |
| | | if (3 == rechargePaymentType) { |
| | | if (!"ICBC".equals(tChargingOrder.getSourceName()) && 3 == rechargePaymentType) { |
| | | AliQueryOrder data = h5AliPaymentClient.query(tChargingOrder.getCode()).getData(); |
| | | if (null != data) { |
| | | //支付失败,删除无效的订单 |
| | |
| | | .eq(TChargingOrder::getAppUserCarId, userId).eq(TChargingOrder::getRechargePaymentStatus, 1).eq(TChargingOrder::getDelFlag, 0)); |
| | | if (null != one) { |
| | | //查询三方支付数据,支付中直接结束三方订单 |
| | | if (1 == one.getRechargePaymentType()) { |
| | | if ("ICBC".equals(one.getSourceName())) { |
| | | IcbcPaymentResp data = icbcPaymentClient.query(one.getCode()).getData(); |
| | | if (null != data && isIcbcRemovableStatus(data.getStatus(), one)) { |
| | | this.removeById(one.getId()); |
| | | } else { |
| | | return AjaxResult.error("存在支付中订单,请稍后再试"); |
| | | } |
| | | } |
| | | if (!"ICBC".equals(one.getSourceName()) && 1 == one.getRechargePaymentType()) { |
| | | NotifyV3PayDecodeRespBody data = wxPaymentClient.queryOrderInfo(one.getCode()).getData(); |
| | | if (null != data) { |
| | | String trade_state = data.getTrade_state(); |
| | |
| | | } |
| | | } |
| | | } |
| | | if (2 == one.getRechargePaymentType()) { |
| | | if (!"ICBC".equals(one.getSourceName()) && 2 == one.getRechargePaymentType()) { |
| | | AliQueryOrder data = aliPaymentClient.query(one.getCode()).getData(); |
| | | if (null != data) { |
| | | String trade_state = data.getTradeStatus(); |
| | |
| | | } |
| | | } |
| | | } |
| | | if (3 == one.getRechargePaymentType()) { |
| | | if (!"ICBC".equals(one.getSourceName()) && 3 == one.getRechargePaymentType()) { |
| | | AliQueryOrder data = h5AliPaymentClient.query(one.getCode()).getData(); |
| | | if (null != data) { |
| | | String trade_state = data.getTradeStatus(); |
| | |
| | | chargingOrder.setWhitelistServiceRefundAmount(BigDecimal.ZERO); |
| | | chargingOrder.setVipDiscountAmount(BigDecimal.ZERO); |
| | | chargingOrder.setOrderSource(1); |
| | | if (thirdPayAmount.compareTo(BigDecimal.ZERO) > 0) { |
| | | chargingOrder.setSourceName("ICBC"); |
| | | } |
| | | chargingOrder.setTitle("【充电桩充电】" + chargingPile.getNumber() + "号桩/" + tChargingGun.getCode() + "号枪"); |
| | | |
| | | Site site = siteClient.getSiteByIds(Arrays.asList(tChargingGun.getSiteId())).getData().get(0); |
| | |
| | | } |
| | | //会员优惠折扣将其计入增加充电时长(增加总充电金额) |
| | | //如果充电总金额未使用完,则需要退回费用=(原金额/总金额)*(总金额-实际充电金额) |
| | | if (1 == addChargingOrder.getPaymentType()) { |
| | | PaymentOrder paymentOrder = new PaymentOrder(); |
| | | paymentOrder.setCode(chargingOrder.getCode()); |
| | | paymentOrder.setAmount(thirdPayAmount); |
| | | paymentOrder.setOpenId(appUser.getWxOpenid()); |
| | | paymentOrder.setDescription("充电充值"); |
| | | paymentOrder.setNotifyUrl("/payment/wx/pay/notify"); |
| | | Map<String, Object> data = wxPaymentClient.orderPay(paymentOrder).getData(); |
| | | IcbcPaymentReq req = new IcbcPaymentReq(); |
| | | req.setOutTradeNo(chargingOrder.getCode()); |
| | | req.setAmount(thirdPayAmount); |
| | | req.setSubject("充电充值"); |
| | | req.setBody("充电充值"); |
| | | req.setPaymentType(addChargingOrder.getPaymentType()); |
| | | req.setNotifyUrl("/payment/icbc/callBack"); |
| | | req.setReturnUrl(addChargingOrder.getReturnUrl()); |
| | | req.setOpenId(Integer.valueOf(1).equals(addChargingOrder.getPaymentType()) ? appUser.getWxOpenid() : appUser.getAliOpenid()); |
| | | req.setProfitSharing(buildIcbcProfitSharing(site)); |
| | | IcbcPaymentResp data = icbcPaymentClient.payment(req).getData(); |
| | | if (isIcbcPaymentCreated(data)) { |
| | | return AjaxResult.success(data); |
| | | } |
| | | if (2 == addChargingOrder.getPaymentType()) { |
| | | AliPaymentReq req = new AliPaymentReq(); |
| | | req.setOutTradeNo(chargingOrder.getCode()); |
| | | req.setTotalAmount(thirdPayAmount.toString()); |
| | | req.setSubject("充电充值"); |
| | | req.setBuyerOpenId(appUser.getAliOpenid()); |
| | | req.setBody("充电充值"); |
| | | req.setNotifyUrl("/payment/ali/callBack"); |
| | | AliPaymentResp data = aliPaymentClient.payment(req).getData(); |
| | | if (null != data) { |
| | | return AjaxResult.success(data); |
| | | } |
| | | } |
| | | if (3 == addChargingOrder.getPaymentType()) { |
| | | AliPaymentReq req = new AliPaymentReq(); |
| | | req.setOutTradeNo(chargingOrder.getCode()); |
| | | req.setTotalAmount(thirdPayAmount.toString()); |
| | | req.setSubject("充电充值"); |
| | | req.setBuyerOpenId(appUser.getAliOpenid()); |
| | | req.setBody("充电充值"); |
| | | req.setNotifyUrl("/payment/aliH5/callBack"); |
| | | req.setReturnUrl(addChargingOrder.getReturnUrl()); |
| | | String data = h5AliPaymentClient.payment(req).getData(); |
| | | if (StringUtils.hasLength(data)) { |
| | | return AjaxResult.success("操作成功", data); |
| | | } |
| | | } |
| | | throw new RuntimeException("无效的支付方式"); |
| | | throw new RuntimeException("调起工行支付失败"); |
| | | } |
| | | |
| | | |
| | |
| | | return; |
| | | } |
| | | this.updateById(chargingOrder); |
| | | if (isIcbcOrder(chargingOrder)) { |
| | | refundByIcbcAndFinish(chargingOrder, chargingOrderRefund, thirdRefundAmount, "充电失败,取消充电订单", false, |
| | | recordedRefundAmount, balanceRefundAmount); |
| | | return; |
| | | } |
| | | if (thirdRefundAmount.compareTo(BigDecimal.ZERO) > 0 && 1 == rechargePaymentType) { |
| | | WxPaymentRefundModel model = new WxPaymentRefundModel(); |
| | | model.setOut_trade_no(chargingOrder.getCode()); |
| | |
| | | finishManualChargingRefund(chargingOrder, chargingOrderRefund, money, balanceRefundAmount, thirdRefundAmount); |
| | | return; |
| | | } |
| | | if (isIcbcOrder(chargingOrder)) { |
| | | refundByIcbcAndFinish(chargingOrder, chargingOrderRefund, thirdRefundAmount, "充电完成退款", true, |
| | | money, balanceRefundAmount); |
| | | return; |
| | | } |
| | | if (1 == rechargePaymentType) { |
| | | WxPaymentRefundModel model = new WxPaymentRefundModel(); |
| | | model.setOut_trade_no(chargingOrder.getCode()); |
| | |
| | | chargingOrder.setRefundSerialNumber(refund_id); |
| | | chargingOrder.setRefundTime(LocalDateTime.now()); |
| | | this.updateById(chargingOrder); |
| | | triggerChargingOrderShare(this.getById(order.getId())); |
| | | } |
| | | } catch (InterruptedException e) { |
| | | throw new RuntimeException(e); |
| | |
| | | chargingOrder.setRefundStatus(2); |
| | | chargingOrder.setRefundTime(LocalDateTime.now()); |
| | | this.updateById(chargingOrder); |
| | | triggerChargingOrderShare(chargingOrder); |
| | | } else if (isIcbcOrder(chargingOrder)) { |
| | | refundByIcbcAndFinish(chargingOrder, chargingOrderRefund, thirdRefundAmount, "充电完成退款", false, |
| | | refundAmount, balanceRefundAmount); |
| | | } else if (1 == rechargePaymentType) { |
| | | WxPaymentRefundModel model = new WxPaymentRefundModel(); |
| | | model.setOut_trade_no(chargingOrder.getCode()); |
| | |
| | | chargingOrder.setRefundStatus(2); |
| | | chargingOrder.setRefundTime(LocalDateTime.now()); |
| | | this.updateById(chargingOrder); |
| | | triggerChargingOrderShare(chargingOrder); |
| | | } else { |
| | | //构建退款明细 |
| | | TChargingOrderRefund chargingOrderRefund = new TChargingOrderRefund(); |
| | |
| | | chargingOrder.setRefundStatus(2); |
| | | chargingOrder.setRefundTime(LocalDateTime.now()); |
| | | this.updateById(chargingOrder); |
| | | triggerChargingOrderShare(chargingOrder); |
| | | } |
| | | } |
| | | |
| | |
| | | summaryData.setElectricPrice(electronic_reduce.setScale(2, BigDecimal.ROUND_HALF_EVEN)); |
| | | summaryData.setServicePrice(serviceMoney.setScale(2, BigDecimal.ROUND_HALF_EVEN)); |
| | | chargingOrderSummaryDataService.save(summaryData); |
| | | if (channelRefundAmount.compareTo(BigDecimal.ZERO) == 0 && Integer.valueOf(2).equals(chargingOrder.getRefundStatus())) { |
| | | triggerChargingOrderShare(chargingOrder, site); |
| | | } |
| | | |
| | | //推送监管平台订单状态和订单详情 |
| | | ChargingOrderVo chargingOrderVo = new ChargingOrderVo(); |
| | |
| | | if (thirdRefundAmount.compareTo(BigDecimal.ZERO) == 0) { |
| | | chargingOrderRefund.setRefundSerialNumber("BALANCE"); |
| | | finishManualChargingRefund(tChargingOrder, chargingOrderRefund, manualRefundAmount, balanceRefundAmount, thirdRefundAmount); |
| | | } |
| | | else if (isIcbcOrder(tChargingOrder)) { |
| | | refundByIcbcAndFinish(tChargingOrder, chargingOrderRefund, thirdRefundAmount, "取消订单", true, |
| | | manualRefundAmount, balanceRefundAmount); |
| | | } |
| | | else if (1 == tChargingOrder.getRechargePaymentType()) { |
| | | WxPaymentRefundModel model = new WxPaymentRefundModel(); |
| | |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | private boolean isIcbcRemovableStatus(String status, TChargingOrder order) { |
| | | if (!StringUtils.hasLength(status)) { |
| | | return false; |
| | | } |
| | | if (Arrays.asList("FAIL", "FAILED", "CLOSED", "TRADE_CLOSED", "REVOKED", "PAYERROR").contains(status)) { |
| | | return true; |
| | | } |
| | | // 工行B2C不支持主动关单;NOTPAY超时仅释放本地待支付订单,不代表远端订单已关闭。 |
| | | return "NOTPAY".equals(status) && isLocalPendingOrderExpired(order); |
| | | } |
| | | |
| | | private boolean isLocalPendingOrderExpired(TChargingOrder order) { |
| | | return order != null && order.getCreateTime() != null && order.getCreateTime().plusMinutes(30).isBefore(LocalDateTime.now()); |
| | | } |
| | | |
| | | private boolean isIcbcPaymentCreated(IcbcPaymentResp resp) { |
| | | if (resp == null || !StringUtils.hasLength(resp.getStatus())) { |
| | | return false; |
| | | } |
| | | return "SUCCESS".equals(resp.getStatus()); |
| | | } |
| | | |
| | | private void triggerChargingOrderShare(TChargingOrder chargingOrder, Site site) { |
| | | try { |
| | | if (!isNeedChargingOrderShare(chargingOrder)) { |
| | | return; |
| | | } |
| | | Partner partner = loadPartner(site); |
| | | for (TChargingOrderShareRecord shareRecord : buildChargingOrderShareRecords(chargingOrder, site, partner)) { |
| | | TChargingOrderShareRecord exist = chargingOrderShareRecordMapper.selectOne(new LambdaQueryWrapper<TChargingOrderShareRecord>() |
| | | .eq(TChargingOrderShareRecord::getChargingOrderCode, shareRecord.getChargingOrderCode()) |
| | | .eq(TChargingOrderShareRecord::getPayChannel, shareRecord.getPayChannel()) |
| | | .eq(TChargingOrderShareRecord::getDelFlag, false) |
| | | .last("limit 1")); |
| | | if (exist != null && ("SUCCESS".equals(exist.getOnlineShareStatus()) || "PROCESSING".equals(exist.getOnlineShareStatus()))) { |
| | | continue; |
| | | } |
| | | shareRecord = mergeChargingOrderShareRecord(exist, shareRecord); |
| | | saveOrUpdateChargingOrderShareRecord(shareRecord); |
| | | if (Boolean.TRUE.equals(shareRecord.getNeedOnlineShare())) { |
| | | processIcbcOnlineShare(shareRecord, partner); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("充电订单分账记录触发失败,订单号:{}", chargingOrder == null ? null : chargingOrder.getCode(), e); |
| | | recordChargingOrderShareFailure(chargingOrder, site, e); |
| | | } |
| | | } |
| | | |
| | | private void triggerChargingOrderShare(TChargingOrder chargingOrder) { |
| | | try { |
| | | if (chargingOrder == null || chargingOrder.getSiteId() == null) { |
| | | return; |
| | | } |
| | | R<List<Site>> siteResult = siteClient.getSiteByIds(Arrays.asList(chargingOrder.getSiteId())); |
| | | List<Site> sites = siteResult == null ? null : siteResult.getData(); |
| | | Site site = sites == null || sites.isEmpty() ? null : sites.get(0); |
| | | triggerChargingOrderShare(chargingOrder, site); |
| | | } catch (Exception e) { |
| | | log.error("充电订单分账站点加载失败,订单号:{}", chargingOrder == null ? null : chargingOrder.getCode(), e); |
| | | } |
| | | } |
| | | |
| | | private boolean isNeedChargingOrderShare(TChargingOrder chargingOrder) { |
| | | return chargingOrder != null |
| | | && Integer.valueOf(5).equals(chargingOrder.getStatus()) |
| | | && Integer.valueOf(2).equals(chargingOrder.getRechargePaymentStatus()) |
| | | && Integer.valueOf(2).equals(chargingOrder.getRefundStatus()) |
| | | && getChargingOrderShareBaseAmount(chargingOrder).compareTo(BigDecimal.ZERO) > 0; |
| | | } |
| | | |
| | | private Partner loadPartner(Site site) { |
| | | if (site == null || site.getPartnerId() == null) { |
| | | return null; |
| | | } |
| | | R<Partner> partnerResult = partnerClient.getPartnerById(site.getPartnerId()); |
| | | return partnerResult == null ? null : partnerResult.getData(); |
| | | } |
| | | |
| | | private List<TChargingOrderShareRecord> buildChargingOrderShareRecords(TChargingOrder chargingOrder, Site site, Partner partner) { |
| | | BigDecimal actualThirdPay = getActualThirdPayAmount(chargingOrder); |
| | | BalanceShareAmount balanceShareAmount = getBalanceShareAmount(chargingOrder); |
| | | BigDecimal actualBalancePay = balanceShareAmount.getPrincipalAmount(); |
| | | BigDecimal actualTotalPay = actualThirdPay.add(actualBalancePay).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal shareElectricityAmount = getChargingOrderShareElectricityAmount(chargingOrder); |
| | | BigDecimal shareServiceAmount = getChargingOrderShareServiceAmount(chargingOrder); |
| | | BigDecimal shareBaseAmount = shareElectricityAmount.add(shareServiceAmount).setScale(2, RoundingMode.HALF_UP); |
| | | if (actualTotalPay.compareTo(BigDecimal.ZERO) <= 0 && isWhitelistOrder(chargingOrder)) { |
| | | actualTotalPay = shareBaseAmount; |
| | | } |
| | | if (actualTotalPay.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return Collections.emptyList(); |
| | | } |
| | | if (!isWhitelistOrder(chargingOrder) && shareBaseAmount.compareTo(BigDecimal.ZERO) > 0 && actualTotalPay.compareTo(shareBaseAmount) < 0) { |
| | | BigDecimal effectiveRatio = actualTotalPay.divide(shareBaseAmount, 6, RoundingMode.HALF_UP); |
| | | shareElectricityAmount = shareElectricityAmount.multiply(effectiveRatio).setScale(2, RoundingMode.HALF_UP); |
| | | shareServiceAmount = shareServiceAmount.multiply(effectiveRatio).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | List<TChargingOrderShareRecord> records = new ArrayList<>(); |
| | | if (actualBalancePay.compareTo(BigDecimal.ZERO) > 0) { |
| | | records.add(buildChargingOrderShareRecord(chargingOrder, site, partner, "BALANCE", actualBalancePay, |
| | | balanceShareAmount.getGiftAmount(), actualTotalPay, shareElectricityAmount, shareServiceAmount)); |
| | | } |
| | | if (actualThirdPay.compareTo(BigDecimal.ZERO) > 0) { |
| | | records.add(buildChargingOrderShareRecord(chargingOrder, site, partner, resolveThirdPayChannel(chargingOrder), actualThirdPay, |
| | | BigDecimal.ZERO, actualTotalPay, shareElectricityAmount, shareServiceAmount)); |
| | | } |
| | | if (records.isEmpty() && isWhitelistOrder(chargingOrder)) { |
| | | records.add(buildChargingOrderShareRecord(chargingOrder, site, partner, "WHITELIST", actualTotalPay, |
| | | BigDecimal.ZERO, actualTotalPay, shareElectricityAmount, shareServiceAmount)); |
| | | } |
| | | return records; |
| | | } |
| | | |
| | | private TChargingOrderShareRecord buildChargingOrderShareRecord(TChargingOrder chargingOrder, Site site, Partner partner, |
| | | String payChannel, BigDecimal channelPayAmount, BigDecimal channelGiftAmount, |
| | | BigDecimal actualTotalPay, BigDecimal shareElectricityAmount, |
| | | BigDecimal shareServiceAmount) { |
| | | TChargingOrderShareRecord record = new TChargingOrderShareRecord(); |
| | | record.setChargingOrderId(chargingOrder.getId()); |
| | | record.setChargingOrderCode(chargingOrder.getCode()); |
| | | record.setSiteId(chargingOrder.getSiteId()); |
| | | record.setPartnerId(site == null ? null : site.getPartnerId()); |
| | | record.setPayChannel(payChannel); |
| | | record.setChannelPayAmount(money(channelPayAmount)); |
| | | record.setChannelGiftAmount(money(channelGiftAmount)); |
| | | BigDecimal channelRatio = channelPayAmount.divide(actualTotalPay, 6, RoundingMode.HALF_UP); |
| | | record.setChannelPayRatio(channelRatio); |
| | | BigDecimal channelElectricityAmount = shareElectricityAmount.multiply(channelRatio).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal channelServiceAmount = shareServiceAmount.multiply(channelRatio).setScale(2, RoundingMode.HALF_UP); |
| | | record.setChannelElectricityAmount(channelElectricityAmount); |
| | | record.setChannelServiceAmount(channelServiceAmount); |
| | | record.setElectricityShareRatio(partner == null ? null : partner.getElectricityShareRatio()); |
| | | record.setServiceShareRatio(partner == null ? null : partner.getServiceShareRatio()); |
| | | BigDecimal electricityShareAmount = calculateShareAmount(channelElectricityAmount, record.getElectricityShareRatio()); |
| | | BigDecimal serviceShareAmount = calculateShareAmount(channelServiceAmount, record.getServiceShareRatio()); |
| | | record.setElectricityShareAmount(electricityShareAmount); |
| | | record.setServiceShareAmount(serviceShareAmount); |
| | | record.setShareAmount(electricityShareAmount.add(serviceShareAmount).setScale(2, RoundingMode.HALF_UP)); |
| | | record.setNeedOnlineShare("ICBC".equals(payChannel) && record.getShareAmount().compareTo(BigDecimal.ZERO) > 0); |
| | | record.setOnlineShareStatus(Boolean.TRUE.equals(record.getNeedOnlineShare()) ? "INIT" : "RECORDED"); |
| | | if ("ICBC".equals(payChannel)) { |
| | | record.setSeqNo(chargingOrder.getCode()); |
| | | record.setBusiType("1"); |
| | | record.setOriTrxDate(chargingOrder.getPayTime() == null ? LocalDate.now() : chargingOrder.getPayTime().toLocalDate()); |
| | | record.setSubOrderId("ICBCS" + chargingOrder.getCode()); |
| | | } |
| | | if ("ICBC".equals(payChannel) && partner != null) { |
| | | record.setSubMerId(partner.getIcbcSubMerId()); |
| | | record.setSubMerPrtclNo(partner.getIcbcSubMerPrtclNo()); |
| | | record.setMerAcct(partner.getIcbcMerAcct()); |
| | | } |
| | | record.setRetryCount(0); |
| | | record.setDelFlag(false); |
| | | return record; |
| | | } |
| | | |
| | | private TChargingOrderShareRecord mergeChargingOrderShareRecord(TChargingOrderShareRecord exist, TChargingOrderShareRecord fresh) { |
| | | if (exist == null) { |
| | | return fresh; |
| | | } |
| | | fresh.setId(exist.getId()); |
| | | fresh.setRetryCount(exist.getRetryCount() == null ? 0 : exist.getRetryCount()); |
| | | fresh.setRequestJson(exist.getRequestJson()); |
| | | fresh.setResponseJson(exist.getResponseJson()); |
| | | fresh.setIcbcOrderId(exist.getIcbcOrderId()); |
| | | fresh.setSubOrderSeq(exist.getSubOrderSeq()); |
| | | fresh.setSubOrderTrxid(exist.getSubOrderTrxid()); |
| | | fresh.setRecResult(exist.getRecResult()); |
| | | fresh.setLastRetryTime(exist.getLastRetryTime()); |
| | | return fresh; |
| | | } |
| | | |
| | | private BigDecimal getActualThirdPayAmount(TChargingOrder chargingOrder) { |
| | | return getRefundableThirdPayAmount(chargingOrder).subtract(money(chargingOrder.getThirdRefundAmount())).max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private BigDecimal getActualBalancePayAmount(TChargingOrder chargingOrder) { |
| | | return money(chargingOrder.getBalancePayAmount()).subtract(money(chargingOrder.getBalanceRefundAmount())).max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private BalanceShareAmount getBalanceShareAmount(TChargingOrder chargingOrder) { |
| | | if (chargingOrder == null || chargingOrder.getId() == null) { |
| | | return new BalanceShareAmount(BigDecimal.ZERO, BigDecimal.ZERO); |
| | | } |
| | | BigDecimal fallbackBalancePay = getActualBalancePayAmount(chargingOrder); |
| | | if (fallbackBalancePay.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return new BalanceShareAmount(BigDecimal.ZERO, BigDecimal.ZERO); |
| | | } |
| | | List<Map<String, Object>> rows = this.baseMapper.exchangeCodeBalanceIncomeByOrderIds(Collections.singletonList(chargingOrder.getId())); |
| | | if (rows == null || rows.isEmpty()) { |
| | | return new BalanceShareAmount(fallbackBalancePay, BigDecimal.ZERO); |
| | | } |
| | | Map<String, Object> row = rows.get(0); |
| | | BigDecimal principalAmount = getSummaryDecimal(row, "balanceIncome").max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal giftAmount = getSummaryDecimal(row, "balanceGiftIncome").max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP); |
| | | return new BalanceShareAmount(principalAmount, giftAmount); |
| | | } |
| | | |
| | | private BigDecimal getChargingOrderShareElectricityAmount(TChargingOrder chargingOrder) { |
| | | if (chargingOrder == null) { |
| | | return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | return money(chargingOrder.getElectrovalence()).add(money(chargingOrder.getWhitelistElectricRefundAmount())).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private BigDecimal getChargingOrderShareServiceAmount(TChargingOrder chargingOrder) { |
| | | if (chargingOrder == null) { |
| | | return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | return money(chargingOrder.getServiceCharge()).add(money(chargingOrder.getWhitelistServiceRefundAmount())).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private BigDecimal getChargingOrderShareBaseAmount(TChargingOrder chargingOrder) { |
| | | return getChargingOrderShareElectricityAmount(chargingOrder).add(getChargingOrderShareServiceAmount(chargingOrder)).setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private String resolveThirdPayChannel(TChargingOrder chargingOrder) { |
| | | if ("ICBC".equals(chargingOrder.getSourceName())) { |
| | | return "ICBC"; |
| | | } |
| | | if (Integer.valueOf(1).equals(chargingOrder.getRechargePaymentType())) { |
| | | return "WECHAT"; |
| | | } |
| | | if (Integer.valueOf(2).equals(chargingOrder.getRechargePaymentType()) || Integer.valueOf(3).equals(chargingOrder.getRechargePaymentType())) { |
| | | return "ALIPAY"; |
| | | } |
| | | return "OTHER"; |
| | | } |
| | | |
| | | private void processIcbcOnlineShare(TChargingOrderShareRecord record, Partner partner) { |
| | | try { |
| | | String invalidReason = validateIcbcShareRecord(record, partner); |
| | | if (StringUtils.hasLength(invalidReason)) { |
| | | markChargingOrderShare(record, "FAIL", invalidReason); |
| | | return; |
| | | } |
| | | IcbcSubOrderSplitReq req = buildIcbcSubOrderSplitReq(record); |
| | | record.setRequestJson(JSON.toJSONString(req)); |
| | | record.setOnlineShareStatus("PROCESSING"); |
| | | record.setRetryCount(record.getRetryCount() == null ? 1 : record.getRetryCount() + 1); |
| | | record.setLastRetryTime(LocalDateTime.now()); |
| | | saveOrUpdateChargingOrderShareRecord(record); |
| | | |
| | | R<IcbcSubOrderSplitResp> result = icbcPaymentClient.splitSubOrder(req); |
| | | IcbcSubOrderSplitResp resp = result == null ? null : result.getData(); |
| | | record.setResponseJson(resp == null ? (result == null ? null : JSON.toJSONString(result)) : resp.getRawResponse()); |
| | | if (result == null || result.getCode() != 200 || resp == null) { |
| | | markChargingOrderShare(record, "FAIL", result == null ? "工行拆分接口无响应" : result.getMsg()); |
| | | return; |
| | | } |
| | | record.setIcbcOrderId(resp.getOrderId()); |
| | | record.setSubOrderSeq(resp.getSubOrderSeq()); |
| | | record.setSubOrderTrxid(resp.getSubOrderTrxid()); |
| | | record.setRecResult(resp.getRecResult()); |
| | | if ("SUCCESS".equals(resp.getStatus()) && ("0".equals(resp.getRecResult()) || !StringUtils.hasLength(resp.getRecResult()))) { |
| | | markChargingOrderShare(record, "SUCCESS", null); |
| | | } else { |
| | | markChargingOrderShare(record, "FAIL", "工行拆分未成功:" + firstNotBlank(resp.getRecResult(), resp.getStatus())); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("工行线上分账处理失败,订单号:{},渠道:{}", record == null ? null : record.getChargingOrderCode(), record == null ? null : record.getPayChannel(), e); |
| | | if (record != null) { |
| | | markChargingOrderShare(record, "FAIL", firstNotBlank(e.getMessage(), "工行线上分账处理异常")); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void recordChargingOrderShareFailure(TChargingOrder chargingOrder, Site site, Exception e) { |
| | | try { |
| | | if (chargingOrder == null || !"ICBC".equals(chargingOrder.getSourceName()) || getActualThirdPayAmount(chargingOrder).compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | | TChargingOrderShareRecord record = chargingOrderShareRecordMapper.selectOne(new LambdaQueryWrapper<TChargingOrderShareRecord>() |
| | | .eq(TChargingOrderShareRecord::getChargingOrderCode, chargingOrder.getCode()) |
| | | .eq(TChargingOrderShareRecord::getPayChannel, "ICBC") |
| | | .eq(TChargingOrderShareRecord::getDelFlag, false) |
| | | .last("limit 1")); |
| | | if (record == null) { |
| | | BigDecimal actualThirdPay = getActualThirdPayAmount(chargingOrder); |
| | | BigDecimal actualTotalPay = actualThirdPay.add(getBalanceShareAmount(chargingOrder).getPrincipalAmount()).setScale(2, RoundingMode.HALF_UP); |
| | | BigDecimal shareElectricityAmount = getChargingOrderShareElectricityAmount(chargingOrder); |
| | | BigDecimal shareServiceAmount = getChargingOrderShareServiceAmount(chargingOrder); |
| | | record = buildChargingOrderShareRecord(chargingOrder, site, null, "ICBC", actualThirdPay, BigDecimal.ZERO, |
| | | actualTotalPay.compareTo(BigDecimal.ZERO) > 0 ? actualTotalPay : actualThirdPay, shareElectricityAmount, shareServiceAmount); |
| | | } |
| | | record.setNeedOnlineShare(true); |
| | | markChargingOrderShare(record, "FAIL", firstNotBlank(e.getMessage(), "充电订单分账记录触发异常")); |
| | | } catch (Exception ex) { |
| | | log.error("充电订单分账失败补偿记录落库失败,订单号:{}", chargingOrder == null ? null : chargingOrder.getCode(), ex); |
| | | } |
| | | } |
| | | |
| | | private String validateIcbcShareRecord(TChargingOrderShareRecord record, Partner partner) { |
| | | if (partner == null) { |
| | | return "站点未配置合作商,无法工行拆分"; |
| | | } |
| | | if (partner.getElectricityShareRatio() == null && partner.getServiceShareRatio() == null) { |
| | | return "合作商未配置分成比例,跳过工行拆分"; |
| | | } |
| | | if (!StringUtils.hasLength(partner.getIcbcSubMerId())) { |
| | | return "合作商未配置工行二级商户编号"; |
| | | } |
| | | if (!StringUtils.hasLength(partner.getIcbcSubMerPrtclNo())) { |
| | | return "合作商未配置工行二级商户协议编号"; |
| | | } |
| | | if (money(record.getShareAmount()).compareTo(BigDecimal.ZERO) <= 0) { |
| | | return "工行拆分金额为0"; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private BigDecimal calculateShareAmount(BigDecimal amount, BigDecimal ratio) { |
| | | if (amount == null || ratio == null) { |
| | | return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | return amount.multiply(ratio).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private IcbcSubOrderSplitReq buildIcbcSubOrderSplitReq(TChargingOrderShareRecord record) { |
| | | IcbcSubOrderSplitReq req = new IcbcSubOrderSplitReq(); |
| | | req.setChargingOrderCode(record.getChargingOrderCode()); |
| | | req.setSeqNo(record.getSeqNo()); |
| | | req.setBusiType(record.getBusiType()); |
| | | req.setOriMerId(record.getOriMerId()); |
| | | req.setOriTrxDate(record.getOriTrxDate() == null ? null : record.getOriTrxDate().toString()); |
| | | req.setSubOrderId(record.getSubOrderId()); |
| | | req.setSubMerId(record.getSubMerId()); |
| | | req.setSubMerPrtclNo(record.getSubMerPrtclNo()); |
| | | req.setClassifyAmt(record.getShareAmount()); |
| | | req.setMerAcct(record.getMerAcct()); |
| | | req.setMerSpInfor("充电订单分账:" + record.getChargingOrderCode()); |
| | | return req; |
| | | } |
| | | |
| | | private void markChargingOrderShare(TChargingOrderShareRecord record, String status, String reason) { |
| | | record.setOnlineShareStatus(status); |
| | | record.setFailReason(reason); |
| | | saveOrUpdateChargingOrderShareRecord(record); |
| | | } |
| | | |
| | | private void saveOrUpdateChargingOrderShareRecord(TChargingOrderShareRecord record) { |
| | | if (record.getId() == null) { |
| | | chargingOrderShareRecordMapper.insert(record); |
| | | } else { |
| | | chargingOrderShareRecordMapper.updateById(record); |
| | | } |
| | | } |
| | | |
| | | private String buildIcbcProfitSharing(Site site) { |
| | | if (site == null || site.getPartnerId() == null) { |
| | | return null; |
| | | } |
| | | R<Partner> partnerResult = partnerClient.getPartnerById(site.getPartnerId()); |
| | | Partner partner = partnerResult == null ? null : partnerResult.getData(); |
| | | if (partner == null || !StringUtils.hasLength(partner.getIcbcSubMerId()) || !StringUtils.hasLength(partner.getIcbcSubMerPrtclNo())) { |
| | | return null; |
| | | } |
| | | if (partner.getElectricityShareRatio() == null && partner.getServiceShareRatio() == null) { |
| | | return null; |
| | | } |
| | | JSONObject data = new JSONObject(); |
| | | data.put("icbcPartnerId", partner.getIcbcPartnerId()); |
| | | data.put("icbcSubMerId", partner.getIcbcSubMerId()); |
| | | data.put("icbcSubMerPrtclNo", partner.getIcbcSubMerPrtclNo()); |
| | | data.put("icbcMerAcct", partner.getIcbcMerAcct()); |
| | | if (partner.getElectricityShareRatio() != null) { |
| | | data.put("electricityShareRatio", partner.getElectricityShareRatio()); |
| | | } |
| | | if (partner.getServiceShareRatio() != null) { |
| | | data.put("serviceShareRatio", partner.getServiceShareRatio()); |
| | | } |
| | | return data.toJSONString(); |
| | | } |
| | | |
| | | private static class BalanceShareAmount { |
| | | private final BigDecimal principalAmount; |
| | | private final BigDecimal giftAmount; |
| | | |
| | | private BalanceShareAmount(BigDecimal principalAmount, BigDecimal giftAmount) { |
| | | this.principalAmount = principalAmount == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : principalAmount.setScale(2, RoundingMode.HALF_UP); |
| | | this.giftAmount = giftAmount == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : giftAmount.setScale(2, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | private BigDecimal getPrincipalAmount() { |
| | | return principalAmount; |
| | | } |
| | | |
| | | private BigDecimal getGiftAmount() { |
| | | return giftAmount; |
| | | } |
| | | } |
| | | |
| | | private String firstNotBlank(String... values) { |
| | | for (String value : values) { |
| | | if (StringUtils.hasLength(value)) { |
| | | return value; |
| | | } |
| | | } |
| | | return ""; |
| | | } |
| | | } |