java对接微信支付、退款

继上一篇实现java对接支付宝,本篇记录一下java实现微信、小程序的支付与退款实现过程,话不多说,直接上代码

pom依赖:

        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
            <version>4.0.3</version>
        </dependency>
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-java</artifactId>
            <version>0.2.11</version>
        </dependency>

支付控制层:

/**
     *  支付接口
     *
     */
    @PostMapping("/v1/order/pay")
    public Result appPay(@RequestBody @Validated AlipayReq param) {
        try {
            return alipayService.createAppPay(param);
        } catch (Exception e) {
            log.error("支付接口请求失败:{}",e);
            return Result.error(e.getMessage());
        }
    }

支付业务层 ,微信支付核心代码在createAppPay方法中

@Service
@Slf4j
@RequiredArgsConstructor
public class PayServiceImpl implements PayService {
    private final OrderDataClient orderDataClient;
    private final AliPayClientConfig aliPayClientConfig;
    private final AlipayClient alipayClient;
    private final PayBackRecordService payBackRecordService;
    private final PayApplyRecordService payApplyRecordService;
    private final PayRefundRecordService payRefundRecordService;
    private final RedissonClient redissonClient;
    private final WxPayV3Bean wxPayV3Bean;
    private final UserInfoClient userInfoClient;
    private final RSAAutoCertificateConfig config;
    private final PayCommonService payCommonService;


    private void checkPay(String orderNo) {
        //校验该笔订单是否支付成功过
        PayBackRecord build = PayBackRecord.builder().orderNo(orderNo).deleted(Boolean.FALSE).build();
        long count = payBackRecordService.count(new QueryWrapper<>(build));
        if (count > long0) {
            throw new BizException("该订单已支付,请勿重复支付");
        }
    }

    @Override
    public Result createAppPay(AlipayReq param) {
        log.info("支付请求参数入参:{}", JSONUtil.toJsonStr(param));
        //同一时刻用户对同一个订单只能支付一次
        String lockKey = RedisKeyContant.LOCK_APP_PAY_ORDER + param.getOrderNo();
        RLock rLock = redissonClient.getLock(lockKey);
        boolean lockResult = rLock.tryLock();
        try {
            if (lockResult) {
                checkPay(param.getOrderNo());
                PayOrderInfo order = this.getOrderInfo(param.getOrderNo());
                if(order.getPrice().compareTo(BigDecimal.ZERO)<=0){
                    throw new BusinessException("支付金额必须大于0");
                }
                if (Objects.equals(PayWayEnum.PAY_ALIPAY.getCode(), param.getPayWay())) {
                    //支付宝支付
                    Result result = this.doAlipayApp(order.getOrderNo(), PayOrderTypeEnum.getByType(order.getPayOrderType()).getMsg(), order.getPrice());
                    //记录支付申请表
                    this.toSavePayApplyRecord(order, result, PayWayEnum.PAY_ALIPAY.getPayWay());
                    log.info("订单编号:{},支付宝响应结果:{}", param.getOrderNo(), JSONUtil.toJsonStr(result));
                    return result;
                }
                if (Objects.equals(PayWayEnum.PAY_WECHAT_APPLET.getCode(), param.getPayWay())) {
                    //微信小程序支付
                    Result<UserInfoVO> userResult = userInfoClient.getUserById(AuthUserUtil.getCurrentUserId());
                    if (!userResult.isSuccess() || Objects.isNull(userResult.getObject())) {
                        throw new BizException("查询用户信息接口异常");
                    }
                    UserInfoVO userInfoVo = userResult.getObject();
                    Result<PrepayWithRequestPaymentResponse> wxPayRespVOResult = this.doWechatAppletPay(order.getOrderNo(), PayOrderTypeEnum.getByType(order.getPayOrderType()).getMsg(), order.getPrice(), userInfoVo.getMiniOpenId());
                    //记录支付申请表
                    this.toSavePayApplyRecord(order, wxPayRespVOResult,PayWayEnum.PAY_WECHAT_APPLET.getPayWay());
                    return wxPayRespVOResult;
                }
                if (Objects.equals(PayWayEnum.PAY_WECHAT.getCode(), param.getPayWay())) {
                    //微信支付
                    Result<WxPayRespVO> payResult = this.doWechatAppPay(order.getOrderNo(), PayOrderTypeEnum.getByType(order.getPayOrderType()).getMsg(), order.getPrice());
                    //记录支付申请表
                    this.toSavePayApplyRecord(order, payResult, PayWayEnum.PAY_WECHAT.getPayWay());
                    return payResult;
                }
            }
            return Result.fail("支付中,请勿重复点击");
        } catch (Exception e) {
            log.error("支付请求失败:{}",e);
            return Result.fail(e.getMessage());
        } finally {
            if (lockResult) {
                rLock.unlock();
            }
        }
    }

