JAVA微信扫码支付模式一功能实现

一、准备工作

4月份那会发了篇关于 Java微信扫码支付模式二功能实现的博客,无数人来追问模式一的开发,所以在这就贴出来,仅供参考。关于模式一和模式二的区别,我有解释过很多次,无非就是模式一的二维码是针对商品的,模式二的二维码是针对订单的,其他具体细节我就不费口舌了,各位可以自行去官方查看文档,然后是选模式一还是模式二就得看自己的业务了。

1.1、有关配置参数

还是之前那四样,APP_ID和APP_SECRET可以在公众平台找着,MCH_ID和API_KEY则在商户平台找到,特别是API_KEY要在商户平台设置好,这个东东关系到参数校验的正确与否,所以一定要设置正确。扫码支付模式一其实与扫码支付模式二类似,实际只会用到APP_ID、MCH_ID和API_KEY,其他的都不用。模式一的官方文档地址在这:

https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_4

1.2、有关概念

在这里我想先修正一个概念,在之前模式二开发过程中我曾提到了一个“支付回调地址”这样的概念,其作用实际就是客户在扫描完成支付后,微信服务器要访问我们提供的这个地址,给我们发送支付结果,以便我们核实订单进行发货,这是其他支付工具比较普遍的概念和叫法。不过后来我翻了一下微信官网的文档,发现在模式一开发中,他们把这个叫做“异步通知url”而不是什么“支付回调地址”,但本质这指的是一个意思。可是为什么我要在这提到这个东东呢?这是因为在模式一中,实际上还有另外一个所谓的“支付回调”称之为“扫码支付回调URL”,这东东与上面的“异步通知url”可就不一样了,简单理解可以认为是咱们的服务器上一个用来辅助完成下单的接口。模式一的开发同时需要“扫码支付回调URL”与“异步通知url”两个接口配合才能完成,所以这里大家要辨别好了。

“异步通知url”在调用统一下单接口时进行设置,可以动态设置,只要这个接口按照有关规则接收参数响应参数即可。而“扫码支付回调URL”则较为固定,它在微信公众平台设置,设置后需要10分钟左右才能生效,大家登录微信公众平台后,选择微信支付,在开发配置选项卡下面就可以找着:

这里咱们要设置一个自己服务器的地址(再说一遍公网地址,就是让微信服务器能找着你)。

1.3、开发环境

我这里以最基本的Servlet 3.0作为示例环境。关于引用第三方的jar包,相比较于模式二开发,除了用到了xml操作的jdom,以外就一个Google ZXing的二维码包和log4j包。如下图:

为了方便调试,建议各位先在这个环境下调通了再移植到真实项目当中去。

二、开发实战

在动手之前,我建议大家先去官方文档那好好看看那个时序图,理解了那个时序图,写代码也就不是什么难事了,当然如果看图你没办法理解,也可以结合我下面的代码来试着理解。

2.1、二维码生成

首先是二维码,二维码中的内容为链接,形式为:

weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXX&mch_id=XXXXX&product_id=XXXXXX&time_stamp=XXXXXX&nonce_str=XXXXX

