【支付】微信公众号支付

  最近做了微信公众号开发,用户使用微信进行账户余额的充值,开发支付功能使用微信的JSSDK。公众号支付,开发文档 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1通过文档熟悉流程。


开发前置条件

相关参数:
  AppId:公众号的唯一标识(登陆微信企业号后台 - 设置 - 账号信息 - CorpID)
  AppSecret:(微信企业号后台 - 设置 - 权限管理 - 新建一个拥有所有应用权限的普通管理组 - Secret)
  Key:商户API密钥(登陆微信商户后台 - 账户中心 - API安全 - API密钥)
  MchId:商户ID(微信企业号后台 - 服务中心 - 微信支付 - 微信支付 -商户信息 - 商户号)
后台设置:
微信企业号后台 - 服务中心 - 微信支付 - 微信支付 - 开发配置 :
  1.测试授权目录,改成线上支付页面的目录(例:http://www.dynamic.com/wxpay/)
  2.测试白名单,加上测试用户的白名单,只有白名单用户可以付款


支付流程:

  1.前台发起请求,获取必须要的数据,订单号,支付金额。设置上面所说的必要的参数,配置授权目录。

  2.请求发起后,根据微信开发文档,统一下单工具类,设置提交给支付网关的数据的格式为XML,包含必要数据和密钥。

  3.HTTP发送请求给微信,获得返回的内容。是否为Success。是,页面出现支付的控件。

  4.输入支付密码,提交支付授权,验证授权。

  5.调用参数中的回调函数,对返回的数据进行校验,验证通过,则执行业务代码把数据保存到数据库中


前台页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>js sdk 调起页面</title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.4.2.min.js"></script>
    <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
    <script>
        function submit(){
            $.ajax({
                type: 'POST',
                url: 'wechatPay/jsOarder.do',
                data: {'detail':'测试','desc':'测试','goodSn':'20000000101','orderSn':'200000001111','amount':'0.01'},
                success: function(data){
                console.log(data.obj);
                var appId=data.obj.appId;
                var timeStamp=data.obj.timeStamp;
                var nonceStr=data.obj.nonceStr;
                var package=data.obj.package;
                var paySign=data.obj.paySign;
                WeixinJSBridge.invoke(
                        'getBrandWCPayRequest', {
                            "appId":appId,     //公众号名称,由商户传入
                            "timeStamp":timeStamp,         //时间戳,自1970年以来的秒数
                            "nonceStr":nonceStr, //随机串
                            "package":package,
                            "signType":"MD5",         //微信签名方式:
                            "paySign":paySign //微信签名
                        },
                        function(res){
                            WeixinJSBridge.log(res.err_msg);
                            if(res.err_msg == "get_brand_wcpay_request:ok"){
                                <!--支付成功调用-->
                                <!--history.go(0);   -->
                                //alert("成功");
                            }else if(res.err_msg == "get_brand_wcpay_request:cancel"){
                                <!--取消支付调用-->
                                //alert("取消");

                            }else{
                                <!--支付失败-->
                                //alert("失败");

                            }
                        }
                );
            } ,
                dataType: "json"});

        }

        $(function(){
                $("#sub").click(function(){
                    submit();
                });
        });

    </script>
</head>
<body>
<input type="text" placeholder="请输入金额"/><br>
<input type="button" id="sub" value="提交">
</body>
</html>

WechatPayControler,请求和回调

@Controller
@RequestMapping("/wechatPay")
public class WechatPayControler extends BaseControler {

    /**
     * 微信公众号调起
     * @param detail    商品描述
     * @param desc      商品详情
     * @param goodSn    商品编号
     * @param openId    用户openid
     * @param orderSn   订单号
     * @param amount    金额
     * @return          返回包装了调起jssdk所需要的函数
     * @throws Exception
     */
    @RequestMapping("/jsOarder.do")
    @ResponseBody
    public Object jsOrder(String detail, String desc, String goodSn,String openId, String orderSn, String amount) throws Exception {
        JSONObject result = WechatOrderUtils.createOrder(detail, desc, "og5IqwbQCiFn03tw9IPtg1X4vO9U", "10.0.0.1", goodSn, orderSn, amount, "JSAPI");
        return result;
    }
/**
 * 微信支付异步通知处理接口
 * 
 * @param request
 * @param response
 * @return
 * @throws IOException
 */
@RequestMapping("weipayCallBack")
public void weipayCallBack(HttpServletRequest request, HttpServletResponse response) throws IOException {
	logger.info("**************************微信支付异步回调通知开始***********************");
	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 resultStr = new String(outSteam.toByteArray(), "utf-8");
	Map<String, Object> resultMap = new HashMap<String, Object>();
	try {
		resultMap = XMLParser.getMapFromXML(resultStr);
		String out_trade_no = (String) resultMap.get("out_trade_no");
		String return_code = (String) resultMap.get("return_code");
		Double total_fee = (Double.parseDouble((String) resultMap.get("total_fee")) / 100);
		System.out.println("用户充值金额------------->" + total_fee);
		// 充值
		String bank = (String) resultMap.get("bank_type");
		String transaction_id = (String) resultMap.get("transaction_id");
		BigDecimal fee = new BigDecimal(total_fee);
		// 签名验证
		boolean valid = Signature.checkIsSignValidFromResponseString(resultStr);
		if (return_code.equals("SUCCESS") && valid) {
			//支付成功,进行业务处理,返回的数据入库
		}
	} catch (ParserConfigurationException e) {
		logger.info("************微信支付异步回调异常" + e.getMessage() + "微信返回" + resultStr);
		e.printStackTrace();
	} catch (SAXException e) {
		logger.info("************微信支付异步回调异常" + e.getMessage() + "微信返回" + resultStr);
		e.printStackTrace();
	}
	// 通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.[一定别手贱传return_msg回去,他们傻逼会继续回调的]
	String success = "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>";
	response.getOutputStream().write(new String(success).getBytes());
	logger.info("**************************微信支付异步回调通知结束***********************");
}
}


WechatOrderUtils

package cn.cuco.controller.wechat.util;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import cn.cuco.constant.Constant;
import cn.cuco.httpservice.HttpClientUtils;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

/**
 * 微信统一下单工具类
 */
public class WechatOrderUtils {

	/**
	 * 统一下单
	 * 
	 * @param detail
	 *            订单详情,必填
	 * @param desc
	 *            商品或订单描述,必填
	 * @param openid
	 *            公众号调起时需要的OPENID,选填,不填传“”
	 * @param ip
	 *            下订单时的IP,必填
	 * @param goodSn
	 *            业务系统商品编号,必填
	 * @param orderSn
	 *            业务系统订单编号,必填
	 * @param amount
	 *            金额,必填
	 * @param type
	 *            支付类型,分为三种,JSAPI表示公众号调起的支付,NATIVE用于PC端网页调起的扫码支付,APP用于APP端调起的支付
	 * @return 返回对象中封装了网页和APP调起支付控件需要的参数,根据不同的支付类型,有不同的返回参数
	 */
	public static synchronized JSONObject createOrder(String detail, String desc, String openid, String ip, String goodSn, String orderSn, String amount, String type) {

		JSONObject result = new JSONObject();
		System.out.println("openid is ----- > " + openid);

		// 1、参数校验
		if (StringUtils.isBlank(detail) || StringUtils.isBlank(desc) || StringUtils.isBlank(ip) || StringUtils.isBlank(goodSn) || StringUtils.isBlank(orderSn) || StringUtils.isBlank(amount) || StringUtils.isBlank(type)) {
			Log.error("微信支付统一下单请求错误:请求参数不足", null);
			result.put("status", "error");
			result.put("msg", "请求参数不足");
			result.put("obj", null);
			return result;
		}

		double relAmount = 0;// 对应微信支付的真实数目
		try {// 进行格式转换异常获取,保证数目正确
			relAmount = Double.parseDouble(amount) * 100;
		} catch (Exception e) {
			Log.error("微信支付统一下单请求错误:请求金额格式错误", e);
			result.put("status", "error");
			result.put("msg", "请求金额格式错误");
			result.put("obj", null);
			return result;
		}

		if (relAmount == 0) {// 微信支付的支付金额必须为大于0的int类型,单位为分
			Log.error("微信支付统一下单请求错误:请求金额不能为0", null);
			result.put("status", "error");
			result.put("msg", "请求金额不能为0");
			result.put("obj", null);
			return result;
		}

		if (!("JSAPI".equalsIgnoreCase(type) || "NATIVE".equalsIgnoreCase(type) || "APP".equalsIgnoreCase(type))) {
			Log.error("微信支付统一下单请求错误:支付类型为空", null);
			result.put("status", "error");
			result.put("msg", "支付类型为空");
			result.put("obj", null);
			return result;
		}

		/* 公众号调起微信支付的时候,必须要有openID */
		if ("JSAPI".equalsIgnoreCase(type) && StringUtils.isBlank(openid)) {
			Log.error("微信支付统一下单请求错误:请求参数不足", null);
			result.put("status", "error");
			result.put("msg", "请求参数不足");
			result.put("obj", null);
			return result;
		}

		// 2、获取系统配置信息
		String wx_order = // 获取统一下单接口地址
		String mchappid = // 商户appid
		String mchid = // 商户ID
		String wx_callback =http://+"域名+'/'+项目名"/wechatPay/weipayCallBack";// 获取微信支付回调接口
		String wx_key = // 微信商户后台设置的key

		if (StringUtils.isBlank(wx_order) || StringUtils.isBlank(mchappid) || StringUtils.isBlank(mchid) || StringUtils.isBlank(wx_callback)) {
			Log.error("微信支付统一下单请求错误:系统配置信息缺失", null);
			result.put("status", "error");
			result.put("msg", "系统配置信息缺失");
			result.put("obj", null);
			return result;
		}

		// 发送报文模板,其中部分字段是可选字段
		String xml = "" + "<xml>" + "<appid>APPID</appid>" + // 公众号ID
				"<device_info>WEB</device_info>" + // 设备信息
				"<detail>DETAIL</detail>" + // 商品详情
				"<body>BODY</body>" + // 商品描述
				"<mch_id>MERCHANT</mch_id>" + // 微信给的商户ID
				"<nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>" + // 32位随机字符串,不改
				"<notify_url><![CDATA[URL_TO]]></notify_url>" + // 信息通知页面
				"<openid>UserFrom</openid>" + // 支付的用户ID
				"<fee_type>CNY</fee_type>" + // 支付货币,不改
				"<spbill_create_ip>IP</spbill_create_ip>" + // 用户IP
				"<time_start>START</time_start>" + // 订单开始时间
				"<time_expire>STOP</time_expire>" + // 订单结束时间
				"<goods_tag>WXG</goods_tag>" + // 商品标记,不改
				"<product_id>GOODID</product_id>" + // 商品ID
				"<limit_pay>no_credit</limit_pay>" + // 支付范围,默认不支持信用卡支付,不改
				"<out_trade_no>PAY_NO</out_trade_no>" + // 商城生成的订单号
				"<total_fee>TOTAL</total_fee>" + // 总金额
				"<trade_type>TYPE</trade_type>" + // 交易类型,JSAPI,NATIVE,APP,WAP
				"<sign>SIGN</sign>" + // 加密字符串
				"</xml>";

		// 生成订单起始时间,订单7天内有效
		DateFormat df = new SimpleDateFormat("yyyyMMddhhmmss");
		String start_time = df.format(new Date());
		String stop_time = df.format(new Date().getTime() + 7 * 24 * 60 * 60 * 1000);

		// 3、xml数据封装
		// 公众号调起的商户号
		xml = xml.replace("MERCHANT", mchid);
		xml = xml.replace("APPID", mchappid);
		xml = xml.replace("DETAIL", detail);
		xml = xml.replace("BODY", desc);
		xml = xml.replace("URL_TO", wx_callback);
		xml = xml.replace("IP", ip);
		xml = xml.replace("START", start_time);
		xml = xml.replace("STOP", stop_time);
		xml = xml.replace("GOODID", goodSn);
		xml = xml.replace("PAY_NO", orderSn);
		xml = xml.replace("TOTAL", (int) relAmount + "");
		xml = xml.replace("TYPE", type);
		xml = xml.replace("UserFrom", openid);

		// 4、加密
		Map<String, String> map = new HashMap<String, String>();
		map.put("device_info", "WEB");
		map.put("detail", detail);
		map.put("body", desc);
                map.put("mch_id", mchid);
		map.put("appid", mchappid);
		
		map.put("nonce_str", "1add1a30ac87aa2db72f57a2375d8fec");
		map.put("notify_url", wx_callback);
		map.put("fee_type", "CNY");
		map.put("spbill_create_ip", ip);
		map.put("time_start", start_time);
		map.put("time_expire", stop_time);
		map.put("goods_tag", "WXG");
		map.put("product_id", goodSn);
		map.put("limit_pay", "no_credit");
		map.put("out_trade_no", orderSn);
		map.put("total_fee", (int) relAmount + "");
		map.put("trade_type", type);
		if (("JSAPI".equalsIgnoreCase(type))) {
			map.put("openid", openid);
		}

		String sign = SignatureUtils.signature(map, wx_key);
		xml = xml.replace("SIGN", sign);

		// 5、请求
		String response = "";
		try {// 注意,此处的httputil一定发送请求的时候一定要注意中文乱码问题,中文乱码问题会导致在客户端加密是正确的,可是微信端返回的是加密错误

			System.out.println("xml is ----->" + xml.toString());
			// response = HttpUtils.post(wx_order, xml);
			response = HttpClientUtils.sendPost(wx_order, xml);
		} catch (Exception e) {
			Log.error("微信支付统一下单失败:http请求失败", e);
			result.put("status", "error");
			result.put("msg", "http请求失败");
			result.put("obj", null);
			return result;
		}

		// 6、处理请求结果
		XStream s = new XStream(new DomDriver());
		s.alias("xml", WechatOrder.class);
		WechatOrder order = (WechatOrder) s.fromXML(response);

		if ("SUCCESS".equals(order.getReturn_code()) && "SUCCESS".equals(order.getResult_code())) {
			Log.error("微信支付统一下单请求成功:" + order.getPrepay_id(), null);
		} else {
			Log.error("微信支付统一下单请求错误:" + order.getReturn_msg() + order.getErr_code(), null);
			result.put("status", "error");
			result.put("msg", "http请求失败");
			result.put("obj", null);
			return result;
		}

		HashMap<String, String> back = new HashMap<String, String>();

		// 生成客户端调时需要的信息对象
		if ("JSAPI".equalsIgnoreCase(type)) {
			// 网页调起的时候
			String time = Long.toString(System.currentTimeMillis());
			back.put("appId", mchappid);
			back.put("timeStamp", time);
			back.put("nonceStr", "5K8264ILTKCH16CQ2502SI8ZNMTM67VS");
			back.put("package", "prepay_id=" + order.getPrepay_id());
			back.put("signType", "MD5");
			String sign2 = SignatureUtils.signature(back, wx_key);
			JSONObject jsonObject = new JSONObject();
			jsonObject.put("appId", mchappid);
			jsonObject.put("timeStamp", time);
			jsonObject.put("nonceStr", "5K8264ILTKCH16CQ2502SI8ZNMTM67VS");
			jsonObject.put("package", "prepay_id=" + order.getPrepay_id());
			jsonObject.put("signType", "MD5");
			jsonObject.put("paySign", sign2);
			result.put("status", "success");
			result.put("msg", "下单成功");
			result.put("obj", jsonObject);
			return result;

		} 
		return result;
	}
}

SignatureUtils 加密工具类

import java.security.MessageDigest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;


public class SignatureUtils {

	/**
	 * 微信支付加密工具
	 */
	public static String signature(Map<String, String> map, String key) {
		Set<String> keySet = map.keySet();
		String[] str = new String[map.size()];
		StringBuilder tmp = new StringBuilder();
		// 进行字典排序
		str = keySet.toArray(str);
		Arrays.sort(str);
		for (int i = 0; i < str.length; i++) {
			String t = str[i] + "=" + map.get(str[i]) + "&";
			tmp.append(t);
		}
		if (StringUtils.isNotBlank(key)) {
			tmp.append("key=" + key);
		}
		String tosend = tmp.toString();
		MessageDigest md = null;
		byte[] bytes = null;
		try {

			md = MessageDigest.getInstance("MD5");
			bytes = md.digest(tosend.getBytes("utf-8"));
		} catch (Exception e) {
			e.printStackTrace();
		}

		String singe = byteToStr(bytes);
		return singe.toUpperCase();

	}

	/**
	 * 微信支付加密工具
	 */
	public static String signatureSHA1(Map<String, String> map) {
		Set<String> keySet = map.keySet();
		String[] str = new String[map.size()];
		StringBuilder tmp = new StringBuilder();
		// 进行字典排序
		str = keySet.toArray(str);
		Arrays.sort(str);
		for (int i = 0; i < str.length; i++) {
			String t = str[i] + "=" + map.get(str[i]) + "&";
			tmp.append(t);
		}

		String tosend = tmp.toString().substring(0, tmp.length() - 1);
		MessageDigest md = null;
		byte[] bytes = null;
		try {

			md = MessageDigest.getInstance("SHA-1");
			bytes = md.digest(tosend.getBytes("utf-8"));
		} catch (Exception e) {
			e.printStackTrace();
		}

		String singe = byteToStr(bytes);
		return singe.toLowerCase();

	}

	public static String sha1Check(String[] str) {

		StringBuilder tmp = new StringBuilder();
		Arrays.sort(str);
		for (int i = 0; i < str.length; i++) {
			String t = str[i];
			tmp.append(t);
		}
		String tosend = tmp.toString();
		MessageDigest md = null;
		byte[] bytes = null;
		try {

			md = MessageDigest.getInstance("SHA-1");
			bytes = md.digest(tosend.getBytes("utf-8"));
		} catch (Exception e) {
			e.printStackTrace();
		}

		String singe = byteToStr(bytes);
		return singe.toUpperCase();
	}

	/**
	 * 字节数组转换为字符串
	 * 
	 * @param byteArray
	 * @return
	 */
	public static String byteToStr(byte[] byteArray) {
		String strDigest = "";
		for (int i = 0; i < byteArray.length; i++) {
			strDigest += byteToHexStr(byteArray[i]);
		}
		return strDigest;
	}

	/**
	 * 字节转换为字符串
	 * 
	 * @param mByte
	 * @return
	 */
	public static String byteToHexStr(byte mByte) {
		char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A',
				'B', 'C', 'D', 'E', 'F' };
		char[] tempArr = new char[2];
		tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
		tempArr[1] = Digit[mByte & 0X0F];

		String s = new String(tempArr);
		return s;
	}

	public static void main(String[] args) {
		Map<String, String> map = new HashMap<String, String>();
		map.put("noncestr", "Wm3WZYTPz0wzccnW");
		map.put("jsapi_ticket",
				"sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg");
		map.put("timestamp", "1414587457");
		map.put("url", "http://mp.weixin.qq.com?params=value");
	}

}