    /**
     * 微信支付
     *
     * @Param: [java.lang.String, java.lang.String, java.math.BigDecimal]
     * @return: cn.aimex.cloud.framework.core.domain.Result<cn.gpets.cloud.payment.server.domain.vo.WxPayRespVO>
     * @Author: FJT
     * @Date: 2023/11/9
     */
    private Result<WxPayRespVO> doWechatAppPay(String orderNo, String msg, BigDecimal price) {
        try {
            // 构建service
            AppService service = new AppService.Builder().config(config).build();
            com.wechat.pay.java.service.payments.app.model.PrepayRequest request = new com.wechat.pay.java.service.payments.app.model.PrepayRequest();
            com.wechat.pay.java.service.payments.app.model.Amount amount = new com.wechat.pay.java.service.payments.app.model.Amount();
            amount.setTotal(NumberUtil.mul(price, 100).intValue());
            request.setAmount(amount);
            request.setAppid(wxPayV3Bean.getAppId());
            request.setMchid(wxPayV3Bean.getMchId());
            request.setDescription(msg);
            request.setNotifyUrl(wxPayV3Bean.getNotifyUrl());
            request.setOutTradeNo(orderNo);
            // 调用下单方法,得到应答
            // 调用微信sdk接口,生成预支付交易单
            com.wechat.pay.java.service.payments.app.model.PrepayResponse response = service.prepay(request);
            WxPayRespVO vo = new WxPayRespVO();
            Signer signer = config.createSigner();
            long timestamp = Instant.now().getEpochSecond();
            vo.setTimeStamp(timestamp);
            String nonceStr = NonceUtil.createNonce(32);
            vo.setNonceStr(nonceStr);
            String message =
                    request.getAppid() + "\n" + timestamp + "\n" + nonceStr + "\n" + response.getPrepayId() + "\n";
            String sign = signer.sign(message).getSign();
            vo.setPaySign(sign);
            vo.setPrepayId(response.getPrepayId());
            vo.setAppId(wxPayV3Bean.getAppId());
            vo.setPartnerId(wxPayV3Bean.getMchId());
            log.info("微信支付请求返回:{}",JSONUtil.toJsonStr(vo));
            return Result.success(vo);
        } catch (ServiceException e) {
            log.error("微信支付请求异常:{}",e);
            return Result.fail(JSONUtil.toJsonStr(e.getResponseBody()));
        } catch (Exception e) {
            log.error("微信支付请求异常:{}",e);
            return Result.fail(JSONUtil.toJsonStr(e.getMessage()));
        }
    }

    /**
     * 微信小程序支付
     *
     * @param orderNo
     * @param msg
     * @param price
     * @Param: []
     * @return: void
     * @Author: FJT
     * @Date: 2023/11/8
     */
    private Result<PrepayWithRequestPaymentResponse> doWechatAppletPay(String orderNo, String msg, BigDecimal price, String openId) {
        try {
            // 构建service
            JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
            // request.setXxx(val)设置所需参数,具体参数可见Request定义
            PrepayRequest request = new PrepayRequest();
            Amount amount = new Amount();
            amount.setTotal(NumberUtil.mul(price, 100).intValue());
            request.setAmount(amount);
            request.setAppid(wxPayV3Bean.getAppletAppId());
            request.setMchid(wxPayV3Bean.getMchId());
            request.setDescription(msg);
            request.setNotifyUrl(wxPayV3Bean.getNotifyUrl());
            request.setOutTradeNo(orderNo);
            Payer payer = new Payer();
            payer.setOpenid(openId);
            request.setPayer(payer);
            // 调用微信sdk接口,生成预支付交易单
            PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
//            Signer signer = config.createSigner();
//            long timestamp = Instant.now().getEpochSecond();
//            vo.setTimeStamp(timestamp);
//            String nonceStr = NonceUtil.createNonce(32);
//            vo.setNonceStr(nonceStr);
//            String message =
//                    request.getAppid() + "\n" + timestamp + "\n" + nonceStr + "\n" + response.getPrepayId() + "\n";
//            String sign = signer.sign(message).getSign();
//            vo.setPaySign(sign);
//            vo.setPrepayId(response.getPrepayId());
//            vo.setAppId(wxPayV3Bean.getAppletAppId());
//            vo.setPartnerId(wxPayV3Bean.getMchId());
            log.info("微信支付请求返回:{}",JSONUtil.toJsonStr(response));
            return Result.success(response);
        } catch (ServiceException e) {
            log.info("微信支付请求返回异常:{}",e);
            return Result.fail(JSONUtil.toJsonStr(e.getResponseBody()));
        } catch (Exception e) {
            log.info("微信支付请求返回异常:{}",e);
            return Result.fail(JSONUtil.toJsonStr(e.getMessage()));
        }
    }