具体可以参考官方文档模式一生成二维码规则。接下来我们需要将该链接生成二维码,我这里使用了Google ZXing来生成二维码。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
    1. package com.wqy;  
    2.   
    3. import java.io.IOException;  
    4. import java.io.OutputStream;  
    5. import java.util.HashMap;  
    6. import java.util.Iterator;  
    7. import java.util.Map;  
    8. import java.util.Set;  
    9. import java.util.SortedMap;  
    10. import java.util.TreeMap;  
    11.   
    12. import javax.servlet.ServletException;  
    13. import javax.servlet.annotation.WebServlet;  
    14. import javax.servlet.http.HttpServlet;  
    15. import javax.servlet.http.HttpServletRequest;  
    16. import javax.servlet.http.HttpServletResponse;  
    17.   
    18. import org.apache.log4j.Logger;  
    19.   
    20. import com.google.zxing.BarcodeFormat;  
    21. import com.google.zxing.EncodeHintType;  
    22. import com.google.zxing.MultiFormatWriter;  
    23. import com.google.zxing.WriterException;  
    24. import com.google.zxing.client.j2se.MatrixToImageWriter;  
    25. import com.google.zxing.common.BitMatrix;  
    26. import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;  
    27. import com.wqy.util.PayCommonUtil;  
    28. import com.wqy.util.PayConfigUtil;  
    29.   
    30. /** 
    31.  * Servlet implementation class Pay1 
    32.  */  
    33. @WebServlet("/Pay1")  
    34. public class Pay1 extends HttpServlet {  
    35.     private static final long serialVersionUID = 1L;  
    36.     private static Logger logger = Logger.getLogger(Pay1.class);  
    37.       
    38.     public static int defaultWidthAndHeight=200;  
    39.       
    40.     /** 
    41.      * @see HttpServlet#HttpServlet() 
    42.      */  
    43.     public Pay1() {  
    44.         super();  
    45.         // TODO Auto-generated constructor stub  
    46.     }  
    47.   
    48.     /** 
    49.      * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse 
    50.      *      response) 
    51.      */  
    52.     protected void doGet(HttpServletRequest request, HttpServletResponse response)  
    53.             throws ServletException, IOException {  
    54.           
    55.         // TODO Auto-generated method stub  
    56.         String nonce_str = PayCommonUtil.getNonce_str();  
    57.         long time_stamp = System.currentTimeMillis() / 1000;  
    58.         String product_id = "hd_goodsssss_10";  
    59.         String key = PayConfigUtil.API_KEY; // key  
    60.           
    61.         SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();  
    62.         packageParams.put("appid", PayConfigUtil.APP_ID);  
    63.         packageParams.put("mch_id", PayConfigUtil.MCH_ID);  
    64.         packageParams.put("time_stamp", String.valueOf(time_stamp));  
    65.         packageParams.put("nonce_str", nonce_str);  
    66.         packageParams.put("product_id", product_id);  
    67.         String sign = PayCommonUtil.createSign("UTF-8", packageParams,key);//MD5哈希  
    68.         packageParams.put("sign", sign);   
    69.           
    70.         //生成参数  
    71.         String str = ToUrlParams(packageParams);  
    72.         String payurl = "weixin://wxpay/bizpayurl?" + str;  
    73.         logger.info("payurl:"+payurl);  
    74.           
    75.           
    76.         //生成二维码  
    77.         Map<EncodeHintType, Object>  hints=new HashMap<EncodeHintType, Object>();  
    78.         // 指定纠错等级    
    79.         hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);    
    80.         // 指定编码格式    
    81.         hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");    
    82.         hints.put(EncodeHintType.MARGIN, 1);  
    83.         try {  
    84.             BitMatrix bitMatrix = new MultiFormatWriter().encode(payurl,BarcodeFormat.QR_CODE, defaultWidthAndHeight, defaultWidthAndHeight, hints);  
    85.             OutputStream out = response.getOutputStream();  
    86.             MatrixToImageWriter.writeToStream(bitMatrix, "png", out);//输出二维码  
    87.             out.flush();  
    88.             out.close();  
    89.               
    90.         } catch (WriterException e) {  
    91.             // TODO Auto-generated catch block  
    92.             e.printStackTrace();  
    93.         }  
    94.     }  
    95.       
    96.     public String ToUrlParams(SortedMap<Object, Object> packageParams){  
    97.         //实际可以不排序  
    98.         StringBuffer sb = new StringBuffer();    
    99.         Set es = packageParams.entrySet();    
    100.         Iterator it = es.iterator();    
    101.         while (it.hasNext()) {    
    102.             Map.Entry entry = (Map.Entry) it.next();    
    103.             String k = (String) entry.getKey();    
    104.             String v = (String) entry.getValue();    
    105.             if (null != v && !"".equals(v)) {    
    106.                 sb.append(k + "=" + v + "&");    
    107.             }    
    108.         }  
    109.           
    110.         sb.deleteCharAt(sb.length()-1);//删掉最后一个&  
    111.         return sb.toString();  
    112.     }  
    113.   
    114.     /** 
    115.      * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse 
    116.      *      response) 
    117.      */  
    118.     protected void doPost(HttpServletRequest request, HttpServletResponse response)  
    119.             throws ServletException, IOException {  
    120.         // TODO Auto-generated method stub  
    121.         doGet(request, response);  
    122.     }  
    123.   
    124. }  

