app集成微信支付服务器端开发(java)

一、微信支付太坑爹,废话不说了,下面是我的服务端微信支付开发过程和代码记录

二、首先去微信申请账户,这里有两个平台 

1、微信公众平台
2、微信开放平台(https://open.weixin.qq.com)这里选择第二个

三、账户开通、开发者认证之后就可以进行微信支付开发了

1、微信统一下单接口调用获取预支付id
/**
	 * 获取微信支付所需信息(统一下单接口调用)
	 * @param backURL
	 * @param mDealerOrderEntity
	 * @param mCarInfoEntity
	 * @return
	 * @throws JSONException 
	 * @throws IOException 
	 * @throws JDOMException 
	 */
	public Map<String, String> getWechatOrderInfo(String notifyUrl, MDealerOrderEntity mDealerOrderEntity, String body, HttpServletRequest request, HttpServletResponse response) throws Exception {
		Map<String, String> resultMap = new HashMap<String, String>();
		//生成payPreId
		Map<String, String> payPreIdMap = WechatUtil.getPayPreId(mDealerOrderEntity.getGoodorderno(), body, notifyUrl, request.getRemoteAddr(), String.valueOf((int)(mDealerOrderEntity.getMoney()*100)));
		String prePayId = payPreIdMap.get("prepay_id");
		if(StringUtils.isNotEmpty(prePayId)) {
			//生成调用微信APP参数
			resultMap =  WechatUtil.genPayReq(prePayId);
		}
		return resultMap;
	}

此方法返回的数据如下

{
        "appid": "123132131",
        "noncestr": "416e5cf0acb7e553a880b7647903da6e",
        "packageValue ": "Sign=WXPay",
        "partnerid ": "1276000000",
        "prepayid ": "wx2015101611341514a3cbbbf90572184370",
        "timestamp ": "1444966497",
        "sign": "1DD72B07607B0B41D2827954150D89E9" 
    }
2、服务器端接受微信支付结果通知
/**
	 * 处理微信支付通知
	 * @param request
	 * @return
	 * @throws Exception
	 */
	public String saveWechatNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
		Map<String, String> noticeMap = XMLUtil.parseXml(request);
		String transactionId = noticeMap.get("transaction_id");
		MWechatInfoEntity wechatInfoEntity = this.findEntityByProperty(MWechatInfoEntity.class, "transactionId", transactionId);
		//如果wechatInfoEntity存在,说明请求已经处理过,直接返回
		if(wechatInfoEntity != null) {
			return "SUCCESS";
		}
		String sign = noticeMap.get("sign");
		noticeMap.remove("sign");
		// 验签通过
		if (WechatUtil.getSignVeryfy(noticeMap, sign)) {
			// 通信成功此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断
			if ("SUCCESS".equals(noticeMap.get("return_code"))) {
				// 交易成功
				if ("SUCCESS".equals(noticeMap.get("result_code"))) {
					// 商户订单号
					String goodorderno = noticeMap.get("out_trade_no");
					MDealerOrderEntity mDealerOrderEntity = this.findEntityByProperty(MDealerOrderEntity.class, "goodorderno", goodorderno);
					MCarInfoEntity mCarInfoEntity = this.get(MCarInfoEntity.class, mDealerOrderEntity.getCarid());
					// 订单更新时间
					mDealerOrderEntity.setUpdatetime(new Date());
					// ------------------------------
					// 处理业务开始
					// ------------------------------
					// 这里写自己业务相关
					// ------------------------------
					// 处理业务完毕
					// ------------------------------
					noticeMap.put("sign", sign);
					this.common99Service.saveWechatInfo(noticeMap, mDealerOrderEntity.getId());
				} else {
					// 错误时,返回结果未签名,记录retcode、retmsg看失败详情。
					System.out.println("查询验证签名失败或业务错误");
					System.out.println("retcode:" + noticeMap.get("retcode") + " retmsg:" + noticeMap.get("retmsg"));
				}
				return "SUCCESS";
			} else {
				System.out.println("后台调用通信失败");
			}
			return "SUCCESS";
		} else {
			System.out.println("通知签名验证失败");
		}
		return null;
	}