    private void toSavePayApplyRecord(PayOrderInfo order, Result result, String payWay) {
        PayApplyRecord payApplyRecord = new PayApplyRecord();
        payApplyRecord.setApplyResult(JSONUtil.toJsonStr(result));
        payApplyRecord.setBusinessNo(order.getOrderNo());
        payApplyRecord.setPayChannel(order.getPayOrderType());
        payApplyRecord.setPayPrice(order.getPrice());
        payApplyRecord.setPayMode(payWay);
        payApplyRecord.setCreateDateTime(LocalDateTime.now());
        payApplyRecord.setCreateUser(AuthUserUtil.getCurrentUserId());
        payApplyRecordService.save(payApplyRecord);
    }

    @Override
    public PayOrderInfo getOrderInfo(String orderNo) {
        Result<PayOrderInfo> orderResult = orderDataClient.getOrderInfoByOrderNo(orderNo);
        if (!orderResult.isSuccess()) {
            throw new BizException("查询订单信息异常");
        }
        PayOrderInfo order = orderResult.getObject();
        if (Objects.isNull(order)) {
            throw new BizException("订单信息不存在");
        }
        return order;
    }

    @Override
    public Result getOrderPayState(AlipayReq param) {
        if (Objects.equals(PayWayEnum.PAY_ALIPAY.getCode(), param.getPayWay())) {
            //查询支付宝支付状态
            AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
            AlipayTradeQueryModel model = new AlipayTradeQueryModel();
            model.setOutTradeNo(param.getOrderNo());
            request.setBizModel(model);
            try {
                AlipayTradeQueryResponse response = alipayClient.execute(request);
                log.info("订单号:{},支付订单状态结果:{}", JSONUtil.toJsonStr(param.getOrderNo()), JSONUtil.toJsonStr(response));
                if (response.getTradeStatus().equals(AliPayUtil.TRADE_SUCCESS)) {
                    return Result.success(JSONUtil.parseObj(response.getBody()));
                }
                return Result.fail("订单支付失败");
            } catch (AlipayApiException e) {
                log.error("获取支付订单支付状态失败:{}", e);
                return Result.fail(e.getErrMsg());
            }
        }

        if (Objects.equals(PayWayEnum.PAY_WECHAT.getCode(), param.getPayWay())
                || Objects.equals(PayWayEnum.PAY_WECHAT_APPLET.getCode(), param.getPayWay())) {
            AppService service = new AppService.Builder().config(config).build();
            QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
            request.setOutTradeNo(param.getOrderNo());
            request.setMchid(wxPayV3Bean.getMchId());
            Transaction transaction = null;
            try {
                transaction = service.queryOrderByOutTradeNo(request);
                log.info("微信订单支付状态响应结果:{}", JSONUtil.toJsonStr(transaction));
                if (transaction.getTradeState().equals(Transaction.TradeStateEnum.SUCCESS)) {
                    return Result.success(JSONUtil.parse(transaction));
                }
            } catch (ServiceException e) {
                log.error("获取支付订单支付状态异常:{}", e);
                return Result.fail(e.getErrorMessage());
            }

            return Result.fail("订单支付失败");
        }

        return Result.fail();
    }