2.2、扫描支付回调url接口

当客户用微信扫了上面的二位码之后,微信服务器就会访问此接口,在这里我们要完成统一下单获取交易会话标识,处理的主要流程如下:

1)、接收微信服务器发送过来的参数,对参数进行签名校验;

2)、取出参数product_id,这是二维码上唯一能够透传过来的参数,其他参数可参照官方文档模式一3.1 输入参数;

3)、根据product_id处理自己的业务,比如计算支付金额,生成订单号等;

4)、调用统一下单接口获取交易会话标识prepay_id;

    4.1)、准备好相关参数(如appid、mch_id、支付金额、订单号、商品描述等),调用微信统一下单接口(与模式二调用统一下单接口类似),留意一下这里要加上上面提到的“异步通知url”,也就是后面会说道的异步通知url接口,具体参数参考官方文档统一下单请求参数;

    4.2)、接收统一下单接口返回的参数,对参数进行验签;

    4.3)、取出参数prepay_id,这是交易会话标识,极其重要,其他参数可参考官方文档统一下单返回结果;

5)、准备好相关参数(如appid、mch_id、return_code、prepay_id等),响应最开始的支付回调(如果上面步骤如果错误,如验签失败则可以返回错误参数给微信服务器),具体参数可参照官方文档模式一3.2 输出参数。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
    1. package com.wqy;  
    2.   
    3. import java.io.BufferedOutputStream;  
    4. import java.io.BufferedReader;  
    5. import java.io.IOException;  
    6. import java.io.InputStream;  
    7. import java.io.InputStreamReader;  
    8. import java.util.SortedMap;  
    9. import java.util.TreeMap;  
    10.   
    11. import javax.servlet.ServletException;  
    12. import javax.servlet.annotation.WebServlet;  
    13. import javax.servlet.http.HttpServlet;  
    14. import javax.servlet.http.HttpServletRequest;  
    15. import javax.servlet.http.HttpServletResponse;  
    16.   
    17. import org.apache.log4j.Logger;  
    18.   
    19. import com.wqy.util.HttpUtil;  
    20. import com.wqy.util.PayCommonUtil;  
    21. import com.wqy.util.PayConfigUtil;  
    22.   
    23. /** 
    24.  * Servlet implementation class Notify1 
    25.  */  
    26. @WebServlet("/Notify1")  
    27. public class Notify1 extends HttpServlet {  
    28.     private static final long serialVersionUID = 1L;  
    29.     private static Logger logger = Logger.getLogger(Notify1.class);  
    30.   
    31.     /** 
    32.      * @see HttpServlet#HttpServlet() 
    33.      */  
    34.     public Notify1() {  
    35.         super();  
    36.         // TODO Auto-generated constructor stub  
    37.     }  
    38.   
    39.     /** 
    40.      * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse 
    41.      *      response) 
    42.      */  
    43.     protected void doGet(HttpServletRequest request, HttpServletResponse response)  
    44.             throws ServletException, IOException {  
    45.         // TODO Auto-generated method stub  
    46.   
    47.         // 读取xml  
    48.         InputStream inputStream;  
    49.         StringBuffer sb = new StringBuffer();  
    50.         inputStream = request.getInputStream();  
    51.         String s;  
    52.         BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));  
    53.         while ((s = in.readLine()) != null) {  
    54.             sb.append(s);  
    55.         }  
    56.         in.close();  
    57.         inputStream.close();  
    58.           
    59.         SortedMap<Object, Object> packageParams = PayCommonUtil.xmlConvertToMap(sb.toString());  
    60.         logger.info(packageParams);   
    61.           
    62.         // 账号信息  
    63.         String key = PayConfigUtil.API_KEY; // key  
    64.           
    65.         String resXml="";//反馈给微信服务器  
    66.         // 验签  
    67.         if (PayCommonUtil.isTenpaySign("UTF-8", packageParams, key)) {  
    68.             //appid openid mch_id is_subscribe nonce_str product_id sign  
    69.               
    70.             //统一下单  
    71.             String openid = (String)packageParams.get("openid");  
    72.             String product_id = (String)packageParams.get("product_id");  
    73.             //解析product_id,计算价格等  
    74.               
    75.             String out_trade_no = String.valueOf(System.currentTimeMillis()); // 订单号    
    76.             String order_price = "1"// 价格   注意:价格的单位是分    
    77.             String body = product_id;   // 商品名称  这里设置为product_id  
    78.             String attach = "XXX店"//附加数据  
    79.               
    80.             String nonce_str0 = PayCommonUtil.getNonce_str();  
    81.               
    82.             // 获取发起电脑 ip    
    83.             String spbill_create_ip = PayConfigUtil.CREATE_IP;      
    84.             String trade_type = "NATIVE";   
    85.               
    86.               
    87.             SortedMap<Object,Object> unifiedParams = new TreeMap<Object,Object>();    
    88.             unifiedParams.put("appid", PayConfigUtil.APP_ID); // 必须  
    89.             unifiedParams.put("mch_id", PayConfigUtil.MCH_ID); // 必须  
    90.             unifiedParams.put("out_trade_no", out_trade_no); // 必须  
    91.             unifiedParams.put("product_id", product_id);  
    92.             unifiedParams.put("body", body); // 必须  
    93.             unifiedParams.put("attach", attach);  
    94.             unifiedParams.put("total_fee", order_price);  // 必须   
    95.             unifiedParams.put("nonce_str", nonce_str0);  // 必须  
    96.             unifiedParams.put("spbill_create_ip", spbill_create_ip); // 必须   
    97.             unifiedParams.put("trade_type", trade_type); // 必须    
    98.             unifiedParams.put("openid", openid);    
    99.             unifiedParams.put("notify_url", PayConfigUtil.NOTIFY_URL);//异步通知url  
    100.               
    101.             String sign0 = PayCommonUtil.createSign("UTF-8", unifiedParams,key);    
    102.             unifiedParams.put("sign", sign0); //签名  
    103.               
    104.             String requestXML = PayCommonUtil.getRequestXml(unifiedParams);    
    105.             logger.info(requestXML);  
    106.             //统一下单接口  
    107.             String rXml = HttpUtil.postData(PayConfigUtil.UFDODER_URL, requestXML);    
    108.               
    109.             //统一下单响应  
    110.             SortedMap<Object, Object> reParams = PayCommonUtil.xmlConvertToMap(rXml);  
    111.             logger.info(reParams);   
    112.               
    113.             //验签  
    114.             if (PayCommonUtil.isTenpaySign("UTF-8", reParams, key)) {  
    115.                 // 统一下单返回的参数  
    116.                 String prepay_id = (String)reParams.get("prepay_id");//交易会话标识  2小时内有效  
    117.                   
    118.                 String nonce_str1 = PayCommonUtil.getNonce_str();  
    119.                   
    120.                 SortedMap<Object,Object> resParams = new TreeMap<Object,Object>();    
    121.                 resParams.put("return_code""SUCCESS"); // 必须  
    122.                 resParams.put("return_msg""OK");  
    123.                 resParams.put("appid", PayConfigUtil.APP_ID); // 必须  
    124.                 resParams.put("mch_id", PayConfigUtil.MCH_ID);  
    125.                 resParams.put("nonce_str", nonce_str1); // 必须  
    126.                 resParams.put("prepay_id", prepay_id); // 必须  
    127.                 resParams.put("result_code""SUCCESS"); // 必须  
    128.                 resParams.put("err_code_des""OK");  
    129.                   
    130.                 String sign1 = PayCommonUtil.createSign("UTF-8", resParams,key);    
    131.                 resParams.put("sign", sign1); //签名  
    132.                   
    133.                 resXml = PayCommonUtil.getRequestXml(resParams);  
    134.                 logger.info(resXml);  
    135.                   
    136.             }else{  
    137.                 logger.info("签名验证错误");  
    138.                 resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"    
    139.                         + "<return_msg><![CDATA[签名验证错误]]></return_msg>" + "</xml> ";   
    140.             }  
    141.               
    142.         }else{  
    143.             logger.info("签名验证错误");  
    144.             resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"    
    145.                     + "<return_msg><![CDATA[签名验证错误]]></return_msg>" + "</xml> ";   
    146.         }  
    147.   
    148.         //------------------------------    
    149.         //处理业务完毕    
    150.         //------------------------------    
    151.         BufferedOutputStream out = new BufferedOutputStream(    
    152.                 response.getOutputStream());    
    153.         out.write(resXml.getBytes());    
    154.         out.flush();    
    155.         out.close();    
    156.           
    157.     }  
    158.   
    159.     /** 
    160.      * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse 
    161.      *      response) 
    162.      */  
    163.     protected void doPost(HttpServletRequest request, HttpServletResponse response)  
    164.             throws ServletException, IOException {  
    165.         // TODO Auto-generated method stub  
    166.         doGet(request, response);  
    167.     }  
    168.   
    169. }  

