微信支付发起退款时,异常解决

保证自身服务代码正常情况下

微信发起退款的异常分两种

第一种是业务异常:账户余额不足 等退款失败|或者超额退款失败

第二种是网络异常:创建微信Config时会执行一次微信校验;会调用api.mch.weixin.qq.com 校验 | 还有真实发起退款可能也会因为api.mch.weixin.qq.com的DNS解析出错导致连接失败

代码如下

  /**
     * 当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,
     * <p>
     * 微信支付将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
     *
     * @param cashierPayment cashierPayment
     * @return Refund
     */
    @SuppressWarnings("unchecked,deprecation,unused")
    public Refund refundOrderV3(CashierPayment cashierPayment, CashierPayment originalOrder) {
        Refund refund = null;
        try {
            Config wxConfig = wxConfigService.getWxConfig(cashierPayment.getPayChannelId(), new PayChannel());
            String wxCallBackUrl = wxConfigService.getWxCallBackUrl(true, originalOrder.getMerchantId());
            RefundService service = new RefundService.Builder().config(wxConfig).build();
            CreateRequest request = new CreateRequest();
            // 退款收银台订单号
            request.setOutRefundNo(cashierPayment.getPaymentNo());
            // 微信支付订单号(原支付渠道流水号)
            request.setTransactionId(cashierPayment.getChannelFlowNo());
            // 使用可用余额退款 ===== request.setFundsAccount(ReqFundsAccount.AVAILABLE);
            request.setNotifyUrl(wxCallBackUrl);
            // 退款金额
            AmountReq amountReq = new AmountReq();
            // 原始订单金额
            amountReq.setTotal(originalOrder.getAmount());
            // 退款订单金额
            amountReq.setRefund(cashierPayment.getRefundAmount());
            amountReq.setCurrency(StringUtils.isEmpty(cashierPayment.getCurrencyType()) ? "CNY" : cashierPayment.getCurrencyType());
            request.setAmount(amountReq);
            // 发起退款
            refund = service.create(request);
        } catch (ServiceException e) {
            // 捕捉-微信退款下单时返回的状态码是发送HTTP请求成功,服务返回异常。例如返回状态码小于200或大于等于300。
            String errorCode = e.getErrorCode();
            // 判断由定时任务调度过来的|跳过本地环境---直接操作微信环境---引起本地和微信订单不同步操作
            String message = e.getErrorMessage();
            if (errorCode.equals("INVALID_REQUEST") && message.equals("订单已全额退款")) {
                log.info("订单已全额退款" + cashierPayment.getPaymentNo());
                throw new BusinessException(Status.SUCCESS.name());
            }
            log.error("发起退款失败,错误码:{}", errorCode, e);
            throw new BusinessException(errorCode);
        } catch (Exception e) {
            if (e instanceof BusinessException) {
                log.error(e.getMessage(), e);
                throw e;
            }
            // 防止网络连接不上---标记失败放入重试队列
            log.error(cashierPayment.getPaymentNo() + "发起退款失败:{}", e.getMessage(), e);
            throw new BusinessException("HTTP_TIME_OUT");
        }
        log.debug("退款处理--微信结果:{}", refund.toString());
        if (refund.getStatus() == Status.SUCCESS ||
                refund.getStatus() == Status.PROCESSING) {
            log.info("发起退款成功:" + cashierPayment.getPaymentNo());
            return refund;
        } else {
            log.error("发起退款失败:{}", refund.getStatus());
            throw new BusinessException("发起退款失败");
        }

核心1-业务错误:根据微信侧的ServiceException异常获取错误码;可以在不同类型错误时做出对应处理

比如 errorCode.equals("INVALID_REQUEST") && message.equals("订单已全额退款") 这种就是已经退款完了,还发起的可以做幂等或者别的操作

比如 NOT_ENOUGH 错误码,代表余额不足,无法发起退款。。可以提醒管理员需要往微信里面充钱了,或者建议微信账户始终保留部分余额。。防止结算后账户没钱用于客户退款

比如 USER_ACCOUNT_ABNORMAL 等等......

核心2-网络错误:

可以捕捉异常:判断是否是网络错误类型 api.mch.weixin.qq.com: Name or service not known等 ,如果是就记录退款订单信息,

然后由定时任务调度退款异常订单,判断是否是网络错误等需要重新发起退款的订单,执行微信退款调用重放。。。

还可以把错误订单信息发送时,通过短信推送到管理员手里,做一次手动补偿重发退款的事件

基本就大概率解决退款问题了

退款微信通知回调你的接口时;

建议做成同步接口,保证你的业务处理成功才给微信返回成功,否则就返回给微信失败。不要自己去维护微信推送给你成功,你业务处理失败场景,减少服务的压力和性能开销。。。。

posted on 2024-08-29 18:52  白嫖老郭  阅读(93)  评论(0编辑  收藏  举报

导航