    /**
     * 订单退款
     *
     * @Param: [cn.gpets.cloud.payment.clent.domain.req.RefundOrderReq]
     * @return: cn.aimex.cloud.framework.core.domain.Result
     * @Author: FJT
     * @Date: 2023/11/7
     */
    @Override
    public Result refundOrder(RefundOrderReq req) {
        log.info("订单退款入参:{}", JSONUtil.toJsonStr(req));
        String refundNo = req.getRefundNo();
        //查询是否成功退款过
        PayRefundRecord payRefundRecordBuild = PayRefundRecord.builder().refundNo(refundNo).deleted(Boolean.FALSE).build();
        PayRefundRecord payRefundRecord = payRefundRecordService.getOne(new QueryWrapper<>(payRefundRecordBuild));
        if (Objects.isNull(payRefundRecord)) {
            log.error("用户没有退款记录,无法退款,退款单号:{}",req.getRefundNo());
            throw new BizException("用户没有退款记录,无法退款");
        }
        if (payRefundRecord.getRefundSuccess()) {
            log.error("该商品已退款,请勿重复退款,退款单号:{}",req.getRefundNo());
            throw new BizException("该商品已退款,请勿重复退款");
        }
        PayOrderInfo orderInfo = this.getOrderInfo(payRefundRecord.getRefundBusinessNo());
        Result<OrderProductVo> result = orderDataClient.getOrderProductInfo(payRefundRecord.getOrderProductId(), orderInfo.getPayOrderType());
        if (!result.isSuccess()) {
            throw new BizException("查询商品行信息接口异常");
        }
        OrderProductVo productVo = result.getObject();
        //查询支付申请记录,查看该笔是否成功支付过
        PayBackRecord payBackRecord = payBackRecordService.getOne(new QueryWrapper<>(PayBackRecord.builder().orderNo(payRefundRecord.getRefundBusinessNo()).deleted(Boolean.FALSE).build()));
        if (Objects.isNull(payBackRecord)) {
            log.error("订单未支付过,无法退款,退款单号:{}",req.getRefundNo());
            throw new BizException("订单未支付过,无法退款");
        }
        //同一时刻对同一个商品只能退一次
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(RedisKeyContant.LOCK_REFUND_ORDER).append(refundNo);
        String lockKey = stringBuffer.toString();
        RLock rLock = redissonClient.getLock(lockKey);
        boolean lockResult = rLock.tryLock();
        try {
            if (lockResult) {
                payRefundRecordBuild.setRefundNo(refundNo);
                BigDecimal price = productVo.getPrice();
                if (Objects.nonNull(req.getRefundPrice()) && price.compareTo(req.getRefundPrice()) >= 0) {
                    //如果传过来的退款金额不为空,并且小于原商品行的支付金额,则使用传递的退款金额
                    price = req.getRefundPrice();
                }
                if (Objects.equals(PayWayEnum.PAY_ALIPAY.getPayWay(), payBackRecord.getPayWay())) {
                    //原使用支付宝进行支付的,使用支付宝进行退款
                    AlipayTradeRefundResponse refundResult = this.doAlipayRefund(payRefundRecord.getRefundBusinessNo(), price, payRefundRecord.getRefundReason(),refundNo);
                    log.info("退款单号:{},支付宝退款响应结果:{}", refundNo, JSONUtil.toJsonStr(refundResult));
                    if (refundResult.isSuccess()&&refundResult.getFundChange().equals(AliPayUtil.CHANGE_STATE_YES)) {
                        //更新退款申请表的退款状态
                        payRefundRecord.setRefundSuccess(Boolean.TRUE);
                        payRefundRecord.setUpdateDateTime(LocalDateTime.now());
                        payRefundRecord.setTradeNo(refundResult.getTradeNo());
                        payRefundRecord.setRefundDetail(JSONUtil.toJsonStr(refundResult));
                        payRefundRecordService.updateById(payRefundRecord);
                        //更新订单退款状态
                        UpdateOrderRefundReq updateOrderRefundReq = new UpdateOrderRefundReq();
                        updateOrderRefundReq.setOrderNo(orderInfo.getOrderNo());
                        updateOrderRefundReq.setOrderProductId(payRefundRecord.getOrderProductId());
                        updateOrderRefundReq.setOrderProductState(payRefundRecord.getOrderProductState());
                        updateOrderRefundReq.setPayOrderType(orderInfo.getPayOrderType());
                        if(Objects.equals(PayOrderTypeEnum.PET_ALL_PAY.getType(),orderInfo.getPayOrderType())){
                            updateOrderRefundReq.setRefundOrderState(PetOrderStateEnum.STATE_7.getTagId());
                        }
                        StringBuffer stringBuffer1 = new StringBuffer();
                        updateOrderRefundReq.setRefundMsg(stringBuffer1.append(req.getRefundMsg()).append(":").append(OrderRemarkEnum.BUYER_HAD_REFUND.getMsg()).toString());
                        orderDataClient.updateOrderRefundState(updateOrderRefundReq);
                        //发送钱包通知消息
                        payCommonService.doSendRefundMag(MessageContentEnum.REFUND_TO_ACCOUNT,payRefundRecord.getRefundNo(),price,orderInfo.getUserId(),payRefundRecord.getRefundReason());
                        //给python端发送退款到账消息
                        payCommonService.doSendRefundResult(refundNo,Boolean.TRUE,null);
                        return Result.success();
                    }
                    return Result.fail(refundResult.getSubMsg());
                }
                if (Objects.equals(PayWayEnum.PAY_WECHAT.getPayWay(), payBackRecord.getPayWay())
                        || Objects.equals(PayWayEnum.PAY_WECHAT_APPLET.getPayWay(), payBackRecord.getPayWay())) {
                    //使用微信退款
                    Refund refund = this.doWechatRefund(price, refundNo, payBackRecord.getPayPrice(),payBackRecord.getOrderNo(),req.getRefundMsg());
                    log.info("退款单号:{},微信退款响应结果:{}", refundNo, JSONUtil.toJsonStr(refund));
                    if (refund.getStatus().equals(Status.SUCCESS)||refund.getStatus().equals(Status.PROCESSING)) {
                        //更新订单退款状态
                        UpdateOrderRefundReq updateOrderRefundReq = new UpdateOrderRefundReq();
                        updateOrderRefundReq.setOrderNo(orderInfo.getOrderNo());
                        updateOrderRefundReq.setOrderProductId(payRefundRecord.getOrderProductId());
                        updateOrderRefundReq.setOrderProductState(payRefundRecord.getOrderProductState());
                        updateOrderRefundReq.setPayOrderType(orderInfo.getPayOrderType());
                        if(Objects.equals(PayOrderTypeEnum.PET_ALL_PAY.getType(),orderInfo.getPayOrderType())){
                            updateOrderRefundReq.setRefundOrderState(PetOrderStateEnum.STATE_6.getTagId());
                        }
                        updateOrderRefundReq.setRefundMsg(OrderRemarkEnum.BUYER_APPLY_REFUND.getMsg());
                        orderDataClient.updateOrderRefundState(updateOrderRefundReq);
                        //发送钱包通知消息
                        payCommonService.doSendRefundMag(MessageContentEnum.RETURN_HAD_ACCEPTANCE,payRefundRecord.getRefundNo(),price,orderInfo.getUserId(),payRefundRecord.getRefundReason());
                        return Result.success();
                    }
                }
            } else {
                return Result.fail("请勿重复退款");
            }
        } catch (BizException e) {
            log.info("退款失败:{}", e.getMessage());
            return Result.fail(e.getMessage());
        } finally {
            if (lockResult) {
                rLock.unlock();
            }
        }
        return Result.fail();
    }

