微信支付 APP端 后端 第二弹 微信回调

目录

第二弹:支付回调

成果展示

1 订单设计思路

1.1设计思路

2.回调接口

2.1controller

2.2验证静态方法

2.3缓存参数 常量

2.4 service 校验金额

2.4.1金额静态方法校验

2.4.2总体校验

3. 定时任务 查询处理特殊订单

3.1 get请求 统一配置

3.2 查询订单service

3.3关闭订单

3.4 定时任务 处理订单

3.4.1 金额校验 (和微信回调处理金额校验相同)

3.4.2 定时任务处理 


第二弹:支付回调

微信支付通过支付通知接口将用户支付成功消息通知给商户

定时任务处理特殊订单

(这些要根据直接项目的情况按需来安排)

成果展示

(只展示部分 这个设计到项目内容 不好全部展示)

1 订单设计思路

1.1设计思路

2.回调接口

2.1controller

 @PostMapping("/wechatPayCallback")
    @ApiOperation("支付回调给微信确认")
    @ApiIgnore
    public String wechatCallback(HttpServletRequest request) {
        ToolWxConfig wxConfig = iToolWxConfigService.find();
        log.info("微信退款回调通知调用=============================");
        Gson gson = new Gson();
        Map<String,String> result = new HashMap(SystemConstant.NUM_16);
        result.put("code", "FAIL");
        result.put("message","失败");
            try {
                //微信回调信息校验
                // 构建request,传入必要参数
                Notification notification = WxPayUtil.verifyBack(request, wxConfig);
                log.info("=================微信验证签名成功=======成功时间=={}=====",notification.getCreateTime());
                // 思路: 验证订单 订单号是否存在 订单状态 通过缓存来做到 一回调验证多订单的类型
                // 生成订单的时候 把订单信息放入缓存中 order:key key为订单号 30min 通过获取 订单消息做到 快速验证 插入操作 用if 进行
                if (iToolWxConfigService.verifyCreateOrder(notification.getDecryptData())) {
                    log.info("==============================微信退款成功订单=====================================");
                    result.put("code", WXOrderConstant.WX_BACK_OK);
                    result.put("message", "支付回调成功");
                }
            } catch (ValidationException | ParseException | IOException e) {
                log.error("微信支付回调失败验证" + e);
            }
        log.info("微信返回结果"+result);
        return gson.toJson(result);
    }

2.2验证静态方法

 /**
     *回调验证
     * @param request 微信回调请求
     * @param wxConfig 微信基本配置信息
     * @return String
     * @author zhangjunrong
     * @date 2022/4/21 15:02
     */
    public static Notification verifyBack(HttpServletRequest request, ToolWxConfig wxConfig) throws IOException, ValidationException, ParseException {
        //应答报文主体
        BufferedReader br = request.getReader();
        String str;
        StringBuilder builder = new StringBuilder();
        while ((str = br.readLine()) != null) {
            builder.append(str);
        }
        // 构建request,传入必要参数
        //参数 1.微信序列号 2.应答随机串 3.应答时间戳 4.应答签名 5.应答报文主体
        NotificationRequest notificationRequest = new NotificationRequest.Builder()
                .withSerialNumber(request.getHeader(WechatPayHttpHeaders.WECHATPAY_SERIAL))
                .withNonce(request.getHeader(WechatPayHttpHeaders.WECHATPAY_NONCE))
                .withTimestamp(request.getHeader(WechatPayHttpHeaders.WECHATPAY_TIMESTAMP))
                .withSignature(request.getHeader(WechatPayHttpHeaders.WECHATPAY_SIGNATURE))
                .withBody(builder.toString())
                .build();
        NotificationHandler handler = new NotificationHandler(WxPayUtil.getVerifier(wxConfig), wxConfig.getApiV3key().getBytes(StandardCharsets.UTF_8));
        // 验签和解析请求体
        log.info("验签和解析请求体==============================开始验证==============================");
        Notification notification = handler.parse(notificationRequest);
        Assert.assertNotNull(notification);
        return notification;
    }

2.3缓存参数 常量

package com.yqs.constant.wechatPay;