3、上面代码用到的工具方法都在WechatUtil.java工具类中
package com.jim.iweb.haocheok.tenpay.util;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.commons.httpclient.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.jdom2.JDOMException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class WechatUtil {

	private static Logger logger = LoggerFactory.getLogger(WechatUtil.class);
	public static final String TAG = "Wechat.Util";
	private static final int timeout = 5000;

	public static byte[] httpPost(String url, String entity) throws URISyntaxException, IOException {
		if (url == null || url.length() == 0) {
			logger.info(TAG, "httpPost, url is null");
			return null;
		}
		CloseableHttpClient httpClient = HttpClients.createDefault();
		URIBuilder uriBuilder = new URIBuilder(url);
		HttpPost httpPost = new HttpPost(uriBuilder.build());
		RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout).setConnectionRequestTimeout(timeout).setConnectTimeout(timeout).build();
		httpPost.setConfig(requestConfig);
		// 避免汉字乱码导致请求失败,
		httpPost.setEntity(new StringEntity(entity, "UTF-8"));
		CloseableHttpResponse resp = null;
		try {
			resp = httpClient.execute(httpPost);
			if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
				logger.info(TAG, "httpGet fail, status code = " + resp.getStatusLine().getStatusCode());
				return null;
			}
			return EntityUtils.toByteArray(resp.getEntity());
		} catch (Exception e) {
			logger.info(TAG, "httpPost exception, e = " + e.getMessage());
			e.printStackTrace();
			return null;
		} finally {
			if (httpClient != null) {
				httpClient.close();
			}
			if (resp != null) {
				resp.close();
			}
		}
	}

	/**
	 * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
	 * 
	 * @param params
	 *            需要排序并参与字符拼接的参数组
	 * @return 拼接后字符串
	 */
	public static String createLinkString(Map<String, String> params) {

		List<String> keys = new ArrayList<String>(params.keySet());
		Collections.sort(keys);

		String prestr = "";

		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			String value = params.get(key);

			if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
				prestr = prestr + key + "=" + value;
			} else {
				prestr = prestr + key + "=" + value + "&";
			}
		}

		return prestr;
	}

	/**
	 * 根据反馈回来的信息,生成签名结果
	 * 
	 * @param Params
	 *            通知返回来的参数数组
	 * @param sign
	 *            比对的签名结果
	 * @return 生成的签名结果
	 */
	public static boolean getSignVeryfy(Map<String, String> Params, String sign) {
		// 过滤空值、sign与sign_type参数
		// Map<String, String> sParaNew = AlipayCore.paraFilter(Params);
		// 获取待签名字符串
		String preSignStr = createLinkString(Params);
		preSignStr += "&key=" + ConstantUtil.API_KEY;
		// 获得签名验证结果
		String resultSign = MD5.getMessageDigest(preSignStr.getBytes()).toUpperCase();
		// String resultSign = MD5Util.MD5Encode(preSignStr.toString(), "UTF-8").toLowerCase();
		if (sign.equals(resultSign)) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 装配xml,生成请求prePayId所需参数
	 * 
	 * @param params
	 * @return
	 */
	public static String toXml(List<NameValuePair> params) {
		StringBuilder sb = new StringBuilder();
		sb.append("<xml>");
		for (int i = 0; i < params.size(); i++) {
			sb.append("<" + params.get(i).getName() + ">");
			sb.append(params.get(i).getValue());
			sb.append("</" + params.get(i).getName() + ">");
		}
		sb.append("</xml>");
		return sb.toString();
	}

	/**
	 * 生成签名
	 */
	public static String genPackageSign(List<NameValuePair> params) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < params.size(); i++) {
			sb.append(params.get(i).getName());
			sb.append('=');
			sb.append(params.get(i).getValue());
			sb.append('&');
		}
		sb.append("key=");
		sb.append(ConstantUtil.API_KEY);
		String packageSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
		return packageSign;
	}

	/**
	 * 
	 * @param goodOrderNo
	 * @param body
	 * @param noticeUrl
	 * @param ip
	 * @param totalFee
	 * @return
	 */
	public static String genProductArgs(String goodOrderNo, String body, String noticeUrl, String ip, String totalFee) {
		StringBuffer xml = new StringBuffer();
		try {
			String nonceStr = getNonceStr();
			xml.append("</xml>");
			List<NameValuePair> packageParams = new LinkedList<NameValuePair>();
			packageParams.add(new BasicNameValuePair("appid", ConstantUtil.APP_ID));
			packageParams.add(new BasicNameValuePair("body", body));
			packageParams.add(new BasicNameValuePair("mch_id", ConstantUtil.MCH_ID));
			packageParams.add(new BasicNameValuePair("nonce_str", nonceStr));
			packageParams.add(new BasicNameValuePair("notify_url", noticeUrl));
			packageParams.add(new BasicNameValuePair("out_trade_no", goodOrderNo));
			packageParams.add(new BasicNameValuePair("spbill_create_ip", ip));
			packageParams.add(new BasicNameValuePair("total_fee", totalFee));
			packageParams.add(new BasicNameValuePair("trade_type", "APP"));
			String sign = genPackageSign(packageParams);
			packageParams.add(new BasicNameValuePair("sign", sign));
			String xmlstring = toXml(packageParams);
			return xmlstring;
		} catch (Exception e) {
			logger.info("genProductArgs fail, ex = " + e.getMessage());
			return null;
		}
	}

	/**
	 * 生成app支付签名
	 * 
	 * @param params
	 * @return
	 */
	public static String genAppSign(List<NameValuePair> params) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < params.size(); i++) {
			sb.append(params.get(i).getName());
			sb.append('=');
			sb.append(params.get(i).getValue());
			sb.append('&');
		}
		sb.append("key=");
		sb.append(ConstantUtil.API_KEY);
		String appSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
		logger.info("orion", appSign);
		return appSign;
	}

	/**
	 * 生成调用微信app支付所需参数
	 * 
	 * @param prepayId
	 * @return
	 */
	public static Map<String, String> genPayReq(String prepayId) {
		Map<String, String> resultMap = new HashMap<String, String>();
		String timeStamp = getTimeStamp();
		String nonceStr = getNonceStr();
		List<NameValuePair> signParams = new LinkedList<NameValuePair>();
		signParams.add(new BasicNameValuePair("appid", ConstantUtil.APP_ID));
		signParams.add(new BasicNameValuePair("noncestr", nonceStr));
		signParams.add(new BasicNameValuePair("package", "Sign=WXPay"));
		signParams.add(new BasicNameValuePair("partnerid", ConstantUtil.MCH_ID));
		signParams.add(new BasicNameValuePair("prepayid", prepayId));
		signParams.add(new BasicNameValuePair("timestamp", timeStamp));
		String sign = genAppSign(signParams);
		resultMap.put("appid", ConstantUtil.APP_ID);
		resultMap.put("noncestr", nonceStr);
		resultMap.put("packageValue", "Sign=WXPay");
		resultMap.put("partnerid", ConstantUtil.MCH_ID);
		resultMap.put("prepayid", prepayId);
		resultMap.put("timestamp", timeStamp);
		resultMap.put("sign", sign);
		return resultMap;
	}

	/**
	 * 微信支付生成预支付订单
	 * 
	 * @throws IOException
	 * @throws JDOMException
	 */
	public static Map<String, String> getPayPreId(String goodOrderNo, String body, String noticeUrl, String ip, String totalFee) throws Exception {
		String paramsXml = genProductArgs(goodOrderNo, body, noticeUrl, ip, totalFee);
		logger.info("orion", paramsXml);
		byte[] buf = WechatUtil.httpPost(ConstantUtil.URL, paramsXml);
		String contentXml = new String(buf);
		Map<String, String> resultMap = XMLUtil.doXMLParse(contentXml);
		return resultMap;
	}

	public static String getNonceStr() {
		Random random = new Random();
		return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
	}

	public static String getTimeStamp() {
		return String.valueOf(System.currentTimeMillis() / 1000);
	}
}
4、下面是用到的配置类
package com.jim.iweb.haocheok.tenpay.util;