    /**
     * 微信或微信小程序退款
     *
     * @Param: [java.lang.String, java.math.BigDecimal, java.lang.String]
     * @return: void
     * @Author: FJT
     * @Date: 2023/11/9
     */
    private Refund doWechatRefund(BigDecimal price, String refundNo, BigDecimal payPrice, String orderNo, String refundMsg) {
        Refund refund = null;
        try {
            // 构建退款service
            RefundService service = new RefundService.Builder().config(config).build();
            // request.setXxx(val)设置所需参数,具体参数可见Request定义
            //构建退款请求
            CreateRequest request = new CreateRequest();
            //构建订单金额信息
            AmountReq amountReq = new AmountReq();
            //退款金额
            amountReq.setRefund(NumberUtil.mul(price, 100).longValue());
            //原订单金额
            amountReq.setTotal(NumberUtil.mul(payPrice, 100).longValue());
            //货币类型(默认人民币)
            amountReq.setCurrency("CNY");
            request.setAmount(amountReq);
            request.setOutTradeNo(orderNo);
            request.setReason(refundMsg);
            //商户退款单号
            request.setOutRefundNo(refundNo);
            //退款通知回调地址
            request.setNotifyUrl(wxPayV3Bean.getRefundNotifyUrl());
            // 调用退款方法,得到应答
            // 调用微信sdk接口
            refund = service.create(request);
            //接收退款返回参数
            return refund;
        } catch (ServiceException e) {
            log.error("退款异常:{}", e);
            throw new BizException(e.getErrorMessage());
        } catch (Exception e) {
            log.error("退款异常:{}", e);
            throw new BizException(e.getMessage());
        }
    }

    /**
     * 支付宝退款
     *
     * @Param: [java.lang.String, java.math.BigDecimal, java.lang.String]
     * @return: void
     * @Author: FJT
     * @Date: 2023/11/7
     */
    private AlipayTradeRefundResponse doAlipayRefund(String refundBusinessNo, BigDecimal sumPrice, String refundReason, String refundNo) {

        AlipayTradeRefundResponse response = null;
        try {
            AlipayTradeRefundRequest alipayTradeCloseRequest = new AlipayTradeRefundRequest();
            //请求参数集合对象,除了公共参数之外,所有参数都可通过此对象传递
            AlipayTradeRefundModel alipayTradeRefundModel = new AlipayTradeRefundModel();
            //退款的订单号,传入生成支付订单时的订单号即可
            alipayTradeRefundModel.setOutTradeNo(refundBusinessNo);
            //退款金额
            alipayTradeRefundModel.setRefundAmount(sumPrice.toString());
            //退款的原因
            alipayTradeRefundModel.setRefundReason(refundReason);
            alipayTradeRefundModel.setOutRequestNo(refundNo);
            alipayTradeCloseRequest.setBizModel(alipayTradeRefundModel);

            //退款的执行流程与支付不太一样,支付时成功之后,需要通知回调接口,而退款则不需要,只需判断响应			参数 refundResponse.getFundChange().equals("Y") 判断是否发生了资金变化, equals("Y")表示资金发生了变化,退款成功
            response = alipayClient.execute(alipayTradeCloseRequest);
            return response;
        } catch (AlipayApiException e) {
            log.error("支付宝退款失败:{}", e);
            return response;
        }
    }