/**
 * @Description 微信定时任务
 * @Author 小乌龟
 * @Date 2022/5/13 9:01
 */
public class WxRedisKey {
    /**
     *微信支付订单 list<对象>=>(订单详细信息key+订单类型+下单时间+支付状态) 有效时间6分钟
     */
    public static final String WX_PAY_ORDER = "wxPayOrderList";

    /**
     *微信退款订单 存微信基础信息 有效时间3分钟 wxRefundOrder::
     */
    public static final String WX_REFUND_ORDER = "wxRefundOrder::";
}

2.4 service 校验金额

2.4.1金额静态方法校验

 /**
      *验证用户支付金额 场景1.微信回调验证 2.定时任务核对订单
      * @param node 微信回调json 返回参数
      * @param redisTotal redis记录金额
      * @return Boolean
      * @author zhangjunrong
      * @date 2022/5/16 8:39
      */
    public static Boolean verifyMoney(JsonNode node,Integer redisTotal){
        //总金额计数值 用户支付计算
        int userPayTotal = SystemConstant.NUM_ZERO;
        //1.验证订单金额
        //用户支付金额
        int payerTotal = node.get(WXOrderConstant.AMOUNT).get(WXOrderConstant.AMOUNT_PAYER_TOTAL).asInt();
        userPayTotal = userPayTotal + payerTotal;
        //CASH充值型代金券 要加上优惠金额 银行优惠 获取
        //排空 如果没有优惠则跳过
        if (!ObjectUtil.isEmpty(node.get(WXOrderConstant.PROMOTION_DETAIL))) {
            for (JsonNode objNode : node.get(WXOrderConstant.PROMOTION_DETAIL)) {
                //如果优惠类型为CASH 则要和 用户支付金额 累加
                if (WXOrderConstant.WX_DISCOUNT_TYPE.equals(objNode.get(WXOrderConstant.PROMOTION_DETAIL_TYPE).textValue())) {
                    userPayTotal = userPayTotal + objNode.get(WXOrderConstant.AMOUNT).asInt();
                }
            }
        }
        //2.总金额 预支付时的 金额 与 total 用户支付金额
        //微信端返回的支付总金额
        int wxTotal = node.get(WXOrderConstant.AMOUNT).get(WXOrderConstant.AMOUNT_TOTAL).asInt();
        //redis缓存中的金额
        //校验通知的信息是否与商户侧的信息一致,防止数据泄露导致出现“假通知”,造成资金损失。
        //缓存中存入的用户支付金额 totalRedisRO.getTicketOrder().getTotalMoney().movePointRight(SystemConstant.NUM_TWO).intValue()
        log.info("微信回调金额===比较=== "+"微信端返回的支付总金额"+node.get(WXOrderConstant.AMOUNT).get(WXOrderConstant.AMOUNT_TOTAL).asInt()+"==========redis缓存中的金额"+redisTotal+"================用户支付总金额计算"+userPayTotal);
        //只要缓存的金额小于用户实际付款金额 判定成功
        return wxTotal == userPayTotal && redisTotal == wxTotal;
    }

2.4.2总体校验