public class ConstantUtil {
	/**
	 * 商家可以考虑读取配置文件
	 */
	
	//初始化
	public static String APP_ID = "wxsdfsdfsf5fdbc";//微信开发平台应用id
	public static String APP_SECRET = "aab95csdfsdfsffdcsdfsfs0df34";//应用对应的凭证
	//商户号
	public static String MCH_ID = "1233312201";
	public static String PARTNER = "1233312201";//财付通商户号
	public static String API_KEY = "KgjyjirmjajdfjsdjfsjffVpT6RMbrB";
	public static String PARTNER_KEY = "KgjyjirmjajdfjsdjfsjffVpT6RMbrB";//商户号对应的密钥
	public static String URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";//获取预支付id的接口url
}
5、xml 解析工具类
package com.jim.iweb.haocheok.tenpay.util;

import java.io.ByteArrayInputStream;
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 javax.servlet.http.HttpServletRequest;

import org.dom4j.io.SAXReader;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

/**
 * xml工具类
 * 
 * @author miklchen
 *
 */
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();
	}

	/**
	 * 将requestxml通知结果转出啊成map
	 * @param request
	 * @return
	 * @throws Exception
	 */
	public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
		// 解析结果存储在HashMap
		Map<String, String> map = new HashMap<String, String>();
		InputStream inputStream = request.getInputStream();
		// 读取输入流
		SAXReader reader = new SAXReader();
		org.dom4j.Document document = reader.read(inputStream);
		// 得到xml根元素
		org.dom4j.Element root = document.getRootElement();
		// 得到根元素的所有子节点
		List<org.dom4j.Element> elementList = root.elements();
		// 遍历所有子节点
		for (org.dom4j.Element e : elementList)
			map.put(e.getName(), e.getText());
		// 释放资源
		inputStream.close();
		inputStream = null;
		return map;
	}

}
6、MWechatInfoEntity类