    /**
     * 支付宝app支付
     *
     * @Param: [java.lang.String, java.lang.String, java.math.BigDecimal]
     * @return: cn.aimex.cloud.framework.core.domain.Result
     * @Author: FJT
     * @Date: 2023/11/7
     */
    private Result doAlipayApp(String orderNumber, String msg, BigDecimal price) {
        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();

        //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();

        model.setSubject(msg);
        model.setOutTradeNo(orderNumber);
        model.setTimeoutExpress(AliPayUtil.TIME_OUT_EXPRESS);
        model.setTotalAmount(price.toString());
        request.setBizModel(model);
        //异步通知地址
        request.setNotifyUrl(aliPayClientConfig.getNotifyUrl());
        try {
            //这里和普通的接口调用不同,使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            if (!response.isSuccess()) {
                log.error("接口调用错误:{}", response.getBody());
                return Result.fail(response.getBody());
            }
            //就是orderString 可以直接给客户端请求,无需再做处理。
            log.info("支付请求返回:{}",response.getBody());
            return Result.success().setData(Collections.singletonList(response.getBody()));
        } catch (AlipayApiException e) {
            log.error("生成支付订单错误:", e);
            return Result.fail(e.getErrMsg());
        }
    }

}

微信回调类

/**
     *  微信支付回调
     *
     */
    @PostMapping("/v1/pay/callBack/result")
    public Result<Boolean> wechatPaycallBack(HttpServletRequest request) {
        try {
            return payCallBackService.wechatPayCallBack(request);
        } catch (Exception e) {
            return Result.error(e.getMessage());
        }
    }

    /**
     *  微信退款回调
     *
     */
    @PostMapping("/v1/refund/callBack/result")
    public Result<Boolean> wechatRefundCallBack(HttpServletRequest request) {
        try {
            return payCallBackService.wechatRefundCallBack(request);
        } catch (Exception e) {
            return Result.error(e.getMessage());
        }
    }

回调业务处理类