@Override
    public Boolean verifyCreateOrder(String decryptOrder) {
        //在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
        //实现: 加入一把可重入锁
        if (reentrantLock.tryLock()) {
            try {
                log.info("===================================进入微信支付回调核对订单中========================================");
                ObjectMapper objectMapper = new ObjectMapper();
                //微信回调 解密后 信息
                JsonNode node = objectMapper.readTree(decryptOrder);
                //获取订单商户号
                String orderNo = node.get(WXOrderConstant.OUT_TRADE_NO).textValue();
                //1.获取redis中的订单信息
                OrderTotalRedisRO totalRedisRO = (OrderTotalRedisRO) redisUtil.get(SystemConstant.ORDER_TOTAL + orderNo);
                //1.1微信 同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
                //实现方法: 通过订单状态来 判定是否要进行判定 出未支付以外的 都返回 结果 通过缓存获取到支付状态 防止微信重复调用该方法
                //如果回调 缓存中记录清除说明 入库判定等等成功 直接返回true
                if (ObjectUtil.isEmpty(totalRedisRO)) {
                    return true;
                }
                log.info(node.get(WXOrderConstant.OUT_TRADE_NO) + "订单回调信息记录:订单状态:" + orderNo);
                //2.如果回调 支付类型为成功 核对金额 入数据库
                //获取支付状态
                String tradeState = node.get(WXOrderConstant.TRADE_STATE).textValue();
                if (StrUtil.equals(WXOrderConstant.WX_BACK_OK, tradeState)) {
                    //redis缓存中的金额
                    int redisTotal = totalRedisRO.getTicketOrder().getPayMoney().movePointRight(SystemConstant.NUM_TWO).intValue();
                    //校验通知的信息是否与商户侧的信息一致,防止数据泄露导致出现“假通知”,造成资金损失。
                    //缓存中存入的用户支付金额 totalRedisRO.getTicketOrder().getTotalMoney().movePointRight(SystemConstant.NUM_TWO).intValue()
                    if (WxPayUtil.verifyMoney(node, redisTotal)) {
                        //3.对应的数据入库
                        log.info("redis入数据库信息======================" + totalRedisRO);
                        if (!ObjectUtil.isEmpty(totalRedisRO)) {
                            //缓存放入一个状态 表明已操作该订单 存放200秒
                            // 支付成功就把redis中缓存记录清除
                            totalRedisRO.getTicketOrder().setOrderStatus(SystemConstant.NUM_ONE);
                            redisUtil.del(SystemConstant.ORDER_TOTAL + orderNo);
                           //订单入库
                            iTicketOrderService.createAllTicket(totalRedisRO, node.get(WXOrderConstant.TRANSACTION_ID).textValue());
                        }
                    }
                    //为什么没有插入成功也返回true?
                    //因为就算数据库没有入成功 但是金额 订单校验等等的都通过
                    //说明数据库入库失败
                    //如果入库失败 让用户联系客服接入管理 [钱一定要收下来]
                    return true;
                }
            } catch (Exception e) {
                log.error("订单支付异常===>订单回调信息记录:订单状态:" + decryptOrder);
            }finally {
                //释放锁
                reentrantLock.unlock();
            }
        }
        return false;
    }

3. 定时任务 查询处理特殊订单

3.1 get请求 统一配置

   /**
     *构造HttpClient 实现 微信申请接口 调用功能
     * @param wxConfig 微信支付数据库参数
     * @param verifier 微信验签器
     * @return CloseableHttpClient
     * @author zhangjunrong
     * @date 2022/5/16 8:54
     */
    public static CloseableHttpClient getHttpClient(ToolWxConfig wxConfig, Verifier verifier) {
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(wxConfig.getPrivateKey().getBytes(StandardCharsets.UTF_8)));
        //通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(wxConfig.getMchId(), wxConfig.getMchSerialNo(), merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier));
        return builder.build();
    }
/**
     * 调用微信支付 get请求 统一配置
     * 要微信签名认证
     * @param url
     * @return String
     * @author zhangjunrong
     * @date 2022/5/9 20:39
     */
    private String getHttpGet(ToolWxConfig wxConfig, String url) throws URISyntaxException, IOException {
        //1.构造httpGet请求
        URIBuilder uriBuilder = null;
        uriBuilder = new URIBuilder(url);
        HttpGet httpGet = new HttpGet(uriBuilder.build());
        httpGet.addHeader(WechatPayHttpHeaders.ACCEPT, WechatPayHttpHeaders.APPLICATION_JSON);
        //2.调起微信查询订单接口
        CloseableHttpResponse response = WxPayUtil.getHttpClient(wxConfig, WxPayUtil.getVerifier(wxConfig)).execute(httpGet);
        //3.返回结果信息
        return EntityUtils.toString(response.getEntity());
    }

3.2 查询订单service

  /**
     * 查询订单
     * 使用场景:
     * 1.当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知。
     * 2.调用支付接口后,返回系统错误或未知交易状态情况。
     * 3.调用付款码支付API,返回USERPAYING(用户付费)的状态。
     * 4.调用关单或撤销接口API之前,需确认支付状态。
     * @param wxConfig   微信配置
     * @param outTradeNo 商户订单号 系统生成
     * @return String 订单交易状态
     * @author zhangjunrong
     * @date 2022/4/14 16:14
     *
     */ 
