微信V3版本支付下单、查询支付订单状态、订单退款接入正式项目中并引入策略模式实操

开篇介绍:

一、策略模式(Strategy Pattern)的概念

二、应用场景举例

三、角色

四、服务端OR客户端生成支付等二维码优缺点分析

五、 多渠道支付对接-策略模式+简单工厂模式编码实操

六、查询支付订单状态

七、再看下订单退款实操


开篇介绍:

        本文介绍微信支付中的Native支付方式,版本是APIv3,其中Native和JSAPI的区别如下
Native支付:商家在系统中按微信支付协议生成支付二维码,用户扫码拉起微信收银台,确认并完成付款
JSAPI支付:商家张贴收款码物料,用户打开扫一扫,扫码后输入金额,完成付款
以下内容增加了支付接口调和策略模式的使用,支付的VO类,大家可以参照自己公司的VO字段,需要改动支付的部分业务参数即可。

 注意:微信支付个人无法对接操作,需要有公司商户账号,一般开发过程中是产品经理或者相关负责人提供。

我们来看下支付成功和退款成功的截图,看完本博文后,你将学会在实际项目中完成微信支付。

 

 首先我们将先复习下策略模式,前面4步骤都是策略模式,后面几步骤是代码实操,不要急,一步步来。

一、策略模式(Strategy Pattern)的概念

    定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换
    比如说淘宝天猫双十一,正在搞活动有打折的、有满减的、有返利的等等,这些算法只是一种策略,并且是随时都可能互相替换的, 我们就可以定义一组算法,将每个算法都封装起来,并且使它们之间可以互换

二、应用场景举例

        。老王计划外出旅游,选择骑自行车、坐汽车、飞机等,每一种旅行方式都是一个策略
        。 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么可以使用策略模式
         。 不希望暴露复杂的、与算法有关的数据结构,那么可以使用策略模式来封装算法
         。对接第三方支付里面,微信支付、支付宝支付等都可以是一种策略 

三、角色

    Context上下文:屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化(相当于中介)
    Strategy策略角色:抽象策略角色,是对策略、算法家族的抽象,定义每个策略或算法必须具有的方法和属性
    ConcreteStrategy具体策略角色:用于实现抽象策略中的操作,即实现具体的算法 

四、服务端OR客户端生成支付等二维码优缺点分析

服务端生成二维码
优点:
    引入二维码jar包统一生成
    图片生成兼容性好,传输相对安全不易被篡改
缺点:
    占据服务端CPU内存/资源、消耗传输带宽,性能较差
    没法一并返回其他参数,定制化相对弱

客户端生成二维码(建议使用)
优点
    在客户端生成二维码会降低服务器的运算与存储压力
    选择框架要考虑兼容性,客户端根据js框架生成二维码
    后端返回 二维码文本值,还可以携带其他参数(比如订单号,可以轮训支付状态)
缺点
    客户端需要引入二维码生成js插件
    需要考虑浏览器兼容性

五、 多渠道支付对接-策略模式+简单工厂模式编码实操

 策略模式代码骨架分为以下几种:

策略接口开发 PayStrategy
策略上下文 PayStrategyContext开发
具体支付策略开发 AlipayStrategy、WechatPayStrategy
简单工厂类开发;

首先第一步我们从impl调用层开始倒推:

   //这个是在实际service实现类中的调用,下面这段代码被controller层调用,
 //然后返回给前端,前端根据返回的code_url展示二维码信息
    @Autowired
    private PayFactory payFactory;
   //调用支付信息
        String codeUrl = payFactory.pay(payInfoVO);
        if(StringUtils.isNotBlank(codeUrl)){
            Map<String,String> resultMap = new HashMap<>(2);
            resultMap.put("code_url",codeUrl);
            resultMap.put("out_trade_no",payInfoVO.getOutTradeNo());
            return JsonData.buildSuccess(resultMap);
      }

 第二步再来看下PayFactory支付工厂类,里面的支付 做了什么:

package net.wnn.component;