/**
     * 微信支付回调
     *
     * @Param: [java.util.Map<java.lang.String, java.lang.String>]
     * @return: cn.aimex.cloud.framework.core.domain.Result<java.lang.Boolean>
     * @Author: FJT
     * @Date: 2023/11/9
     */
    @Override
    public Result<Boolean> wechatPayCallBack(HttpServletRequest request) {
        try {
            //签名校验并封装微信requestParam
            RequestParam requestParam = this.getRequestParamAndCheckSign(request);
            NotificationParser parser = new NotificationParser(config);
            Transaction parse = parser.parse(requestParam, Transaction.class);
            String orderNo = parse.getOutTradeNo();
            log.info("微信支付回调结果,支付单号:{},:{}",JSONUtil.toJsonStr(parse), orderNo);
            PayOrderInfo orderInfo = payService.getOrderInfo(orderNo);
            this.checkWxPayParam(orderNo, parse.getMchid(), parse.getAmount(), parse.getTradeState(), orderInfo.getPrice());
            String payWay = PayWayEnum.PAY_WECHAT.getPayWay();
            if (parse.getTradeType().equals(Transaction.TradeTypeEnum.JSAPI)) {
                payWay = PayWayEnum.PAY_WECHAT_APPLET.getPayWay();
            }
            //保存支付回调记录
            String payApplyNo = parse.getTransactionId();
            this.toSavePayBackRecord(orderNo, payApplyNo, orderInfo.getPayOrderType(), payWay, orderInfo.getPrice(), JSONUtil.toJsonStr(parse));
            //更新支付状态
            UpdateOrderPayStateReq req = new UpdateOrderPayStateReq();
            req.setOrderNo(orderNo);
            req.setPayOrderType(orderInfo.getPayOrderType());
            req.setPayApplyNo(payApplyNo);
            req.setPayWay(payWay);
            orderDataClient.updateOrderPayState(req);
            //发送钱包通知消息
            payCommonService.doSendPayMag(MessageContentEnum.PAY_SUCCESS,payApplyNo,orderInfo.getPrice(),orderInfo.getUserId());
            return Result.success();
        } catch (Exception e) {
            log.error("处理支付回调失败:{}",e);
            throw new BizException("处理支付回调失败");
        }
    }

    /**
     * 微信退款回调
     *
     * @Param: [javax.servlet.http.HttpServletRequest]
     * @return: cn.aimex.cloud.framework.core.domain.Result<java.lang.Boolean>
     * @Author: FJT
     * @Date: 2023/11/9
     */
    @Override
    public Result<Boolean> wechatRefundCallBack(HttpServletRequest request) {
        RequestParam requestParam = this.getRequestParamAndCheckSign(request);
        NotificationParser parser = new NotificationParser(config);
        RefundNotification parse = parser.parse(requestParam, RefundNotification.class);
        PayOrderInfo orderInfo = payService.getOrderInfo(parse.getOutTradeNo());
        PayRefundRecord payRefundRecord = null;
        try {
            payRefundRecord = this.checkWxRefundParam(parse.getOutTradeNo(), parse.getAmount(), parse.getRefundStatus(), orderInfo.getPrice(), parse.getOutRefundNo());
        } catch (Exception e) {
            //给python端发送退款失败消息
            payCommonService.doSendRefundResult(payRefundRecord.getRefundNo(),Boolean.FALSE,e.getMessage());
            return Result.fail(e.getMessage());
        }
        Result<OrderProductVo> result = orderDataClient.getOrderProductInfo(payRefundRecord.getOrderProductId(), orderInfo.getPayOrderType());
        if (!result.isSuccess()) {
            throw new BizException("查询商品行信息接口异常");
        }
        OrderProductVo productVo = result.getObject();
        //更新退款申请为退款成功
        payRefundRecord.setRefundSuccess(Boolean.TRUE);
        payRefundRecord.setUpdateDateTime(LocalDateTime.now());
        payRefundRecord.setRefundDetail(JSONUtil.toJsonStr(parse));
        payRefundRecord.setTradeNo(parse.getRefundId());
        payRefundRecordService.updateById(payRefundRecord);
        //更新订单退款状态
        UpdateOrderRefundReq req=new UpdateOrderRefundReq();
        req.setOrderNo(orderInfo.getOrderNo());
        req.setOrderProductId(payRefundRecord.getOrderProductId());
        req.setOrderProductState(payRefundRecord.getOrderProductState());
        req.setPayOrderType(orderInfo.getPayOrderType());
        if(Objects.equals(PayOrderTypeEnum.PET_ALL_PAY.getType(),orderInfo.getPayOrderType())){
            req.setRefundOrderState(PetOrderStateEnum.STATE_7.getTagId());
        }
        StringBuffer stringBuffer=new StringBuffer();
        req.setRefundMsg(stringBuffer.append(payRefundRecord.getRefundReason()).append(":").append(OrderRemarkEnum.BUYER_HAD_REFUND.getMsg()).toString());
        orderDataClient.updateOrderRefundState(req);
        //发送钱包通知消息
        payCommonService.doSendRefundMag(MessageContentEnum.REFUND_TO_ACCOUNT,payRefundRecord.getRefundNo(),productVo.getPrice(),orderInfo.getUserId(),payRefundRecord.getRefundReason());
        //给python端发送退款到账消息
        payCommonService.doSendRefundResult(payRefundRecord.getRefundNo(),Boolean.TRUE,null);
        return Result.success();
    }

    /**
    * 检查微信退款回调参数
    * @Param: [java.lang.String, com.wechat.pay.java.service.refund.model.Amount, com.wechat.pay.java.service.refund.model.Status, java.math.BigDecimal]
    * @return: void
    * @Author: FJT
    * @Date: 2023/11/9
    */
    private PayRefundRecord checkWxRefundParam(String orderNo, Amount amount, Status refundStatus, BigDecimal price, String outRefundNo) {

        //校验交易状态
        if (!Status.SUCCESS.equals(refundStatus)) {
            log.error("orderNo:{},微信退款回调未成功", orderNo);
            throw new BizException("微信退款回调未成功");
        }

        Long totalAmount = amount.getTotal();
        if (!totalAmount.equals(NumberUtil.mul(price, 100).longValue())) {
            log.error("orderNo:{},金额校验失败", orderNo);
            throw new BizException("金额校验失败");
        }

        //校验退款单号
        PayRefundRecord refundRecord = payRefundRecordService.getOne(new QueryWrapper<>(PayRefundRecord.builder().refundNo(outRefundNo).deleted(Boolean.FALSE).build()));
        if(Objects.isNull(refundRecord)){
            throw new BizException("用户退款记录不存在");
        }

        //校验是否已经退款成功过了
        if(refundRecord.getRefundSuccess()){
            throw new BizException("用户已成功退款了");
        }
        return refundRecord;
    }

    /**
     * //签名校验并封装微信requestParam
     *
     * @Param: [javax.servlet.http.HttpServletRequest]
     * @return: com.wechat.pay.java.core.notification.RequestParam
     * @Author: FJT
     * @Date: 2023/11/9
     */
    private RequestParam getRequestParamAndCheckSign(HttpServletRequest request) {
        try {
            //读取请求体的信息
            ServletInputStream inputStream = request.getInputStream();
            StringBuffer stringBuffer = new StringBuffer();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String s;
            //读取回调请求体
            while ((s = bufferedReader.readLine()) != null) {
                stringBuffer.append(s);
            }
            String s1 = stringBuffer.toString();
            String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
            String nonce = request.getHeader(WECHAT_PAY_NONCE);
            String signType = request.getHeader("Wechatpay-Signature-Type");
            String serialNo = request.getHeader(WECHAT_PAY_SERIAL);
            String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
            RequestParam requestParam = new RequestParam.Builder()
                    .serialNumber(serialNo)
                    .nonce(nonce)
                    .signature(signature)
                    .timestamp(timestamp)
                    // 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048
                    .signType(signType)
                    .body(s1)
                    .build();
            return requestParam;
        } catch (Exception e) {
            log.error("微信回调数据异常:{}", e);
            throw new BizException(e.getMessage());
        }
    }

    private void checkWxPayParam(String orderNo, String mchid, TransactionAmount amount, Transaction.TradeStateEnum tradeState,BigDecimal orderPrice) {
        Integer totalAmount = amount.getTotal();
        if (!totalAmount.equals(NumberUtil.mul(orderPrice, 100).intValue())) {
            log.error("orderNo:{},金额校验失败", orderNo);
            throw new BizException("金额校验失败");
        }

        //3.验证 商户号 是否为该商家本身
        if (!wxPayV3Bean.getMchId().equals(mchid)) {
            log.error("orderNo:{},应用mchid校验失败", orderNo);
            throw new BizException("应用APPID校验失败");
        }
        //校验交易状态
        if (!Transaction.TradeStateEnum.SUCCESS.equals(tradeState)) {
            log.error("orderNo:{},微信支付回调未成功", orderNo);
            throw new BizException("微信支付回调未成功");
        }
    }

    /**
     * 保存支付回调记录
     *
     * @Param: [java.lang.String, java.lang.String, java.lang.Integer, java.lang.String]
     * @return: void
     * @Author: FJT
     * @Date: 2023/11/7
     */
    private void toSavePayBackRecord(String orderNo, String payApplyNo, Integer payOrderType, String payWay, BigDecimal price, String result) {
        PayBackRecord build = PayBackRecord.builder().payApplyNo(payApplyNo).orderNo(orderNo).orderPayType(payOrderType)
                .payPrice(price).payWay(payWay)
                .createDateTime(LocalDateTime.now())
                .payResult(result)
                .deleted(Boolean.FALSE).build();
        payBackRecordService.save(build);
    }

