微信App支付

本文分享了微信APP支付的实战经验,详细介绍了支付流程中的关键步骤,包括如何正确设置total_fee参数,签名生成方法,以及如何处理prepayid的获取与验证。通过代码示例,深入解析了微信支付接口的调用细节。
摘要由CSDN通过智能技术生成

最近 在开发新项目的微信app支付 记录一下踩得坑

1:要明确微信支付的total_fee是“”,这个是重点
2:签名的主要三个 app_id , PARTNER_ID(商户id和商户号MCH_ID相同) 和APP_KEY(应用对应的密钥)
3:如果返回获取不到prepayid,最大可能是签名有误
4:签名不要忘记加密哦

好了 上代码
这里是pom

在这里插入图片描述

	  /**
		     * 生成预支付订单,获取prepayId
		     * @param request
		     * @param response
		     * @return
		     * @throws Exception
		     */
		    @RequestMapping("/createBond")
		    public Map<String, Object> getOrder(HttpServletRequest request, HttpServletResponse response)
		            throws Exception {    	
		    	log.info("开始微信支付保证金接口");	    
		        String token = request.getParameter("token"); 
		        String goodsId = request.getParameter("goodsId");
		        log.info("token{}:"+token);
				 String returnUrl2 = request.getParameter("returnUrl2");
		        Map<String, Object> user =SecurityUtils.getSubject().getSession()!=null?        			  (Map)SecurityUtils.getSubject().getSession().getAttribute("user"):null;   
		        String loginName = "";
		        if(user!=null){
		       	 loginName =  user.get("userName").toString();
		        }else if(token!=null&&!token.isEmpty()){
		       	 loginName = redis.getString(token) ;
		        }
		        log.info("缴纳保证金接口用户电话:{}", loginName)
		        String userHpCard = this.orderservice.findUserHpCard(loginName);
		        String userName = this.orderservice.findUserName(loginName);
		        Long uid = this.orderservice.findUid(loginName);
		        //这里是我的业务逻辑(小伙伴可根据自己的实际业务开发)
		        Map<String, Object> ret = this.orderservice.addOrder(goodsId, 1, 0, returnUrl2,loginName);
		    	
		        Map<String, Object> bondorder = (Map)ret.get("bondorder");
		        String tradeNo = bondorder.get("tradeSeqNo").toString();
		        //获取支付金额
		        String paySize = bondorder.get("pay_size").toString();
	
		        Map<String, Object> map = new HashMap<>();
		        // 获取生成预支付订单的请求类
		        PrepayIdRequestHandler prepayReqHandler = new PrepayIdRequestHandler(request, response);
		        
		        Double pay_size=Double.valueOf(paySize);
		        //将实际金额转换为以分为单位
		        Double pay_size1 = pay_size*100;
		        String paySize2 = String.valueOf(pay_size1);
		        String total_fee = paySize2.substring(0,paySize2.lastIndexOf("."));
		        
		        
		        log.info("total_fee{}===:" + paySize);
		        prepayReqHandler.setParameter("appid", ConstantUtil.APP_ID);
		        prepayReqHandler.setParameter("body", ConstantUtil.BODY);
		        prepayReqHandler.setParameter("mch_id", ConstantUtil.MCH_ID);
		        String nonce_str = WXUtil.getNonceStr();
		        prepayReqHandler.setParameter("nonce_str", nonce_str);
		        prepayReqHandler.setParameter("notify_url", ConstantUtil.NOTIFY_URL);
		        prepayReqHandler.setParameter("out_trade_no", tradeNo);
		        prepayReqHandler.setParameter("spbill_create_ip", request.getRemoteAddr());
		        log.info("request.getRemoteAddr(){}====="+request.getRemoteAddr());
		        String timestamp = WXUtil.getTimeStamp();
		        prepayReqHandler.setParameter("time_start", timestamp);
		       
		        prepayReqHandler.setParameter("total_fee", total_fee);
		        prepayReqHandler.setParameter("trade_type",ConstantUtil.TRADE_TYPE);
		        /**
		         * 注意签名(sign)的生成方式,具体见官方文档(传参都要参与生成签名,且参数名按照字典序排序,最后接上APP_KEY,转化成大写)
		         */
		        prepayReqHandler.setParameter("sign", prepayReqHandler.createMD5Sign());
		        log.info("=========:"+prepayReqHandler.createMD5Sign());
		        prepayReqHandler.setGateUrl(ConstantUtil.GATEURL);
		        
		        String prepayid = prepayReqHandler.sendPrepay();        
		        log.info("prepayid{}:"+prepayid);
		        
		        String phone = loginName;
		        // 若获取prepayid成功,将相关信息返回客户端
		        if (prepayid != null && !prepayid.equals("")) {	           
		           // String totalFee = String.valueOf(pay_size);
		        	String totalFee = paySize;
		            String signs = "appid=" + ConstantUtil.APP_ID + "&noncestr=" + nonce_str + "&package=Sign=WXPay&partnerid="
		                    + ConstantUtil.PARTNER_ID + "&prepayid=" + prepayid + "&timestamp=" + timestamp + "&key="
		                    + ConstantUtil.APP_KEY;
		            map.put("code", 0);
		            map.put("info", "success");
		            map.put("prepayid", prepayid);
		            /**
		             * 签名方式与上面类似
		             */
		            map.put("sign", MD5Util.MD5Encode(signs, "utf8").toUpperCase());
		            map.put("appid", ConstantUtil.APP_ID);
		            map.put("timestamp", timestamp);  //等于请求prepayId时的time_start
		            map.put("noncestr", nonce_str);   //与请求prepayId时值一致
		            map.put("package", "Sign=WXPay");  //固定常量
		            map.put("partnerid", ConstantUtil.PARTNER_ID);
		        } else {
		            map.put("code", 1);
		            map.put("info", "获取prepayid失败");
		        }
		        return map;
		    }
	
 /**
	     * 接收微信支付成功通知
	     * @param request
	     * @param response
	     * @throws IOException
	     */
	    @RequestMapping(value = "/notify")
	    public void getnotify(HttpServletRequest request, HttpServletResponse response)
	            throws IOException {

        log.info("微信异步回调信息到了");
        PrintWriter writer = response.getWriter();
        InputStream inStream = request.getInputStream();
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        outSteam.close();
        inStream.close();
        String result = new String(outSteam.toByteArray(), "utf-8");
        log.info("微信支付通知结果{}:"+result);
        Map<String, String> map = null;
        try {
            /**
             * 解析微信通知返回的信息
             */
            map = XMLUtil.doXMLParse(result);
        } catch (JDOMException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("=========:"+result);
        // 若支付成功,则告知微信服务器收到通知
        if (map.get("return_code").equals("SUCCESS")) {
            if (map.get("result_code").equals("SUCCESS")) {
                log.info("充值成功!");
               *********业务开始********
               ********业务结束*********
                 String notifyStr = XMLUtil.setXML("SUCCESS", "");
                 writer.write(notifyStr);
                 writer.flush();	                
            }
        }
    }
}