@Override
    public JsonNode queryCreateOrder(ToolWxConfig wxConfig, String outTradeNo) {
        try {
            //1.查单 微信接口 编辑 微信订单号 + 商户号
            String url = StrFormatter.format(WxApiType.QUERY_CREATE_ORDER.getValue(), outTradeNo, wxConfig.getMchId());
            //2.调用微信接口
            String bodyAsString = getHttpGet(wxConfig, url);
            log.info("支付查单信息" + bodyAsString);
            //返回查单结果信息
            if (ObjectUtil.isNotEmpty(bodyAsString)){
                ObjectMapper objectMapper = new ObjectMapper();
                return objectMapper.readTree(bodyAsString);
            }
        } catch (Exception e) {
            log.error("支付查单失败" + outTradeNo);
        }
        return null;
    }

3.3关闭订单

  /**
     * 关闭订单
     * 使用场景:
     * 1、商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;
     * 2、系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。
     * 注意:关单没有时间限制,建议在订单生成后间隔几分钟(最短5分钟)再调用关单接口,避免出现订单状态同步不及时导致关单失败
     *
     * @param wxConfig
     * @param outTradeNo
     * @return String
     * @author zhangjunrong
     * @date 2022/4/14 17:02
     */
@Override
    public String closeOrder(ToolWxConfig wxConfig, String outTradeNo) {
        try {
            //1.微信接口编辑
            String url = StrFormatter.format(WxApiType.CLOSE_ORDER.getValue(), outTradeNo);
            HttpPost httpPost = new HttpPost(url);
            //格式配置
            httpPost.addHeader(WechatPayHttpHeaders.ACCEPT, WechatPayHttpHeaders.APPLICATION_JSON);
            httpPost.addHeader(WechatPayHttpHeaders.CONTENT_TYPE, WechatPayHttpHeaders.APPLICATION_JSON_UTF);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            //2.添加商户id
            ObjectMapper objectMapper = new ObjectMapper();
            ObjectNode rootNode = objectMapper.createObjectNode();
            rootNode.put(WXOrderConstant.MCHID, wxConfig.getMchId());
            objectMapper.writeValue(bos, rootNode);
            //3.调起微信关单接口
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            CloseableHttpResponse response = WxPayUtil.getHttpClient(wxConfig, WxPayUtil.getVerifier(wxConfig)).execute(httpPost);
            //无数据(Http状态码为204) 微信返回结果无数据 状态码为204 成功
            if (response.getStatusLine().getStatusCode() == MessageEnum.NO_CONTENT.getCode()) {
                return "关单成功";
            }
        } catch (Exception e) {
            log.error("关单失败" + outTradeNo + e);
        }
        return null;
    }

3.4 定时任务 处理订单