工具和对象类

@Component
@Configuration
@ConfigurationProperties(prefix = "gpets-cloud.wechat")
@Data
public class WxPayV3Bean {

    /**
     * 微信appId
     */
    private String appId;

    /**
     * 微信小程序appId
     */
    private String appletAppId;

    /**
     * 证书序列号
     */
    private String mchSerialNo;

    /**
     * 商户号
     */
    private String mchId;

    /**
     * 商户key
     */
    private String apiKey;


    /**
     * 订单支付回调地址
     */
    private String notifyUrl;

    /**
     * 订单微信退款回调地址
     */
    private String refundNotifyUrl;

    @Bean
    public RSAAutoCertificateConfig getWxConfig() {
        // 使用自动更新平台证书的RSA配置
        // 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
        return new RSAAutoCertificateConfig.Builder()
                .merchantId(mchId)
                .privateKey(WXPayUtil.PRIVATE_CERT_KEY)
                .merchantSerialNumber(mchSerialNo)
                .apiV3Key(apiKey)
                .build();
    }

ps:这里的微信priavateKey是微信证书的私钥key

WXPayUtil: 证书privateKey替换自己注册的商家证书key

public class WXPayUtil {

    //随机字符串设置
    private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    private static final Random RANDOM = new SecureRandom();

    public static final String TRANSFER_FAIL_STATE="FAIL";
    public static final String TRANSFER_SUCCESS_STATE="SUCCESS";
   //证书privatekey
    public static final String PRIVATE_CERT_KEY="-----BEGIN PRIVATE KEY-----\n" 
}

退款请求类:

@Data
public class RefundOrderReq {

    /**退款单号**/
    @NotNull(message = "退款单号不能为空")
    private String refundNo;

    /**加密字符串**/
    @NotNull(message = "加密字符串不能为空")
    private String sign;


    /**实际退还金额,如果该字段为空,则使用商品行金额,该金额不能大于商品行金额**/
    private BigDecimal refundPrice;

    /**退款原因**/
    private String refundMsg;


}

好了,里面的业务代码可以替换为自己需要处理的业务,如果对你有帮助可以点个赞

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值