ConstantUtil

public class ConstantUtil {
public static final  String API_KEY = "82f4184ac27213b19688910b6453de33";// API密钥
public static final  String SIGN_TYPE = "MD5";// 签名加密方式
public static final  String TRADE_TYPE = "APP";// 支付类型
public static final String input_charset = "UTF-8";
/**
 * 微信开发平台应用ID
 */
public static final String APP_ID = "";
/**
 * 应用对应的凭证
 */
public static final String APP_SECRET = "";
/**
 * 应用对应的密钥
 */
public static final String APP_KEY = "";
/**
 * 微信支付商户号
 */
public static final String MCH_ID = "";
/**
 * 商品描述
 */
public static final String BODY="";
/**
 * 商户号对应的密钥
 */
public static final String PARTNER_key="";

/**
 * 商户id
 */
public static final String PARTNER_ID="";
/**
 * 常量固定值
 */
public static final String GRANT_TYPE="client_credential";
/**
 * 获取预支付id的接口url
 */
public static String GATEURL="https://api.mch.weixin.qq.com/pay/unifiedorder";
/**
 * 微信服务器回调通知url
 */
public static String NOTIFY_URL="";
}

PrepayIdRequestHandler

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class PrepayIdRequestHandler extends RequestHandler {

public PrepayIdRequestHandler(HttpServletRequest request,
                              HttpServletResponse response) {
    super(request, response);
}

public String createMD5Sign() {
    StringBuffer sb = new StringBuffer();
    Set es = super.getAllParameters().entrySet();
    Iterator it = es.iterator();
    while (it.hasNext()) {
        Map.Entry entry = (Map.Entry) it.next();
        String k = (String) entry.getKey();
        String v = (String) entry.getValue();
        sb.append(k + "=" + v + "&");
    }
    String params=sb.append("key="+ConstantUtil.APP_KEY).substring(0);
    String sign = MD5Util.MD5Encode(params, "utf8");
    return sign.toUpperCase();
}

// 提交预支付
public String sendPrepay() throws Exception {
    String prepayid = "";
    Set es=super.getAllParameters().entrySet();
    Iterator it=es.iterator();
    StringBuffer sb = new StringBuffer("<xml>");
    while(it.hasNext()){
        Map.Entry entry = (Map.Entry) it.next();
        String k = (String) entry.getKey();
        String v = (String) entry.getValue();
        sb.append("<"+k+">"+v+"</"+k+">");
    }
    sb.append("</xml>");
    String params=sb.substring(0);
    System.out.println("请求参数:"+params);
    String requestUrl = super.getGateUrl();
    System.out.println("请求url:"+requestUrl);
    TenpayHttpClient httpClient = new TenpayHttpClient();
    httpClient.setReqContent(requestUrl);
    String resContent = "";
    if (httpClient.callHttpPost(requestUrl, params)) {
        resContent = httpClient.getResContent();
        System.out.println("获取prepayid的返回值:"+resContent);
        Map<String,String> map=XMLUtil.doXMLParse(resContent);
        if(map.containsKey("prepay_id"))
            prepayid=map.get("prepay_id");
    }
    return prepayid;
}

}