3.4.1 金额校验 (和微信回调处理金额校验相同)

 /**
      *验证用户支付金额 场景1.微信回调验证 2.定时任务核对订单
      * @param node 微信回调json 返回参数
      * @param redisTotal redis记录金额
      * @return Boolean
      * @author zhangjunrong
      * @date 2022/5/16 8:39
      */
    public static Boolean verifyMoney(JsonNode node,Integer redisTotal){
        //总金额计数值 用户支付计算
        int userPayTotal = SystemConstant.NUM_ZERO;
        //1.验证订单金额
        //用户支付金额
        int payerTotal = node.get(WXOrderConstant.AMOUNT).get(WXOrderConstant.AMOUNT_PAYER_TOTAL).asInt();
        userPayTotal = userPayTotal + payerTotal;
        //CASH充值型代金券 要加上优惠金额 银行优惠 获取
        //排空 如果没有优惠则跳过
        if (!ObjectUtil.isEmpty(node.get(WXOrderConstant.PROMOTION_DETAIL))) {
            for (JsonNode objNode : node.get(WXOrderConstant.PROMOTION_DETAIL)) {
                //如果优惠类型为CASH 则要和 用户支付金额 累加
                if (WXOrderConstant.WX_DISCOUNT_TYPE.equals(objNode.get(WXOrderConstant.PROMOTION_DETAIL_TYPE).textValue())) {
                    userPayTotal = userPayTotal + objNode.get(WXOrderConstant.AMOUNT).asInt();
                }
            }
        }
        //2.总金额 预支付时的 金额 与 total 用户支付金额
        //微信端返回的支付总金额
        int wxTotal = node.get(WXOrderConstant.AMOUNT).get(WXOrderConstant.AMOUNT_TOTAL).asInt();
        //redis缓存中的金额
        //校验通知的信息是否与商户侧的信息一致,防止数据泄露导致出现“假通知”,造成资金损失。
        //缓存中存入的用户支付金额 totalRedisRO.getTicketOrder().getTotalMoney().movePointRight(SystemConstant.NUM_TWO).intValue()
        log.info("微信回调金额===比较=== "+"微信端返回的支付总金额"+node.get(WXOrderConstant.AMOUNT).get(WXOrderConstant.AMOUNT_TOTAL).asInt()+"==========redis缓存中的金额"+redisTotal+"================用户支付总金额计算"+userPayTotal);
        //只要缓存的金额小于用户实际付款金额 判定成功
        return wxTotal == userPayTotal && redisTotal == wxTotal;
    }