import lombok.extern.slf4j.Slf4j;
import net.wnn.enums.ProductOrderPayTypeEnum;
import net.wnn.vo.PayInfoVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class PayFactory {

    @Autowired
    private AliPayStrategy aliPayStrategy;

    @Autowired
    private WechatPayStrategy wechatPayStrategy;

    /**
     * 创建支付,简单工厂模式
     * @param payInfoVO
     * @return
     */
    public String pay(PayInfoVO payInfoVO){

        String payType = payInfoVO.getPayType();

        if (ProductOrderPayTypeEnum.ALI_PAY.name().equals(payType)) {
            //支付宝支付
            PayStrategyContext payStrategyContext = new PayStrategyContext(aliPayStrategy);
            return payStrategyContext.executeUnifiedOrder(payInfoVO);

        } else if(ProductOrderPayTypeEnum.WECHAT_PAY.name().equals(payType)){

            //微信支付
            PayStrategyContext payStrategyContext = new PayStrategyContext(wechatPayStrategy);
            return payStrategyContext.executeUnifiedOrder(payInfoVO);
        }
        return "";
    }


    /**
     * 关闭订单
     * @param payInfoVO
     * @return
     */
    public String closeOrder(PayInfoVO payInfoVO){

        String payType = payInfoVO.getPayType();

        if (ProductOrderPayTypeEnum.ALI_PAY.name().equals(payType)) {
            //支付宝支付
            PayStrategyContext payStrategyContext = new PayStrategyContext(aliPayStrategy);
            return payStrategyContext.executeCloseOrder(payInfoVO);

        } else if(ProductOrderPayTypeEnum.WECHAT_PAY.name().equals(payType)){

            //微信支付
            PayStrategyContext payStrategyContext = new PayStrategyContext(wechatPayStrategy);
            return payStrategyContext.executeCloseOrder(payInfoVO);
        }
        return "";
    }


    /**
     * 查询支付状态
     * @param payInfoVO
     * @return
     */
    public String queryPayStatus(PayInfoVO payInfoVO){

        String payType = payInfoVO.getPayType();

        if (ProductOrderPayTypeEnum.ALI_PAY.name().equals(payType)) {
            //支付宝支付
            PayStrategyContext payStrategyContext = new PayStrategyContext(aliPayStrategy);
            return payStrategyContext.executeQueryPayStatus(payInfoVO);

        } else if(ProductOrderPayTypeEnum.WECHAT_PAY.name().equals(payType)){

            //微信支付
            PayStrategyContext payStrategyContext = new PayStrategyContext(wechatPayStrategy);
            return payStrategyContext.executeQueryPayStatus(payInfoVO);
        }
        return "";
    }


    /**
     * 退款接口
     * @param payInfoVO
     * @return
     */
    public String refund(PayInfoVO payInfoVO){

        String payType = payInfoVO.getPayType();

        if (ProductOrderPayTypeEnum.ALI_PAY.name().equals(payType)) {
            //支付宝支付
            PayStrategyContext payStrategyContext = new PayStrategyContext(aliPayStrategy);
            return payStrategyContext.executeRefund(payInfoVO);

        } else if(ProductOrderPayTypeEnum.WECHAT_PAY.name().equals(payType)){
            //微信支付
            PayStrategyContext payStrategyContext = new PayStrategyContext(wechatPayStrategy);
            return payStrategyContext.executeRefund(payInfoVO);
        }

        return "";
    }




}

由上面代码可见,支付的工厂是用来根据支付方式调用具体的实现方法 。

第三步,这一步我们来看一下PayStrategyContext支付上下文是什么东西:

package net.wnn.component;

import net.wnn.vo.PayInfoVO;


public class PayStrategyContext  {

    private PayStrategy payStrategy;

    public PayStrategyContext(PayStrategy payStrategy){
        this.payStrategy = payStrategy;
    }

    /**
     * 根据策略对象,执行不同的下单接口
     * @return
     */
    public String executeUnifiedOrder(PayInfoVO payInfoVO){

        return payStrategy.unifiedOrder(payInfoVO);
    }


    /**
     * 根据策略对象,执行不同的退款接口
     * @return
     */
    public String executeRefund(PayInfoVO payInfoVO){

        return payStrategy.refund(payInfoVO);
    }

    /**
     * 根据策略对象,执行不同的关闭接口
     * @return
     */
    public String executeCloseOrder(PayInfoVO payInfoVO){

        return payStrategy.closeOrder(payInfoVO);
    }

    /**
     * 根据策略对象,执行不同的查询订单状态接口
     * @return
     */
    public String executeQueryPayStatus(PayInfoVO payInfoVO){

        return payStrategy.queryPayStatus(payInfoVO);
    }


}

标准对Context上下文的解释就是:屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。通俗点说这就是个中介,根据传入的参数去调相应的实现接口,自己就干个调用

第四步、再看看PayStrategy

package net.wnn.component;

import net.wnn.vo.PayInfoVO;

public interface PayStrategy {

    /**
     * 统一下单接口
     * @param payInfoVO
     * @return
     */
    String unifiedOrder(PayInfoVO payInfoVO);