RequestHandler

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;

/**
 * 请求处理类
 * 请求处理类继承此类,重写createSign方法即可。
 *
 */
public class RequestHandler {

    /** 网关url地址 */
    private String gateUrl;

    /** 密钥 */
    private String key;

    /** 请求的参数 */
    private SortedMap parameters;

    protected HttpServletRequest request;

    protected HttpServletResponse response;

    /**
     * 构造函数
     * @param request
     * @param response
     */
    public RequestHandler(HttpServletRequest request, HttpServletResponse response) {
        this.request = request;
        this.response = response;

        this.gateUrl = "https://gw.tenpay.com/gateway/pay.htm";
        this.key = "";
        this.parameters = new TreeMap();
    }

    /**
     *初始化函数。
     */
    public void init() {
        //nothing to do
    }

    /**
     *获取入口地址,不包含参数值
     */
    public String getGateUrl() {
        return gateUrl;
    }

    /**
     *设置入口地址,不包含参数值
     */
    public void setGateUrl(String gateUrl) {
        this.gateUrl = gateUrl;
    }

    /**
     *获取密钥
     */
    public String getKey() {
        return key;
    }

    /**
     *设置密钥
     */
    public void setKey(String key) {
        this.key = key;
    }

    /**
     * 获取参数值
     * @param parameter 参数名称
     * @return String
     */
    public String getParameter(String parameter) {
        String s = (String)this.parameters.get(parameter);
        return (null == s) ? "" : s;
    }