3.4.2 定时任务处理 


    /**
     * 清除过去票 恢复库存
     *
     * @param order 子订单信息
     * @return void
     * @author zhangjunrong
     * @date 2022/5/16 19:44
     */
    private void dealVoidTicket(WxPayListRO order) {
//微信订单redis2 订单详情
        OrderTotalRedisRO orderTotalRedisRO = (OrderTotalRedisRO) redisUtil.get(SystemConstant.ORDER_TOTAL + order.getOutTradeNo());
        orderTotalRedisRO.getTicketOrderItems().stream()
                .filter(Objects::nonNull)
                .forEach(ticketOrderItem -> {
                            //恢复票相应的库存
                            Boolean timeOrder = iTicketOrderService.updateTimeOrder(ticketOrderItem.getTicketId(), ticketOrderItem.getUseStart(), ticketOrderItem.getBranchStart(), ticketOrderItem.getBuyCount());
                            log.info("订单恢复库存{}======订单信息{}===============", timeOrder, ticketOrderItem);
                        }
                );
        //清除缓存 redis1
        long lRemove = redisUtil.lRemove(WxRedisKey.WX_PAY_ORDER, SystemConstant.NUM_ONE, order);
        log.info("清除redis1缓存" + lRemove);
        //清除缓存 redis2
        redisUtil.del(SystemConstant.ORDER_TOTAL + order.getOutTradeNo());
    }

    /**
     * 对于下单定时任务核对 每间隔50秒查询5分钟前的订单
     * 1.防止微信付款成功 订单数据未入库 2.实现定期关单功能
     *
     * @return void
     * @author zhangjunrong
     * @date 2022/5/13 9:59
     */
    @Async
    @Scheduled(cron = "0/50 * * * * ? ")
    public void orderTimeCheck() {
        //redis1 key=>wxPay 设置7分钟有效 一直有效(4分钟更新一次)(redis2key+订单类型+下单时间+支付状态)list<对象>数组redis2 10分钟有效期订单详细信息
        //redis2 订单详细信息 8分钟有效时间
        //1.获取出redis1 微信支付订单list
        List<Object> orderList = redisUtil.lGet(WxRedisKey.WX_PAY_ORDER, SystemConstant.NUM_ZERO, SystemConstant.NUM_NEGATIVE_ONE);
        if (ObjectUtil.isNotEmpty(orderList)) {
            log.info("定时任务核销单====处理特殊订单开启====================");
            List<WxPayListRO> wxPayListROS = redisUtil.castList(orderList, WxPayListRO.class);
            wxPayListROS.stream()
                    //2.获取 前5分钟的订单信息
                    .filter(order -> DateUtil.between(order.getCreateOrderTime(), DateUtil.date(), DateUnit.MINUTE) >= SystemConstant.NUM_FIVE)
                    //3.过滤掉已处理的订单 redis2中没有的订单 支付成功会删除redis2记录
                    .filter(order ->
                    {
                        //如果redis2中没有该记录 则删除掉redis1对于该记录
                        if (!redisUtil.hasKey(SystemConstant.ORDER_TOTAL + order.getOutTradeNo())) {
                            log.info("定时任务判定是redis否拥有订单====" + order.getOutTradeNo());
                            long lRemove = redisUtil.lRemove(WxRedisKey.WX_PAY_ORDER, SystemConstant.NUM_ONE, order);
                            log.info("清除redis1记录" + lRemove);
                            return false;
                        }
                        return true;
                    })
                    .forEach(order -> {
                        //4.调微信接口判定
                        log.info("定时任务核销单====微信定时任务开始核对订单未支付成功订单====" + order);
                        ToolWxConfig wxConfig = iToolWxConfigService.find();
                        //4.1查询微信那的订单状态
                        JsonNode queryCreateOrder = iToolWxConfigService.queryCreateOrder(wxConfig, order.getOutTradeNo());
                        //4.2如果查询结果为null 说明用户没有调启微信支付 直接清除订单redis
                        if (ObjectUtil.isEmpty(queryCreateOrder.get(WXOrderConstant.TRADE_STATE))) {
                            log.info("定时任务核销单====微信订单信息不存在==============");
                            //恢复票务库存 清除库存
                            dealVoidTicket(order);
                        } else {
                            //交易状态
                            String tradeState = queryCreateOrder.get(WXOrderConstant.TRADE_STATE).textValue();
                            String transactionId = queryCreateOrder.get(WXOrderConstant.TRANSACTION_ID).textValue();
                            log.info("定时任务核销单====微信订单状态: 微信接口 对象" + queryCreateOrder + "redis缓存" + order.getOrderStatus());
                            // 4.3核对支付 状态 如果未支付 关单 redis1和redis2清除缓存
                            if (WxPayStatusEnum.NOTPAY.getValue().equals(tradeState)) {
                                log.info("定时任务核销单====微信订单信息未支付==========");
                                //关闭订单
                                String closeOrder = iToolWxConfigService.closeOrder(wxConfig, order.getOutTradeNo());
                                log.info("定时任务核销单====返回结果" + closeOrder);
                                //恢复票务库存 清除库存
                                dealVoidTicket(order);
                            }
                            // 4.4核对支付 状态 如果已支付 redis1未支付 数据库查单再次判定 如果不存在 则入数据库
                            if (WxPayStatusEnum.SUCCESS.getValue().equals(tradeState)) {
                                //数据库查单判定
                                if (ObjectUtil.isEmpty(iTicketOrderService.queryOrderByTranId(transactionId))) {
                                    log.info("定时任务核销单====微信订单信息支付==========数据库未有数据");
                                    //获取redis2
                                    OrderTotalRedisRO orderTotalRedisRO = (OrderTotalRedisRO) redisUtil.get(SystemConstant.ORDER_TOTAL + order.getOutTradeNo());
                                    log.info("微信redis2获取信息===" + orderTotalRedisRO.toString());
                                    //订单金额验证 等等 插入数据库
                                    //redis缓存中的金额
                                    int redisTotal = orderTotalRedisRO.getTicketOrder().getPayMoney().movePointRight(SystemConstant.NUM_TWO).intValue();
                                    //只有金额对等 方可入数据库
                                    if (WxPayUtil.verifyMoney(queryCreateOrder, redisTotal)) {
                                        iTicketOrderService.createAllTicket(orderTotalRedisRO, transactionId);
                                    }
                                    //清除缓存 redis1
                                    long lRemove = redisUtil.lRemove(WxRedisKey.WX_PAY_ORDER, SystemConstant.NUM_ONE, order);
                                    log.info("清除redis1缓存" + lRemove);
                                    //清除缓存 redis2
                                    redisUtil.del(SystemConstant.ORDER_TOTAL + order.getOutTradeNo());
                                }
                            }
                        }
                    });
        }


    }

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

新生代农民工-小王八

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值