    /**
     * 退款接口
     * @param payInfoVO
     * @return
     */
    default String refund(PayInfoVO payInfoVO){ return ""; }


    /**
     * 查询支付状态
     * @param payInfoVO
     * @return
     */
    default String queryPayStatus(PayInfoVO payInfoVO){ return ""; }


    /**
     * 关闭订单
     * @param payInfoVO
     * @return
     */
    default String closeOrder(PayInfoVO payInfoVO){ return ""; }

}

 Strategy策略角色:抽象策略角色,是对策略、算法家族的抽象,定义每个策略或算法必须具有的方法和属性

 第五步、再看看AliPayStrategy,这只是代码骨架,表明策略模式的实现方式,本文重点说的是微信支付,在第六步有

package net.wnn.component;

import lombok.extern.slf4j.Slf4j;
import net.wnn.vo.PayInfoVO;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class AliPayStrategy implements  PayStrategy{

    @Override
    public String unifiedOrder(PayInfoVO payInfoVO) {
        return null;
    }

    @Override
    public String refund(PayInfoVO payInfoVO) {
        return null;
    }

    @Override
    public String queryPayStatus(PayInfoVO payInfoVO) {
        return null;
    }

    @Override
    public String closeOrder(PayInfoVO payInfoVO) {
        return null;
    }
}

ConcreteStrategy就是策略模式中的具体策略角色:用于实现抽象策略中的操作,即实现具体的算法

第六步、本文用的是微信支付,那就再看下WechatPayStrategy 下单的代码逻辑

package net.wnn.component;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import net.wnn.config.WechatPayApi;
import net.wnn.config.WechatPayConfig;
import net.wnn.vo.PayInfoVO;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;

@Service
@Slf4j
public class WechatPayStrategy  implements  PayStrategy{


    @Autowired
    private WechatPayConfig payConfig;

    @Autowired
    private CloseableHttpClient wechatPayClient;



    /**
     * 统一下单接口
     * @param payInfoVO
     * @return
     */
    @Override
    public String unifiedOrder(PayInfoVO payInfoVO) {

        //过期时间  RFC 3339格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
        //支付订单过期时间
        String timeExpire = sdf.format(new Date(System.currentTimeMillis() + payInfoVO.getOrderPayTimeoutMills()));

        JSONObject amountObj = new JSONObject();
        //数据库存储是double比如,100.99元,微信支付需要以分为单位

        int amount = payInfoVO.getPayFee().multiply(BigDecimal.valueOf(60)).intValue();
        amountObj.put("total", amount);
        amountObj.put("currency", "CNY");

        JSONObject payObj = new JSONObject();
        payObj.put("mchid", payConfig.getMchId());
        payObj.put("out_trade_no", payInfoVO.getOutTradeNo());
        payObj.put("appid", payConfig.getWxPayAppid());
        payObj.put("description", "王姑娘之微信支付增加策略模式后的下单验证");
        payObj.put("notify_url", payConfig.getCallbackUrl());

        payObj.put("time_expire", timeExpire);
        payObj.put("amount", amountObj);
        //回调携带
        payObj.put("attach", "{\"accountNo\":" + payInfoVO.getAccountNo() + "}");

        // 处理请求body参数
        String body = payObj.toJSONString();


        log.debug("请求参数:{}",body);

        StringEntity entity = new StringEntity(body,"utf-8");
        entity.setContentType("application/json");

        HttpPost httpPost = new HttpPost(WechatPayApi.NATIVE_ORDER);
        httpPost.setHeader("Accept","application/json");
        httpPost.setEntity(entity);

        String result = "";
        try(CloseableHttpResponse response = wechatPayClient.execute(httpPost)){

            //响应码
            int statusCode = response.getStatusLine().getStatusCode();
            //响应体
            String responseStr = EntityUtils.toString(response.getEntity());

            log.debug("下单响应码:{},响应体:{}",statusCode,responseStr);

            if(statusCode == HttpStatus.OK.value()){
                JSONObject jsonObject = JSONObject.parseObject(responseStr);
                if(jsonObject.containsKey("code_url")){
                    result = jsonObject.getString("code_url");
                }

            }else {
                log.error("下单响应失败:{},响应体:{}",statusCode,responseStr);
            }

        }catch (Exception e){
            log.error("微信支付响应异常:{}",e);
        }

        return result;
    }
}

第七步、下单验证截图

postman请求:

根据code_url生成的二维码,用微信客户端进行扫码支付:

 下单支付接口调用的一些参数:

请求参数:{"time_expire":"2022-03-02T21:29:14+08:00","amount":{"total":60,"currency":"CNY"},"mchid":"1601xxxx","out_trade_no":"yb6gMTsnzyxxxxl4","appid":"wx5beac1xxxxc","description":"王姑娘之微信支付增加策略模式后的下单验证","attach":"{\"accountNo\":693232xxxx}","notify_url":"http://api.xxxx/shop-server/api/callback/order/v1/wechat"}
下单响应码:200,响应体:{"code_url":"weixin://wxpay/bizpayurl?pr=8L2yqCSzz"}

支付截图:

支付成功截图:

看到这你肯定有疑惑了,支付的那些参数等都是从哪来,怎么加载的?由于文章篇幅有限,基础的微信支付信息,写在了下面这篇博客中,可以直接点击查看,里面详细的介绍了微信支付的准备参数:

一文带你学会微信V3版本下单支付、退款、关单流程代码实操_8年开发工作经验的老王,积极分享工作中遇到的问题~-CSDN博客_wechatpay-apache-httpclient

六、查询支付订单状态

第一步实现类的调用:

  @Override
    public String queryProductOrderState(String outTradeNo) {
        PayInfoVO payInfoVO = PayInfoVO.builder().outTradeNo(outTradeNo).payType("WECHAT_PAY").build();
        String result = payFactory.queryPayStatus(payInfoVO);
        return  result;
    }

第二步:

PayFactory的queryPayStatus方法,在上面下单的工厂类都有,这里不再赘述
PayStrategyContext的executeQueryPayStatus方法,上面也有,这里不再赘述 

第三步:支付订单查询的具体操作代码

package net.wnn.component;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import net.wnn.config.WechatPayApi;
import net.wnn.config.WechatPayConfig;

import net.wnn.vo.PayInfoVO;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;



@Service
@Slf4j
public class WechatPayStrategy  implements  PayStrategy{


    @Autowired
    private WechatPayConfig payConfig;

    @Autowired
    private CloseableHttpClient wechatPayClient;


    /**
     * 微信支付查询订单状态
     * @param payInfoVO
     * @return
     */
    @Override
    public String queryPayStatus(PayInfoVO payInfoVO) {
        String outTradeNo = payInfoVO.getOutTradeNo();

        String url = String.format(WechatPayApi.NATIVE_QUERY,outTradeNo,payConfig.getMchId());
        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("Accept","application/json");

        String result = "";
        try(CloseableHttpResponse response = wechatPayClient.execute(httpGet)){

            //响应码
            int statusCode = response.getStatusLine().getStatusCode();
            //响应体
            String responseStr = EntityUtils.toString(response.getEntity());

            log.info("查询响应码:{},响应体:{}",statusCode,responseStr);

            if(statusCode == HttpStatus.OK.value()){
                JSONObject jsonObject = JSONObject.parseObject(responseStr);
                if(jsonObject.containsKey("trade_state")){
                    result = jsonObject.getString("trade_state");
                }
            }else {
                log.error("查询支付状态响应失败:{},响应体:{}",statusCode,responseStr);
            }


        }catch (Exception e){
            log.error("微信支付响应异常:{}",e);
        }

        return result;
    }

}

第四步 postman请求查询接口接口 传参 支付订单ID

 查询返回结果: 订单支付成功

查询响应码:200,响应体:{"amount":{"currency":"CNY","payer_currency":"CNY","payer_total":60,"total":60},"appid":"wx5xxxc","attach":"{\"accountNo\":693232391484866560}","bank_type":"OTHERS","mchid":"16016xxxx",

"out_trade_no":"yb6gMTsnzyNrjb5iDa4Oxxx","payer":{"openid":"oiNKG04xxx"},"promotion_detail":[],"success_time":"2022-03-02T20:59:36+08:00","trade_state":"SUCCESS","trade_state_desc":"支付成功","trade_type":"NATIVE","transaction_id":"4200001363202203029043441628"}

七、再看下订单退款实操

第一步实现类的调用:

  @Override
    public String executeRefund(String outTradeNo) {
        PayInfoVO payInfoVO = PayInfoVO.builder().outTradeNo(outTradeNo).payType("WECHAT_PAY").build();
        String result = payFactory.refund(payInfoVO);
        return  result;
    }

第二步:

PayFactory的refund方法,在上面下单的工厂类都有,这里不再赘述
PayStrategyContext的executeRefund方法,上面也有,这里不再赘述 

第三步:支付订单退款的具体操作代码

package net.wnn.component;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import net.wnn.config.WechatPayApi;
import net.wnn.config.WechatPayConfig;
import net.wnn.util.CommonUtil;
import net.wnn.vo.PayInfoVO;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;