    /**
     * 设置参数值
     * @param parameter 参数名称
     * @param parameterValue 参数值
     */
    public void setParameter(String parameter, Object parameterValue) {
        String v = "";
        if(null != parameterValue) {
            if(parameterValue instanceof String)
                v = ((String) parameterValue).trim();
        }
        this.parameters.put(parameter, v);
    }

    /**
     * 返回所有的参数
     * @return SortedMap
     */
    public SortedMap getAllParameters() {
        return this.parameters;
    }

    /**
     * 获取带参数的请求URL
     * @return String
     * @throws UnsupportedEncodingException
     */
    public String getRequestURL() throws UnsupportedEncodingException {

        this.createSign();

        StringBuffer sb = new StringBuffer();
        String enc = TenpayUtil.getCharacterEncoding(this.request, this.response);
        Set es = this.parameters.entrySet();
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            String v = (String)entry.getValue();

            if(!"spbill_create_ip".equals(k)) {
                sb.append(k + "=" + URLEncoder.encode(v, enc) + "&");
            } else {
                sb.append(k + "=" + v.replace("\\.", "%2E") + "&");
            }
        }

        //去掉最后一个&
        String reqPars = sb.substring(0, sb.lastIndexOf("&"));

        return this.getGateUrl() + "?" + reqPars;

    }

    public void doSend() throws UnsupportedEncodingException, IOException {
        this.response.sendRedirect(this.getRequestURL());
    }

    /**
     * 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
     */
    protected void createSign() {
        StringBuffer sb = new StringBuffer();
        Set es = this.parameters.entrySet();
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            String v = (String)entry.getValue();
            if(null != v && !"".equals(v)
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + this.getKey());
        String enc = TenpayUtil.getCharacterEncoding(this.request, this.response);
        String sign = MD5Util.MD5Encode(sb.toString(), enc).toUpperCase();

        this.setParameter("sign", sign);

    }

    protected HttpServletRequest getHttpServletRequest() {
        return this.request;
    }

    protected HttpServletResponse getHttpServletResponse() {
        return this.response;
    }
}

MD5Util

import java.security.MessageDigest;

public class MD5Util {
    private static String byteArrayToHexString(byte b[]) {
        StringBuffer resultSb = new StringBuffer();
        for (int i = 0; i < b.length; i++)
            resultSb.append(byteToHexString(b[i]));

    return resultSb.toString();
}

private static String byteToHexString(byte b) {
    int n = b;
    if (n < 0)
        n += 256;
    int d1 = n / 16;
    int d2 = n % 16;
    return hexDigits[d1] + hexDigits[d2];
}

public static String MD5Encode(String origin, String charsetname) {
    String resultString = null;
    try {
        resultString = new String(origin);
        MessageDigest md = MessageDigest.getInstance("MD5");
        if (charsetname == null || "".equals(charsetname))
            resultString = byteArrayToHexString(md.digest(resultString
                    .getBytes()));
        else
            resultString = byteArrayToHexString(md.digest(resultString
                    .getBytes(charsetname)));
    } catch (Exception exception) {
    }
    return resultString;
}

private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
        "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };

}

TenpayHttpClient ;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;