import java.io.Serializable;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * The persistent class for the m_wechat_info database table.
 * 
 */
@Entity
@Table(name = "m_wechat_info")
public class MWechatInfoEntity implements Serializable {
	private static final long serialVersionUID = 1L;
	private Integer id;
	//微信支付分配的终端设备号
	private String deviceInfo;
	//随机字符串,不长于32位
	private String nonceStr;
	//SUCCESS/FAIL
	private String resultCode; 
	//错误返回的信息描述 
	private String errCode;
	//错误返回的信息描述
	private String errCodeDes;
	//用户是否关注公众账号,Y-关注,N-未关注,仅在公众账号类型支付有效
	private String isSubscribe;
	//交易类型 (JSAPI、NATIVE、APP) 
	private String tradeType;
	//银行类型,采用字符串类型的银行标识
	private String bankType;
	// 	订单总金额,单位为分
	private Integer totalFee;
	//货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
	private String feeType;
	//现金支付金额订单现金支付金额,详见支付金额
	private Integer cashFee;
	//货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
	private String cashFeeType;
	//代金券或立减优惠金额<=订单总金额,订单总金额-代金券或立减优惠金额=现金支付金额,详见支付金额
	private Integer couponFee;
	//代金券或立减优惠使用数量
	private Integer couponCount;
	//代金券或立减优惠ID,$n为下标,从0开始编号
	private String couponId;
	private String sign;
//	private String signType;
	//微信支付订单号
	private String transactionId;
	//商户订单号
	private String outTradeNo;
	//商家数据包
	private String attach;
	//支付完成时间
	private Date timeEnd;
	private Date createTime;
	private Date updateTime;
	private Integer dealerOrderId;
	