@Service
@Slf4j
public class WechatPayStrategy  implements  PayStrategy{


    @Autowired
    private WechatPayConfig payConfig;

    @Autowired
    private CloseableHttpClient wechatPayClient;

    @Override
    public String refund(PayInfoVO payInfoVO) {
        String outTradeNo = "yb6gMTsnzyNrjb5iDa4OYFwiRc5nh7l4";
        String refundNo = CommonUtil.getStringNumRandom(32);

        // 请求body参数
        JSONObject refundObj = new JSONObject();
        //订单号
        refundObj.put("out_trade_no", outTradeNo);
        //退款单编号,商户系统内部的退款单号,商户系统内部唯一,
        // 只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔
        refundObj.put("out_refund_no", refundNo);
        refundObj.put("reason","商品已售完需要退款");
        refundObj.put("notify_url", payConfig.getCallbackUrl());

        JSONObject amountObj = new JSONObject();
        //退款金额
        amountObj.put("refund", 60);
        //实际支付的总金额
        amountObj.put("total", 60);
        amountObj.put("currency", "CNY");

        refundObj.put("amount", amountObj);


        String body = refundObj.toJSONString();

        log.info("请求参数:{}",body);

        StringEntity entity = new StringEntity(body,"utf-8");
        entity.setContentType("application/json");

        HttpPost httpPost = new HttpPost(WechatPayApi.NATIVE_REFUND_ORDER);
        httpPost.setHeader("Accept","application/json");
        httpPost.setEntity(entity);

        try(CloseableHttpResponse response = wechatPayClient.execute(httpPost)){

            //响应码
            int statusCode = response.getStatusLine().getStatusCode();
            //响应体
            String responseStr = EntityUtils.toString(response.getEntity());

            log.info("申请订单退款响应码:{},响应体:{}",statusCode,responseStr);

        }catch (Exception e){
            e.printStackTrace();
        }
        return "退款成功";
    }

}

退款成功图片:

退款日志消息:

请求参数:{"reason":"商品已售完需要退款","amount":{"total":60,"currency":"CNY","refund":60},"out_trade_no":"yb6gMTsnzyNrjb5iDa4xxx","out_refund_no":"RbpQWZntjoxxxx","notify_url":"http://api.oxxxx/shop-server//wechat"}
申请订单退款响应码:200,响应体:{"amount":{"currency":"CNY","discount_refund":0,"from":[],"payer_refund":60,"payer_total":60,"refund":60,"settlement_refund":60,"settlement_total":60,"total":60},"channel":"ORIGINAL","create_time":"2022-03-02T21:25:00+08:00","funds_account":"AVAILABLE","out_refund_no":"RbpQWZntjoxxxxq","out_trade_no":"yb6gMTsnzyNrjb5iDa4xxxx","promotion_detail":[],"refund_id":"50300400982022xxxx","status":"PROCESSING","transaction_id":"420000136320xxx","user_received_account":"支付用户零钱"}

到这微信Native支付方式的下单、查询、退款操作就整合进实际开发项目中啦,大家引用到自己项里面的时候,只需要改动支付的部分业务参数即可。

基础参数不知道从哪来,证书不知道怎么加载的,看下下面这篇博客,跟本博客代码内容是上下两集:

一文带你学会微信V3版本下单支付、退款、关单流程代码实操_8年开发工作经验的老王,积极分享工作中遇到的问题~-CSDN博客_wechatpay-apache-httpclient本文介绍微信支付中的Native支付方式,版本是APIv3,其中Native和JSAPI的区别如下Native支付:商家在系统中按微信支付协议生成支付二维码,用户扫码拉起微信收银台,确认并完成付款JSAPI支付:商家张贴收款码物料,用户打开扫一扫,扫码后输入金额,完成付款以下博文没有掺杂过多业务逻辑,对象数据都是固定写的,方便将注意力集中在微信支付的接口使用上。正式对接到项目里面,只需要改动支付的部分业务参数即可。微信支付的文档还是很详细的,建议多看几遍,以下博文也都是根据官方文档的描述来操https://blog.csdn.net/wnn654321/article/details/122933324

微信支付回调验签流程

微信支付V3版本回调+验签流程_8年开发工作经验的老王,积极分享工作中遇到的问题~-CSDN博客回调验签流程介绍 官方文档 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_5.shtml https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_1.shtml注意: 同样的通知可能会多次发送给商户系统,商户系统必须能够正确处理重复的通知 确保回调URL是外部可正常访问的,且不能携带后缀参数https://blog.csdn.net/wnn654321/article/details/123298162

  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 17
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值