HttpUtils 给微信网关发送请求

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
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.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;

public class HttpUtils {
	public static HttpClient client;
	static {
		client = HttpClientBuilder.create().build();

	}

	public static String post(String url, Map<String, String> map)
			throws Exception {
		// 处理请求地址
		URI uri = new URI(url);
		HttpPost post = new HttpPost(uri);

		// 添加参数
		List<NameValuePair> params = new ArrayList<NameValuePair>();
		for (String str : map.keySet()) {
			params.add(new BasicNameValuePair(str, map.get(str)));
		}
		post.setEntity(new UrlEncodedFormEntity(params,"utf-8"));
		// 执行请求
		HttpResponse response = client.execute(post);

		if (response.getStatusLine().getStatusCode() == 200) {
			// 处理请求结果
			StringBuffer buffer = new StringBuffer();
			InputStream in = null;
			try {
				in = response.getEntity().getContent();
				BufferedReader reader = new BufferedReader(
						new InputStreamReader(in));
				String line = null;
				while ((line = reader.readLine()) != null) {
					buffer.append(line);
				}

			} finally {
				// 关闭流
				if (in != null)
					in.close();
			}

			return buffer.toString();
		} else {
			return null;
		}

	}

	public static String post(String url, String str)
			throws Exception {
		// 处理请求地址
		URI uri = new URI(url);
		HttpPost post = new HttpPost(uri);
		post.setEntity(new StringEntity(str,"utf-8"));
		// 执行请求
		HttpResponse response = client.execute(post);

		if (response.getStatusLine().getStatusCode() == 200) {
			// 处理请求结果
			StringBuffer buffer = new StringBuffer();
			InputStream in = null;
			try {
				in = response.getEntity().getContent();
				BufferedReader reader = new BufferedReader(
						new InputStreamReader(in));
				String line = null;
				while ((line = reader.readLine()) != null) {
					buffer.append(line);
				}

			} finally {
				// 关闭流
				if (in != null)
					in.close();
			}

			return buffer.toString();
		} else {
			return null;
		}

	}