	public MWechatInfoEntity() {
	}

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name ="id",nullable=false, unique=true)
	public Integer getId() {
		return id;
	}


	public void setId(Integer id) {
		this.id = id;
	}

	@Column(name ="device_info",nullable=true)
	public String getDeviceInfo() {
		return deviceInfo;
	}


	public void setDeviceInfo(String deviceInfo) {
		this.deviceInfo = deviceInfo;
	}

	@Column(name ="nonce_str",nullable=true)
	public String getNonceStr() {
		return nonceStr;
	}


	public void setNonceStr(String nonceStr) {
		this.nonceStr = nonceStr;
	}

	@Column(name ="result_code",nullable=true)
	public String getResultCode() {
		return resultCode;
	}


	public void setResultCode(String resultCode) {
		this.resultCode = resultCode;
	}

	@Column(name ="err_code",nullable=true)
	public String getErrCode() {
		return errCode;
	}


	public void setErrCode(String errCode) {
		this.errCode = errCode;
	}

	@Column(name ="err_code_des",nullable=true)
	public String getErrCodeDes() {
		return errCodeDes;
	}


	public void setErrCodeDes(String errCodeDes) {
		this.errCodeDes = errCodeDes;
	}

	@Column(name ="is_subscribe",nullable=true)
	public String getIsSubscribe() {
		return isSubscribe;
	}


	public void setIsSubscribe(String isSubscribe) {
		this.isSubscribe = isSubscribe;
	}

	@Column(name ="trade_type",nullable=true)
	public String getTradeType() {
		return tradeType;
	}


	public void setTradeType(String tradeType) {
		this.tradeType = tradeType;
	}

	@Column(name ="bank_type",nullable=true)
	public String getBankType() {
		return bankType;
	}


	public void setBankType(String bankType) {
		this.bankType = bankType;
	}

	@Column(name ="total_fee",nullable=true)
	public Integer getTotalFee() {
		return totalFee;
	}


	public void setTotalFee(Integer totalFee) {
		this.totalFee = totalFee;
	}

	@Column(name ="fee_type",nullable=true)
	public String getFeeType() {
		return feeType;
	}


	public void setFeeType(String feeType) {
		this.feeType = feeType;
	}

	@Column(name ="cash_fee",nullable=true)
	public Integer getCashFee() {
		return cashFee;
	}


	public void setCashFee(Integer cashFee) {
		this.cashFee = cashFee;
	}

	@Column(name ="cash_fee_type",nullable=true)
	public String getCashFeeType() {
		return cashFeeType;
	}


	public void setCashFeeType(String cashFeeType) {
		this.cashFeeType = cashFeeType;
	}

	@Column(name ="coupon_fee",nullable=true)
	public Integer getCouponFee() {
		return couponFee;
	}


	public void setCouponFee(Integer couponFee) {
		this.couponFee = couponFee;
	}

	@Column(name ="coupon_count",nullable=true)
	public Integer getCouponCount() {
		return couponCount;
	}


	public void setCouponCount(Integer couponCount) {
		this.couponCount = couponCount;
	}

	@Column(name ="coupon_id",nullable=true)
	public String getCouponId() {
		return couponId;
	}


	public void setCouponId(String couponId) {
		this.couponId = couponId;
	}

	@Column(name ="sign",nullable=true)
	public String getSign() {
		return sign;
	}


	public void setSign(String sign) {
		this.sign = sign;
	}

//	@Column(name ="sign_type",nullable=true)
//	public String getSignType() {
//		return signType;
//	}
//
//
//	public void setSignType(String signType) {
//		this.signType = signType;
//	}

	@Column(name ="transaction_id",nullable=true)
	public String getTransactionId() {
		return transactionId;
	}


	public void setTransactionId(String transactionId) {
		this.transactionId = transactionId;
	}

	@Column(name ="out_trade_no",nullable=true)
	public String getOutTradeNo() {
		return outTradeNo;
	}


	public void setOutTradeNo(String outTradeNo) {
		this.outTradeNo = outTradeNo;
	}

	@Column(name ="attach",nullable=true)
	public String getAttach() {
		return attach;
	}


	public void setAttach(String attach) {
		this.attach = attach;
	}

	@Column(name ="time_end",nullable=true)
	public Date getTimeEnd() {
		return timeEnd;
	}


	public void setTimeEnd(Date timeEnd) {
		this.timeEnd = timeEnd;
	}

	@Column(name ="createtime",nullable=true)
	public Date getCreateTime() {
		return createTime;
	}


	public void setCreateTime(Date createTime) {
		this.createTime = createTime;
	}

	@Column(name ="updatetime",nullable=true)
	public Date getUpdateTime() {
		return updateTime;
	}


	public void setUpdateTime(Date updateTime) {
		this.updateTime = updateTime;
	}
	
	@Column(name ="dealer_order_id",nullable=true)
	public Integer getDealerOrderId() {
		return dealerOrderId;
	}

	public void setDealerOrderId(Integer dealerOrderId) {
		this.dealerOrderId = dealerOrderId;
	}

}

 

转载于:https://my.oschina.net/410584740/blog/521239

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值