微信支付发起退款时,异常解决
保证自身服务代码正常情况下
微信发起退款的异常分两种
第一种是业务异常:账户余额不足 等退款失败|或者超额退款失败
第二种是网络异常:创建微信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等 ,如果是就记录退款订单信息,
然后由定时任务调度退款异常订单,判断是否是网络错误等需要重新发起退款的订单,执行微信退款调用重放。。。
还可以把错误订单信息发送时,通过短信推送到管理员手里,做一次手动补偿重发退款的事件
基本就大概率解决退款问题了
退款微信通知回调你的接口时;
建议做成同步接口,保证你的业务处理成功才给微信返回成功,否则就返回给微信失败。不要自己去维护微信推送给你成功,你业务处理失败场景,减少服务的压力和性能开销。。。。
作者:隔壁老郭
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
Java入门到入坟
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!