微信支付踩坑血泪史(JAVA -V3版本)


背景介绍

最近第一次接触到微信支付,踩了很多坑,赶紧写下来,全是心酸和眼泪。话不多说。开始展示。

项目中使用的接入方式是JSAPI(参考 产品中心 - 微信支付商户平台),当然也可以使用其他接入方式,根据需求来。

微信支付提供了两种对接方式(SDK,工具 | 微信支付商户平台文档中心)。一种是SDK,一种是Client,我个人偏向于SDK,因为它更简单,基本都封装好了。

项目中我们选择的非官方SDK binarywang,没什么别的原因,主要是更方便。

官网:Binary Wang

GitHub:GitHub - binarywang/weixin-java-pay-demo: 基于Spring Boot 和 WxJava 实现的微信支付Java后端Demo

一、接入前准备

接入前需要创建账号等步骤,可以参考官方文档:JSAPI支付-接入前准备 | 微信支付商户平台文档中心​​​​​​​x

二、引入依赖

        <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-mp</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-pay</artifactId>
        </dependency>

三、编写配置文件

开发环境下可以把秘钥存到本地,线上建议放到服务器。

因为支付会有回调和退款等操作,本地接口不能被访问。建议使用内网穿透工具进行测试(Sunny-Ngrok内网转发内网穿透 - 国内内网映射服务器

wechat-pay:
  #商户id
  mchId: 111111
  #公众号appid
  appid: wxce9662d47d419qwvw
  #APIv3密钥
  apiV3key: oLV98ZUF1Kdsfwefgwq7nCazqpew
  #商户证书序列号
  serialNo: 234235r598C803FersvdvB380A4F9CA77C8538AE7F
  #商户私钥
  privateKeyPath: E:/Project/devOps/key/apiclient_key.pem
  privateCertPath: E:/Project/devOps/key/apiclient_cert.pem
  #接收结果通知地址
  notifyUrl: http://howfhu.frfe.idcwfwgye.com/notify/pay/wx
  #退款通知地址
  refundNotifyUrl: http://hu.free.idefengye.com/notify/refund/wx

四、数据库设计

订单表


退款表 支付单表

 订单与支付单和退款单关系为一对多 不同用户扫码会产生多个支付记录,同样退款有多个退款记录。业务代码需要做支付幂等,同一笔不允许重复支付,重复支付需要调用退款接口。

 五、业务代码

 1.控制层

@Api(tags = "订单管理")
@RestController
public class OrderController {
    @Autowired
    private OrderService orderService;

    @Operation(summary = "提交订单")
    @PostMapping("/order/create")
    public BizResult<String> createOrder(@Valid @RequestBody OrderCreateReq req) {
        String orderNO = orderService.createOrder(req);
        return BizResult.create(orderNO);
    }

    @Operation(summary = "订单详情")
    @GetMapping("/order/detail")
    public BizResult<OrderRsp> orderDetail(@RequestParam String orderNo) {
        OrderRsp orderRsp = orderService.orderDetail(orderNo);
        return BizResult.create(orderRsp);
    }

    @Operation(summary = "轮询订单")
    @GetMapping("/order/polling")
    public BizResult<Boolean> pollingOrder(@RequestParam String orderNo) {
        Boolean result = orderService.pollingOrder(orderNo);
        return BizResult.create(result);
    }

    @Operation(summary = "订单列表")
    @PostMapping("/order/list")
    public BizResult<PageData<OrderListRsp>> queryOrderList(@RequestBody OrderListReq req) {
        req.check();
        PageData<OrderListRsp> pageData = orderService.queryOrderList(req);
        return BizResult.create(pageData);
    }

}

2.业务层

@Slf4j
@Service
public class OrderService {


    /**
     * 创建订单
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public String createOrder(OrderCreateReq req) 

        //判断用户是否短时间多次提交 
        // 加锁  RLock rLock

        try {
            //查询商品是否存在  不存在处理异常 
            OrderDO orderDO = OrderConverter.init(req, traceCodeGoodsDO, merchantDO, orderTypeEnum);
            orderMapper.insert(orderDO);
            return orderDO.getOrderNo();
        } finally {
            if (rLock.isHeldByCurrentThread()) {
                rLock.unlock();
            }
        }
    }


    /**
     * 查询订单详情
     */
    public OrderRsp orderDetail(String orderNo) {
        if (StringUtils.isBlank(orderNo)) {
            throw BizException.create(BaseErrorCodeEnum.PARAMS_ERROR, "订单号不存在");
        }
        //查询订单
        OrderDO orderDO = orderMapper.findByOrderNo(orderNo);
        if (Objects.isNull(orderDO)) {
            throw BizException.create(BizErrorCodeEnum.ORDER_NOT_EXITS);
        }
        return OrderConverter.toOrderRsp(orderDO);
    }


    /**
     * 订单轮询   前端轮询这个接口 判断当前订单状态 进行响应状态修改
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Boolean pollingOrder(String orderNo) {
        if (StringUtils.isBlank(orderNo)) {
            throw BizException.create(BaseErrorCodeEnum.PARAMS_ERROR, "订单号不存在");
        }
        OrderDO orderDO = orderMapper.findByOrderNo(orderNo);
        if (Objects.isNull(orderDO)) {
            throw BizException.create(BizErrorCodeEnum.ORDER_NOT_EXITS);
        }
        //状态支付中 将状态改为未支付
        if (Objects.equals(orderDO.getStatus(), OrderStatusEnum.PAYING.getCode())) {
            orderMapper.updateStatus(orderNo, OrderStatusEnum.UNPAID.getCode(), orderDO.getStatus());
            return false;
        }
        //未支付
        if (Objects.equals(orderDO.getStatus(), OrderStatusEnum.UNPAID.getCode())) {
            return false;
        }
        String payNo = orderDO.getPayNo();
        if (StringUtils.isBlank(payNo)) {
            return false;
        }
        //查询支付单是否存在
        PaymentDO paymentDO = paymentMapper.findByPayNo(payNo);
        if (Objects.isNull(paymentDO)) {
            log.error("轮询订单,支付单不存在。orderNo:{},payNo:{}", orderDO, payNo);
            return false;
        }
        if (!Objects.equals(orderDO.getStatus(), OrderStatusEnum.UNPAID.getCode()) && !Objects.equals(orderDO.getStatus(), OrderStatusEnum.PAYING.getCode())) {
            // 查询需要取消支付单记录
            List<PaymentDO> paymentList = paymentMapper.queryPaymentList(orderDO.getOrderNo(), PayStatusEnum.UNPAID.getCode(), paymentDO.getPayNo());
            if (ObjectUtil.isNotEmpty(paymentList)) {
                // 修改支付单取消状态
                paymentMapper.cancelPay(orderDO.getOrderNo(), PayStatusEnum.CANCEL.getCode(), paymentDO.getPayNo(), PayStatusEnum.UNPAID.getCode());
            }
            return true;
        }
        PayStatusEnum payStatusEnum = LocalEnumUtils.findByCodeWithoutDefault(PayStatusEnum.class, paymentDO.getStatus());
        if (Objects.equals(payStatusEnum, PayStatusEnum.CANCEL)
                || Objects.equals(payStatusEnum, PayStatusEnum.FAIL)) {
            //取消支付或支付失败,可重新扫码创建支付单
            return false;
        } else if (Objects.equals(payStatusEnum, PayStatusEnum.UNPAID)) {
            //支付单待支付,查询第三方渠道
            WxPayOrderQueryV3Result wxPayOrderQueryV3Result = null;
            try {
                WxPayOrderQueryV3Request request = new WxPayOrderQueryV3Request();
                request.setOutTradeNo(paymentDO.getPayNo());
                wxPayOrderQueryV3Result = wxManager.getWxPayService().queryOrderV3(request);
            } catch (WxPayException e) {
                log.error("查询微信支付订单失败。payNo:{}", paymentDO.getPayNo(), e);
                return false;
            }
            //查询第三方支付信息并更新订单
            if (orderManager.queryOrderByWx(wxPayOrderQueryV3Result, orderDO, paymentDO, payStatusEnum)) {
                producerHelper.sendOrderMsg(orderDO);
                return true;
            }
        } else {
            //其他状态,更新订单状态。
            int orgOrderStatus = orderDO.getStatus();
            OrderStatusEnum tarOrderStatus = OrderStatusEnum.PAID;
            if (Objects.equals(payStatusEnum, PayStatusEnum.REFUND)) {
                tarOrderStatus = OrderStatusEnum.REFUND;
                orderDO.setRefundTime(paymentDO.getRefundTime());
                orderDO.setRefundAmount(paymentDO.getRefundAmount());
            }
            orderDO.setFinishTime(paymentDO.getPayTime());
            orderDO.setStatus(tarOrderStatus.getCode());
            orderDO.setUpdateTime(new Date());
            int i = orderMapper.updateStatusByOrderNo(orderDO, orgOrderStatus);
            if (i > 0) {
                producerHelper.sendOrderMsg(orderDO);
            }
            return true;
        }
        return false;
    }

    /**
     * jsapi下单并返回前端唤起参数
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public OrderJsApiRsq jsapiPay(JsAPIReq req) {
        String orderNo = req.getOrderNo();
        String openId = req.getOpenid();
        // 加锁 RLock lock 
        if (lock == null) {
            throw BizException.create(CommonErrorCodeEnum.RESUBMIT_ERROR, "订单支付中");
        }
        //查询订单
        OrderDO orderDO = orderMapper.findByOrderNo(orderNo);
        if (Objects.isNull(orderDO)) {
            throw BizException.create(BizErrorCodeEnum.ORDER_NOT_EXITS);
        }
        Integer orgStatus = orderDO.getStatus();
        //校验重复支付
        if (Objects.equals(orgStatus, OrderStatusEnum.PAID.getCode())) {
            throw BizException.create(BizErrorCodeEnum.ORDER_PAY, "订单已支付,请勿重复支付");
        }
        // 查询支付单,没有则新增
        // 对比openid,如果一样直接发起支付,不一样新增支付单
        PaymentDO paymentDO = paymentMapper.findByPayNo(orderDO.getPayNo());
        if (Objects.isNull(paymentDO) || !Objects.equals(paymentDO.getOpenid(), req.getOpenid())) {
            //插入支付单数据
            paymentMapper.insert(paymentDO);
            //更新订单号中支付单号

            // 幂等更新
            int i = orderMapper.updateStatusByOrderNo(orderDO, orgStatus);
            if (i == 0) {
                throw BizException.create(BizErrorCodeEnum.ORDER_PAY, "订单已支付");
            }
        }

        WxPayUnifiedOrderV3Request request = WxPayConverter.toWxPayUnifiedOrderV3Request(orderDO, paymentDO.getPayNo(), openId);
        WxPayUnifiedOrderV3Result.JsapiResult result = null;
        try {
            result = wxManager.getWxPayService().createOrderV3(TradeTypeEnum.JSAPI, request);
        } catch (WxPayException e) {
            log.error("JSAPI下单支付异常,orderNo:{}", orderNo, e);
            throw BizException.create(BizErrorCodeEnum.ORDER_UNIFIED_ERROR, "支付唤起失败");
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return WxPayConverter.toOrderJsApiRsq(result);
    }

    /**
     * 支付通知
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public OrderPayNotify payNotify(JSONObject jsonObject, HttpServletRequest request, HttpServletResponse response) {
        log.info("微信支付结果通知:{}", jsonObject);
        //获取请求头
        SignatureHeader header = PayUtil.getWXRequestHeader(request);
        //请求体
        //解析回调通知
        WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = getWxPayOrderNotifyV3Result(jsonObject, header);
        WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult();
        WxPayTradeStateEnum wxPayStatus = WxPayTradeStateEnum.findByCode(result.getTradeState());
        //中间态,直接返回,继续通知
        if (Objects.equals(wxPayStatus, WxPayTradeStateEnum.NOT_PAY)
                || Objects.equals(wxPayStatus, WxPayTradeStateEnum.ACCEPT)
                || Objects.equals(wxPayStatus, WxPayTradeStateEnum.USER_PAYING)) {
            log.error("微信支付回调,中间态继续通知,payNo:{}", wxPayOrderNotifyV3Result.getResult().getOutTradeNo());
            response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
            return OrderPayNotify.fail();
        }
        //加锁RLock lock 
        if (lock == null) {
            throw BizException.create(BizErrorCodeEnum.PAY_FAIL);
        }
        try {
            //查询支付单
            PaymentDO paymentDO
            log.info("支付单结果解析:{},支付单号{}", paymentDO, paymentDO.getPayNo());
            //查询订单
            OrderDO orderDO 
            log.info("订单结果解析:{},支付单号{}", orderDO, orderDO.getOrderNo());
            Integer status = paymentDO.getStatus();
            //订单已支付  订单状态不等于未支付和支付中
            if (!Objects.equals(orderDO.getStatus(), OrderStatusEnum.UNPAID.getCode())) {
                if (!Objects.equals(orderDO.getStatus(), OrderStatusEnum.PAYING.getCode())) {
                    //订单已支付 判断是否同一支付单, 是则表示同一笔订单重复支付
                    if (!Objects.equals(orderDO.getPayNo(), paymentDO.getPayNo())) {
                        //支付通知状态
                        RefundDO refundDO = null;
                        if (Objects.equals(wxPayStatus, WxPayTradeStateEnum.SUCCESS)) {
                            //重复支付,新增退款记录
                            refundDO = RefundConverter.init(paymentDO);
                            refundMapper.insert(refundDO);
                            paymentDO.setRefundTime(refundDO.getRefundTime());
                            paymentDO.setRefundAmount(refundDO.getRefundAmount());
                            //申请退款
                            WxPayRefundV3Request wxPayRefundV3Request = WxPayConverter.toWxPayRefundV3(refundDO);
                            wxPayRefundV3Request.setNotifyUrl(wxPayProperties.getRefundNotifyUrl());
                            WxPayRefundV3Result wxPayRefundV3Result = wxManager.getWxPayRefundService().refundV3(wxPayRefundV3Request);
                            log.info("申请退款结果解析:{}", wxPayRefundV3Result);
                        }
                        //更新支付单
                        paymentDO.setStatus(PayStatusEnum.REFUNDING.getCode());
                        paymentMapper.updatePayStatus(paymentDO, status);
                        response.setStatus(HttpStatus.SC_OK);
                        return OrderPayNotify.success();
                    }
                    response.setStatus(HttpStatus.SC_OK);
                    return OrderPayNotify.success();
                }
            }
            //未支付订单
            //更新订单和支付单状态
            if (orderManager.payNotify(result, orderDO, paymentDO, wxPayStatus)) {
                //生成码包
                producerHelper.sendOrderMsg(orderDO);
            }
            response.setStatus(HttpStatus.SC_OK);
            return OrderPayNotify.success();
        } catch (Exception e) {
            log.error("微信回调结果异常,第三方流水号{},异常支付单号{}", result.getTransactionId(), wxPayOrderNotifyV3Result.getResult().getOutTradeNo(), e);
            response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
            return OrderPayNotify.fail();
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 退款通知
     */
    @Transactional(rollbackFor = Exception.class)
    public OrderPayNotify payRefundNotify(JSONObject jsonObject, HttpServletRequest request, HttpServletResponse response) {
        //获取请求头
        SignatureHeader header = PayUtil.getWXRequestHeader(request);
        //解析回调
        WxPayRefundNotifyV3Result wxPayRefundNotifyV3Result = getWxPayRefundNotifyV3Result(jsonObject, header);
        WxPayRefundNotifyV3Result.DecryptNotifyResult result = wxPayRefundNotifyV3Result.getResult();
        log.info("微信退款结果通知:{}", result);
        WxPayRefundStateEnum wxPayRefundStateEnum = WxPayRefundStateEnum.findByCode(result.getRefundStatus());
        //退款关闭或者退款异常,直接返回,继续通知
        if (Objects.equals(wxPayRefundStateEnum, WxPayRefundStateEnum.CLOSED) || Objects.equals(wxPayRefundStateEnum, WxPayRefundStateEnum.ABNORMAL)
        ) {
            log.error("微信退款异常回调,退款单号,refundNo:{}", result.getOutRefundNo());
            response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
            return OrderPayNotify.fail();
        }
        //加锁 RLock lock 
        if (lock == null) {
            throw BizException.create(BizErrorCodeEnum.REFUND_NOTIFY_FAIL);
        }
        try {
            //查询退款单
            RefundDO refundDo 
            log.info("微信退款单解析:{},退款单号", refundDo, refundDo.getRefundNo());
            //查询支付单
            PaymentDO payment 
            log.info("支付单结果解析:{},支付单号{}", payment, payment.getPayNo());
            //查询订单
            OrderDO order 
            log.info("订单结果解析:{},订单号{}", order, order.getOrderNo());

            Integer paymentStatus = payment.getStatus();
            Integer orderStatus = order.getStatus();
            Integer refundStatus = refundDo.getStatus();

            //退款单状态退款中 并且退款通知成功   更新支付单和退款单
            if (Objects.equals(refundStatus, RefundStateEnum.REFUNDING.getCode())) {
                //更新退款单
                if (!Objects.equals(refundStatus, RefundStateEnum.SUCCESS.getCode())) {
                    refundDo = RefundConverter.toRefund(wxPayRefundNotifyV3Result, refundDo);
                    int r = refundMapper.updateStatus(refundDo, refundStatus);
                    if (r == 0) {
                        throw BizException.create(BizErrorCodeEnum.REFUND_UPDATE_FAIL, "退款单更新失败");
                    }
                }
                //更新支付单
                payment.setStatus(PayStatusEnum.REFUND.getCode());
                payment.setRefundTime(DateUtil.parse(result.getSuccessTime()));
                BigDecimal refundAmount = LocalMoneyUtils.fen2yuan(Long.valueOf(result.getAmount().getRefund()));
                payment.setPayAmount(refundAmount);
                int p = paymentMapper.updatePayStatus(payment, paymentStatus);
                if (p == 0) {
                    throw BizException.create(BizErrorCodeEnum.PAYMENT_UPDATE_FAIL, "支付单状态更新失败");
                }
                response.setStatus(HttpStatus.SC_OK);
                return OrderPayNotify.success();
            }
            response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
            return OrderPayNotify.fail();
        } catch (Exception e) {
            response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
            return OrderPayNotify.fail();
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 订单列表
     */
    public PageData<OrderListRsp> queryOrderList(OrderListReq req, MerchantContext loginUser) {
        Long count = orderMapper.orderTotal(req, loginUser.getMerchantId());
        if (count <= 0) {
            return PageData.create(Lists.newArrayList(), count, req.getPage(), req.getSize());
        }
        List<OrderDO> orderList = orderMapper.queryOrderList(req, loginUser.getMerchantId());
        List<OrderListRsp> OrderRsp = OrderConverter.toOrderListReq(orderList);
        return PageData.create(OrderRsp, count, req.getPage(), req.getSize());
    }

    /**
     * 关闭订单
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean closeOrder(String orderNo) throws WxPayException {
        //查询订单是否已支付
        OrderDO orderDO = orderMapper.findByOrderNo(orderNo);
        if (!OrderStatusEnum.UNPAID.getCode().equals(orderDO.getStatus())) {
            return false;
        }
        Date expireTime = orderDO.getExpireTime();
        Date date = new Date();
        if (expireTime.compareTo(date) > 0) {
            //时间未到
            return false;
        }

        log.info("订单超期关闭,单号为{}", orderDO.getOrderNo());
        orderDO.setCloseTime(date);
        orderDO.setUpdateTime(date);
        orderDO.setStatus(OrderStatusEnum.CLOSED.getCode());
        int i = orderMapper.updateStatusByOrderNo(orderDO, OrderStatusEnum.UNPAID.getCode());
        if (i == 0) {
            //订单状态以改变
            return false;
        }
        //关闭支付单
        PaymentDO paymentDO = paymentMapper.findByPayNo(orderDO.getPayNo());
        if (Objects.isNull(paymentDO)) {
            return false;
        }
        //更新支付单
        Integer payStatus = paymentDO.getStatus();
        paymentDO.setStatus(PayStatusEnum.CANCEL.getCode());
        paymentDO.setUpdateTime(date);
        paymentMapper.updatePayStatus(paymentDO, payStatus);
        //第三方渠道关单
        WxPayOrderCloseV3Request request = new WxPayOrderCloseV3Request();
        request.setOutTradeNo(paymentDO.getPayNo());
        wxManager.getWxPayService().closeOrderV3(request);
        return true;
    }

    /**
     * 解析微信支付回调通知
     */
    private WxPayOrderNotifyV3Result getWxPayOrderNotifyV3Result(JSONObject jsonObject, SignatureHeader header) {

        WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = null;
        try {
            wxPayOrderNotifyV3Result = wxManager.getWxPayService().parseOrderNotifyV3Result(jsonObject.toJSONString(), header);
            log.info("微信支付结果解析:{}", wxPayOrderNotifyV3Result.getResult());
        } catch (Exception e) {
            log.info("微信支付结果异常解析:{},异常信息{}", wxPayOrderNotifyV3Result.getResult(), e);
        }
        return wxPayOrderNotifyV3Result;
    }

    /**
     * 解析微信退款回调通知
     */
    private WxPayRefundNotifyV3Result getWxPayRefundNotifyV3Result(JSONObject jsonObject, SignatureHeader header) {

        WxPayRefundNotifyV3Result wxPayRefundNotifyV3Result = null;
        try {
            wxPayRefundNotifyV3Result = wxManager.getWxPayRefundService().parseRefundNotifyV3Result(jsonObject.toJSONString(), header);
            log.info("微信退款结果解析:{}", wxPayRefundNotifyV3Result.getResult());
        } catch (WxPayException e) {
            log.info("微信退款结果异常解析:{},异常信息{}", wxPayRefundNotifyV3Result.getResult(), e);
        }
        return wxPayRefundNotifyV3Result;
    }
}

3.manager

@Slf4j
@Component
public class WxManager {

    @Autowired
    WxPayProperties wxPayProperties;
    @Resource
    private RedissonClient redissonClient;
    @Autowired
    private WxMpProperties wxMpProperties;

    public WxMpService getWxMpService() {
        WxMpRedissonConfigImpl config = new WxMpRedissonConfigImpl(redissonClient, CommonConstants.NAMESPACE);
        config.setAppId(wxMpProperties.getAppId());
        config.setSecret(wxMpProperties.getSecret());

        WxMpService service = new WxMpServiceImpl();
        service.setWxMpConfigStorage(config);
        return service;
    }

    public WxPayService getWxPayService() {
        WxPayConfig payConfig = new WxPayConfig();
        payConfig.setAppId(wxPayProperties.getAppid());
        payConfig.setMchId(wxPayProperties.getMchId());
        payConfig.setPrivateKeyPath(wxPayProperties.getPrivateKeyPath());
        payConfig.setNotifyUrl(wxPayProperties.getNotifyUrl());
        payConfig.setApiV3Key(wxPayProperties.getApiV3key());
        payConfig.setPrivateCertPath(wxPayProperties.getPrivateCertPath());
        WxPayService wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(payConfig);
        return wxPayService;
    }

    public WxPayService getWxPayRefundService() {
        WxPayConfig payConfig = new WxPayConfig();
        payConfig.setAppId(wxPayProperties.getAppid());
        payConfig.setMchId(wxPayProperties.getMchId());
        payConfig.setPrivateKeyPath(wxPayProperties.getPrivateKeyPath());
        payConfig.setNotifyUrl(wxPayProperties.getRefundNotifyUrl());
        payConfig.setApiV3Key(wxPayProperties.getApiV3key());
        payConfig.setPrivateCertPath(wxPayProperties.getPrivateCertPath());
        WxPayService wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(payConfig);
        return wxPayService;
    }


}
@Slf4j
@Component
public class OrderManager {
    @Autowired
    OrderMapper orderMapper;
    @Autowired
    PaymentMapper paymentMapper;
    @Autowired
    WxManager wxManager;

    /**
     * 查询第三方支付,更新订单
     *
     * @param orderDO
     * @param paymentDO
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Boolean queryOrderByWx(WxPayOrderQueryV3Result wxPayOrderQueryV3Result, OrderDO orderDO, PaymentDO paymentDO, PayStatusEnum payStatusEnum) {
        //未支付 支付中 等待扣款 直接返回
        WxPayTradeStateEnum wxPayTradeStateEnum = WxPayTradeStateEnum.findByCode(wxPayOrderQueryV3Result.getTradeState());
        if (Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.NOT_PAY)
                || Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.ACCEPT)
                || Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.USER_PAYING)) {
            return false;
        }

        if (!wxPayOrderQueryV3Result.getPayer().getOpenid().equals(paymentDO.getOpenid())) {
            //记录日志,更新openid
            log.error("查询微信支付,openid与本地不一致,paymentDO:{},payer:{}", paymentDO, wxPayOrderQueryV3Result.getPayer());
        }
        //更新支付单状态
        paymentDO = PaymentConverter.toPaymentByWxPayOrderQueryV3Result(wxPayOrderQueryV3Result, paymentDO);
        int p = paymentMapper.updatePayStatus(paymentDO, payStatusEnum.getCode());
        if (p == 0) {
            throw BizException.create(BizErrorCodeEnum.PAYMENT_UPDATE_FAIL, "支付单状态更新失败");
        }
        //已关闭 已撤销 支付失败 不更新订单状态 可重新支付
        if (Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.CLOSED)
                || Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.REVOKED)
                || Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.PAY_ERROR)) {
            return false;
        }
        //更新订单状态  幂等
        int orgOrderStatus = orderDO.getStatus();
        orderDO = OrderConverter.toOrderByPayment(paymentDO, orderDO, payStatusEnum);
        int o = orderMapper.updateStatusByOrgStatus(orderDO, orgOrderStatus);
        if (o == 0) {
            throw BizException.create(BizErrorCodeEnum.ORDER_UPDATE_FAIL, "订单状态更新失败");
        }
        return true;
    }

    /**
     * 支付回调 更新订单
     */
    public Boolean payNotify(WxPayOrderNotifyV3Result.DecryptNotifyResult result, OrderDO order, PaymentDO payment, WxPayTradeStateEnum wxPayTradeStateEnum) {
        if (!result.getPayer().getOpenid().equals(payment.getOpenid())) {
            //记录日志
            log.error("查询微信支付,openid与本地不一致,paymentDO:{},payer:{}", payment, result.getPayer());
        }
        PayStatusEnum payStatusEnum = LocalEnumUtils.findByCodeWithoutDefault(PayStatusEnum.class, payment.getStatus());
        Integer orderStatus = order.getStatus();
        //非 未支付 代扣款 支付中 更新支付单信息
        if (Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.NOT_PAY)
                || Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.ACCEPT)
                || Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.USER_PAYING)) {
            return false;
        }
        //更新支付单
        payment = PaymentConverter.fromWxPayOrderNotifyV3Result(result, payment);
        int p = paymentMapper.updatePayStatus(payment, payStatusEnum.getCode());
        if (p == 0) {
            throw BizException.create(BizErrorCodeEnum.PAYMENT_UPDATE_FAIL, "支付单状态更新失败");
        }
        //已关闭 已撤销 支付失败 不更新订单状态 可重新支付
        if (Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.CLOSED)
                || Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.REVOKED)
                || Objects.equals(wxPayTradeStateEnum, WxPayTradeStateEnum.PAY_ERROR)) {
            return false;
        }
        PayStatusEnum statusEnum = LocalEnumUtils.findByCodeWithoutDefault(PayStatusEnum.class, payment.getStatus());
        //更新订单状态  幂等
        order = OrderConverter.toOrderByPayment(payment, order, statusEnum);
        int o = orderMapper.updateStatusByOrgStatus(order, orderStatus);
        if (o == 0) {
            throw BizException.create(BizErrorCodeEnum.ORDER_UPDATE_FAIL, "订单状态更新失败");
        }
        return true;
    }
}

4.Properties

@Data
@Component
@Configuration
@ConfigurationProperties(prefix = "wechat-pay")
public class WxPayProperties {
    /**
     * 商户号
     */
    private String mchId;
    /**
     * 证书解密的密钥
     */
    private String apiV3key;
    /**
     * 商户私钥文件
     */
    private String privateKeyPath;

    /**
     * apiclient_cert.pem证书文件
     */
    private String privateCertPath;

    /**
     * 商户证书序列号
     */
    private String serialNo;
    /**
     * 赋选供应链 服务号appid
     */
    private String appid;
    /**
     * 支付回调通知地址
     */
    private String notifyUrl;

    /**
     * 退款回调通知地址
     */
    private String refundNotifyUrl;
}

总结

以上就是核心业务代码,涉及到具体业务mapper没有放上来,但是核心的处理步骤已经体现出来了。主要是对订单状态需要仔细判断,不同操作会引起状态的变更。业务中,当多个人扫同一个码,后续扫码成功会调用退款接口。

  • 2
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值