	public static String get(String url) throws Exception {
		URI uri = new URI(url);
		HttpGet get = new HttpGet(uri);
		HttpResponse response = client.execute(get);
		if (response.getStatusLine().getStatusCode() == 200) {
			StringBuffer buffer = new StringBuffer();
			InputStream in = null;
			try {
				in = response.getEntity().getContent();
				BufferedReader reader = new BufferedReader(
						new InputStreamReader(in));
				String line = null;
				while ((line = reader.readLine()) != null) {
					buffer.append(line);
				}

			} finally {
				if (in != null)
					in.close();
			}

			return buffer.toString();
		} else {
			return null;
		}

	}

}

WechatOrder 微信返回的订单实体

/**
 * 微信返回的订单实体
 * @author hxy
 *
 */
public class WechatOrder {
	
	private String return_code;//返回状态码
	private String return_msg;//返回信息
	private String appid;//公众账号ID
	private String mch_id;//商户号
	private String device_info;//设备号
	private String nonce_str;//随机字符串
	private String sign;//签名
	private String result_code;//业务结果
	private String prepay_id;//预支付交易会话标识
	private String trade_type;//交易类型
	private String err_code;//错误代码
	private String err_code_des;//错误代码描述
	private String code_url;//二维码链接
	
	get...
        set...

}

 支付成功。。。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值