至此,用户的微信单就会显示出要支付的金额及商品描述等,接下来就是等待客户完成支付。

2.3、异步通知url接口

当用户在微信上完成支付操作后,微信服务器就会异步通知这个接口,给我们发送最终的支付结果,以便我们核实订单进行发货等操作,注意这个接口和模式二的开发是一模一样的。大概流程如下:

1)、接收微信服务器发送过来的参数,对参数进行签名校验;

2)、取出参数result_code、订单号out_trade_no、订单金额total_fee及其他业务相关的参数,具体参数可参照官方文档支付结果通用通知的通知参数;

3)、处理业务,如校验订单号及订单金额、修改订单状态等;

4)、准备好相关参数(return_code和return_msg),应答微信服务器。

注意,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。 (通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒)

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
    1. package com.wqy;  
    2.   
    3. import java.io.BufferedOutputStream;  
    4. import java.io.BufferedReader;  
    5. import java.io.IOException;  
    6. import java.io.InputStream;  
    7. import java.io.InputStreamReader;  
    8. import java.util.SortedMap;  
    9.   
    10. import javax.servlet.ServletException;  
    11. import javax.servlet.annotation.WebServlet;  
    12. import javax.servlet.http.HttpServlet;  
    13. import javax.servlet.http.HttpServletRequest;  
    14. import javax.servlet.http.HttpServletResponse;  
    15.   
    16. import org.apache.log4j.Logger;  
    17.   
    18. import com.wqy.util.PayCommonUtil;  
    19. import com.wqy.util.PayConfigUtil;  
    20.   
    21. /** 
    22.  * Servlet implementation class Re_notify 
    23.  */  
    24. @WebServlet("/Re_notify")  
    25. public class Re_notify extends HttpServlet {  
    26.     private static final long serialVersionUID = 1L;  
    27.     private static Logger logger = Logger.getLogger(Re_notify.class);  
    28.   
    29.     /** 
    30.      * @see HttpServlet#HttpServlet() 
    31.      */  
    32.     public Re_notify() {  
    33.         super();  
    34.         // TODO Auto-generated constructor stub  
    35.     }  
    36.   
    37.     /** 
    38.      * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse 
    39.      *      response) 
    40.      */  
    41.     protected void doGet(HttpServletRequest request, HttpServletResponse response)  
    42.             throws ServletException, IOException {  
    43.         // TODO Auto-generated method stub  
    44.         // 读取参数  
    45.         InputStream inputStream;  
    46.         StringBuffer sb = new StringBuffer();  
    47.         inputStream = request.getInputStream();  
    48.         String s;  
    49.         BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));  
    50.         while ((s = in.readLine()) != null) {  
    51.             sb.append(s);  
    52.         }  
    53.         in.close();  
    54.         inputStream.close();  
    55.   
    56.         SortedMap<Object, Object> packageParams = PayCommonUtil.xmlConvertToMap(sb.toString());  
    57.         logger.info(packageParams);  
    58.   
    59.         // 账号信息  
    60.         String key = PayConfigUtil.API_KEY; // key  
    61.   
    62.         String resXml = ""// 反馈给微信服务器  
    63.         // 判断签名是否正确  
    64.         if (PayCommonUtil.isTenpaySign("UTF-8", packageParams, key)) {  
    65.             // ------------------------------  
    66.             // 处理业务开始  
    67.             // ------------------------------  
    68.             if ("SUCCESS".equals((String) packageParams.get("result_code"))) {  
    69.                 // 这里是支付成功  
    70.                 // 执行自己的业务逻辑  
    71.                 String mch_id = (String) packageParams.get("mch_id");  
    72.                 String openid = (String) packageParams.get("openid");  
    73.                 String is_subscribe = (String) packageParams.get("is_subscribe");  
    74.                 String out_trade_no = (String) packageParams.get("out_trade_no");  
    75.   
    76.                 String total_fee = (String) packageParams.get("total_fee");  
    77.   
    78.                 logger.info("mch_id:" + mch_id);  
    79.                 logger.info("openid:" + openid);  
    80.                 logger.info("is_subscribe:" + is_subscribe);  
    81.                 logger.info("out_trade_no:" + out_trade_no);  
    82.                 logger.info("total_fee:" + total_fee);  
    83.   
    84.                 // 执行自己的业务逻辑  
    85.   
    86.                 logger.info("支付成功");  
    87.                 // 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.  
    88.                 resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"  
    89.                         + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";  
    90.   
    91.             } else {  
    92.                 logger.info("支付失败,错误信息:" + packageParams.get("err_code"));  
    93.                 resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"  
    94.                         + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";  
    95.             }  
    96.   
    97.         } else {  
    98.             logger.info("签名验证错误");  
    99.             resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"    
    100.                     + "<return_msg><![CDATA[签名验证错误]]></return_msg>" + "</xml> ";   
    101.         }  
    102.   
    103.         // ------------------------------  
    104.         // 处理业务完毕  
    105.         // ------------------------------  
    106.         BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());  
    107.         out.write(resXml.getBytes());  
    108.         out.flush();  
    109.         out.close();  
    110.     }  
    111.   
    112.     /** 
    113.      * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse 
    114.      *      response) 
    115.      */  
    116.     protected void doPost(HttpServletRequest request, HttpServletResponse response)  
    117.             throws ServletException, IOException {  
    118.         // TODO Auto-generated method stub  
    119.         doGet(request, response);  
    120.     }  
    121.   
    122. }  

三、测试结果

3.1、生成的支付二维码链接

3.2、支付回调url接口接收到的参数

3.3、发起统一下单请求参数

3.4、统一下单返回参数

3.5、支付回调url接口最终的响应参数

转载请注明:http://blog.csdn.net/wangqiuyun/article/details/52679682

源码下载地址:https://github.com/wangqiuyun/wpay


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值