public class TenpayHttpClient {

/** 请求内容,无论post和get,都用get方式提供 */
private String reqContent;

/** 应答内容 */
private String resContent;

/** 请求方法 */
private String method;

/** 错误信息 */
private String errInfo;

/** 超时时间,以秒为单位 */
private int timeOut;

/** http应答编码 */
private int responseCode;

/** 字符编码 */
private String charset;

private InputStream inputStream;

public TenpayHttpClient() {
    this.reqContent = "";
    this.resContent = "";
    this.method = "POST";
    this.errInfo = "";
    this.timeOut = 30;//30秒

    this.responseCode = 0;
    this.charset = "utf8";

    this.inputStream = null;
}

/**
 * 设置请求内容
 * @param reqContent 表求内容
 */
public void setReqContent(String reqContent) {
    this.reqContent = reqContent;
}

/**
 * 获取结果内容
 * @return String
 * @throws IOException
 */
public String getResContent() {
    try {
        this.doResponse();
    } catch (IOException e) {
        this.errInfo = e.getMessage();
        //return "";
    }

    return this.resContent;
}

/**
 * 设置请求方法post或者get
 * @param method 请求方法post/get
 */
public void setMethod(String method) {
    this.method = method;
}

/**
 * 获取错误信息
 * @return String
 */
public String getErrInfo() {
    return this.errInfo;
}

/**
 * 设置超时时间,以秒为单位
 * @param timeOut 超时时间,以秒为单位
 */
public void setTimeOut(int timeOut) {
    this.timeOut = timeOut;
}

/**
 * 获取http状态码
 * @return int
 */
public int getResponseCode() {
    return this.responseCode;
}

protected void callHttp() throws IOException {

    if("POST".equals(this.method.toUpperCase())) {
        String url = HttpClientUtil.getURL(this.reqContent);
        String queryString = HttpClientUtil.getQueryString(this.reqContent);
        byte[] postData = queryString.getBytes(this.charset);
        this.httpPostMethod(url, postData);

        return ;
    }

    this.httpGetMethod(this.reqContent);

}

public boolean callHttpPost(String url, String postdata) {
    boolean flag = false;
    byte[] postData;
    try {
        postData = postdata.getBytes(this.charset);
        this.httpPostMethod(url, postData);
        flag = true;
    } catch (IOException e1) {
        e1.printStackTrace();
    }
    return flag;
}

/**
 * 以http post方式通信
 * @param url
 * @param postData
 * @throws IOException
 */
protected void httpPostMethod(String url, byte[] postData)
        throws IOException {

    HttpURLConnection conn = HttpClientUtil.getHttpURLConnection(url);

    this.doPost(conn, postData);
}

/**
 * 以http get方式通信
 *
 * @param url
 * @throws IOException
 */
protected void httpGetMethod(String url) throws IOException {

    HttpURLConnection httpConnection =
            HttpClientUtil.getHttpURLConnection(url);

    this.setHttpRequest(httpConnection);

    httpConnection.setRequestMethod("GET");

    this.responseCode = httpConnection.getResponseCode();

    this.inputStream = httpConnection.getInputStream();

}

/**
 * 以https get方式通信
 * @param url
 * @param sslContext
 * @throws IOException
 */
protected void httpsGetMethod(String url, SSLContext sslContext)
        throws IOException {

    SSLSocketFactory sf = sslContext.getSocketFactory();

    HttpsURLConnection conn = HttpClientUtil.getHttpsURLConnection(url);

    conn.setSSLSocketFactory(sf);

    this.doGet(conn);

}

protected void httpsPostMethod(String url, byte[] postData,
                               SSLContext sslContext) throws IOException {

    SSLSocketFactory sf = sslContext.getSocketFactory();

    HttpsURLConnection conn = HttpClientUtil.getHttpsURLConnection(url);

    conn.setSSLSocketFactory(sf);

    this.doPost(conn, postData);

}

/**
 * 设置http请求默认属性
 * @param httpConnection
 */
protected void setHttpRequest(HttpURLConnection httpConnection) {

    //设置连接超时时间
    httpConnection.setConnectTimeout(this.timeOut * 1000);


    //不使用缓存
    httpConnection.setUseCaches(false);

    //允许输入输出
    httpConnection.setDoInput(true);
    httpConnection.setDoOutput(true);

}

/**
 * 处理应答
 * @throws IOException
 */
protected void doResponse() throws IOException {

    if(null == this.inputStream) {
        return;
    }

    //获取应答内容
    this.resContent=HttpClientUtil.InputStreamTOString(this.inputStream,this.charset);

    //关闭输入流
    this.inputStream.close();

}

/**
 * post方式处理
 * @param conn
 * @param postData
 * @throws IOException
 */
protected void doPost(HttpURLConnection conn, byte[] postData)
        throws IOException {

    // 以post方式通信
    conn.setRequestMethod("POST");

    // 设置请求默认属性
    this.setHttpRequest(conn);

    // Content-Type
    conn.setRequestProperty("Content-Type",
            "application/x-www-form-urlencoded");

    BufferedOutputStream out = new BufferedOutputStream(conn
            .getOutputStream());

    final int len = 1024; // 1KB
    HttpClientUtil.doOutput(out, postData, len);

    // 关闭流
    out.close();

    // 获取响应返回状态码
    this.responseCode = conn.getResponseCode();

    // 获取应答输入流
    this.inputStream = conn.getInputStream();

}

/**
 * get方式处理
 * @param conn
 * @throws IOException
 */
protected void doGet(HttpURLConnection conn) throws IOException {

    //以GET方式通信
    conn.setRequestMethod("GET");

    //设置请求默认属性
    this.setHttpRequest(conn);

    //获取响应返回状态码
    this.responseCode = conn.getResponseCode();

    //获取应答输入流
    this.inputStream = conn.getInputStream();
}
}

XMLUtil

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import java.io.ByteArrayInputStream;

public class XMLUtil {
    /**
     * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
     * @param strxml
     * @return
     * @throws JDOMException
     * @throws IOException
     */
    public static Map doXMLParse(String strxml) throws JDOMException, IOException {
        strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
        if(null == strxml || "".equals(strxml)) {
            return null;
        }

    Map m = new HashMap();

    InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
    SAXBuilder builder = new SAXBuilder();
    Document doc = builder.build(in);
    Element root = doc.getRootElement();
    List list = root.getChildren();
    Iterator it = list.iterator();
    while(it.hasNext()) {
        Element e = (Element) it.next();
        String k = e.getName();
        String v = "";
        List children = e.getChildren();
        if(children.isEmpty()) {
            v = e.getTextNormalize();
        } else {
            v = XMLUtil.getChildrenText(children);
        }

        m.put(k, v);
    }

    //关闭流
    in.close();

    return m;
}

/**
 * 获取子结点的xml
 * @param children
 * @return String
 */
public static String getChildrenText(List children) {
    StringBuffer sb = new StringBuffer();
    if(!children.isEmpty()) {
        Iterator it = children.iterator();
        while(it.hasNext()) {
            Element e = (Element) it.next();
            String name = e.getName();
            String value = e.getTextNormalize();
            List list = e.getChildren();
            sb.append("<" + name + ">");
            if(!list.isEmpty()) {
                sb.append(XMLUtil.getChildrenText(list));
            }
            sb.append(value);
            sb.append("</" + name + ">");
        }
    }

    return sb.toString();
}

/**
 * 获取xml编码字符集
 * @param strxml
 * @return
 * @throws IOException
 * @throws JDOMException
 */
public static String getXMLEncoding(String strxml) throws JDOMException, IOException {
    InputStream in = HttpClientUtil.String2Inputstream(strxml);
    SAXBuilder builder = new SAXBuilder();
    Document doc = builder.build(in);
    in.close();
    return (String)doc.getProperty("encoding");
}

/**
 * 支付成功,返回微信那服务器
 * @param return_code
 * @param return_msg
 * @return
 */
public static String setXML(String return_code, String return_msg) {
    return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>";
}

public static String createXML(Map<String,Object> map){
    Set<Entry<String,Object>> set=map.entrySet();
    set.iterator();
    return null;
}

}

各位小伙伴 如果有问题 请指教啊 多多留言 稍后会更新微信的h5支付和jsapi支付

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值