微信小程序支付V3版本接口实现

一.说明和前期准备(小程序的V3版本)

特别说明:遇到 java.security.InvalidKeyException: Illegal key size ******* getValidator的错误
参考添加链接描述
JDK7的下载地址
JDK8的下载地址:

下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt
如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security目录下覆盖原来的文件
如果安装了JDK,还要将两个jar文件也放到%JDK_HOME%\jre\lib\security目录下覆盖原来文件。
在这里插入图片描述

1.场景:小程序项目需要对接微信小程序的支付接口,这里使用的是V3版本

官方文档链接:微信小程支付申请文档链接

1.1按照上面的文档申请支付

拿到相关的参数(小程序的Appid,小程序的appSecret ,商户秘钥 PrivateKey,商户号 Mchid,证书序列号MchSerialNo,V3秘钥))本人读取方式为从配置文件中读取

package com.wxapplet.model;

import java.io.IOException;
import java.util.Properties;

import org.springframework.beans.factory.annotation.Value;

import com.foc.alipay.config.AlipayServiceEnvConstants;
import com.foc.common.util.Constants;

public class WxV3payConfig {

	static {
		/**
		 * 静态代码块初始化类变量
		 */

		Properties proper = new Properties();
		try {
			proper.clear();
			proper.load(WxV3payConfig.class.getResourceAsStream("/paykey.properties"));
			appletAppid=proper.getProperty("appletAppid");
			appletMchid=proper.getProperty("appletMchid");
			appletPrivateKeyPath=proper.getProperty("appletPrivateKeyPath");
			appletMchSerialNo=proper.getProperty("appletMchSerialNo");
			appletSecret=proper.getProperty("appletSecret");
			appletApiV3Key=proper.getProperty("appletApiV3Key");
		
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public static String appletAppid;// 由微信生成的应用ID
	public static String appletMchid;// 直连商户的商户号,由微信支付生成并下发。
	public static String appletPrivateKeyPath;// 小程序商户秘钥

	public static String appletMchSerialNo;// 商户证书序列号

	public static String appletSecret;// 小程序 appSecret

	public static String appletApiV3Key;// V3密钥

	@Value("${appletAppid}")
	public static void setAppletAppid(String appletAppid) {
		WxV3payConfig.appletAppid = appletAppid;
	}

	@Value("${appletMchid}")
	public static void setAppletMchid(String appletMchid) {
		WxV3payConfig.appletMchid = appletMchid;
	}

	@Value("${appletPrivateKeyPath}")
	public static void setAppletPrivateKey(String appletPrivateKeyPath) {
		WxV3payConfig.appletPrivateKeyPath = appletPrivateKeyPath;
	}

	@Value("${appletMchSerialNo}")
	public static void setAppletMchSerialNo(String appletMchSerialNo) {
		WxV3payConfig.appletMchSerialNo = appletMchSerialNo;
	}

	@Value("${appletSecret}")
	public static void setAppletSecret(String appletSecret) {
		WxV3payConfig.appletSecret = appletSecret;
	}

	@Value("${appletApiV3Key}")
	public static void setAppletApiV3Key(String appletApiV3Key) {
		WxV3payConfig.appletApiV3Key = appletApiV3Key;
	}
	
	
	

}

在这里插入图片描述
对应的paykey.properties文件

#v3
#ying yong id 
appletAppid=
#shang hu hao
appletMchid=

appletPrivateKeyPath=appletPrivateKey.pem

#zheng shu xu lie hao
appletMchSerialNo=
#appsectet
appletSecret=
#v3 mi yao
appletApiV3Key=

在这里插入图片描述

2.引入相关的maven包

2.1参考的微信官方文档

微信开发指引
微信java给出的示例
在这里插入图片描述

<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-apache-httpclient</artifactId>
    <version>0.4.7</version>
</dependency>

在这里插入图片描述

3.相关工具类

3.1发送http请求工具类
package com.wxapplet.util;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.PrivateKey;
import java.util.HashMap;

import javax.servlet.http.HttpServletRequest;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
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.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;

@Component
public class AppletHttpClient  {
	
   /**
	 * 将通知参数转化为字符串
	 * 
	 * @param request
	 * @return
	 */
	public static String readData(HttpServletRequest request) {
		BufferedReader br = null;
		try {
			StringBuilder result = new StringBuilder();
			br = request.getReader();
			for (String line; (line = br.readLine()) != null;) {
				if (result.length() > 0) {
					result.append("\n");
				}
				result.append(line);
			}
			return result.toString();
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 获取商户的私钥文件
	 * 
	 * @param filename
	 * @return
	 */
	public PrivateKey getPrivateKey(String filename) {
		InputStream insss = AppletHttpClient.class.getClassLoader().getResourceAsStream(filename);

		// InputStream insss = ClassLoader.getSystemResourceAsStream(filename);
		return PemUtil.loadPrivateKey(insss);
	}

	/**
	 * 获取http请求对象
	 * 
	 * @param verifier
	 * @return
	 */
	@Bean(name = "wxPayClient")
	public CloseableHttpClient getWxPayClient(AutoUpdateCertificatesVerifier verifier) {

		// 获取商户私钥
		PrivateKey privateKey = getPrivateKey(WxV3payConfig.appletPrivateKeyPath);

		WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
				.withMerchant(WxV3payConfig.appletMchid, WxV3payConfig.appletMchSerialNo, privateKey).withValidator(new WechatPay2Validator(verifier));
		// ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient

		// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
		CloseableHttpClient httpClient = builder.build();

		return httpClient;
	}

	/**
	 * 获取HttpClient,无需进行应答签名验证,跳过验签的流程
	 */
	@Bean(name = "wxPayNoSignClient")
	public CloseableHttpClient getWxPayNoSignClient() {

		// 获取商户私钥
		PrivateKey privateKey = getPrivateKey(WxV3payConfig.appletPrivateKeyPath);

		// 用于构造HttpClient
		WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
				// 设置商户信息
				.withMerchant(WxV3payConfig.appletMchid, WxV3payConfig.appletMchSerialNo, privateKey)
				// 无需进行签名验证、通过withValidator((response) -> true)实现
				.withValidator((response) -> true);

		// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
		CloseableHttpClient httpClient = builder.build();

		return httpClient;
	}
	

	

	
	
	/**
	 * V3  SHA256withRSA 签名.
	 * @param appid 
	 * @param timeStamp
	 * @param nonceStr
	 * @param prepayId
	 * @param privateKey
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeyException
	 * @throws SignatureException
	 * @throws FileNotFoundException 
	 */
	
	public String getSign1(String appid ,String timeStamp,String nonceStr ,String prepayId  , String  privateKeyPath) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, FileNotFoundException{
		try {
			String signatureStr = Stream.of(appid, timeStamp, nonceStr, prepayId)
		            .collect(Collectors.joining("\n", "", "\n"));
			Signature	sign = Signature.getInstance("SHA256withRSA");
			 InputStream insss = PayCommonUtil.class.getClassLoader().getResourceAsStream(privateKeyPath);
			PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(insss);
			sign.initSign(merchantPrivateKey);
			sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
			return Base64.getEncoder().encodeToString(sign.sign());
		} catch (Exception e) {
			e.printStackTrace();
		}
	    
		return null;
	}
	
	
	/**
	 * 获取签名验证器
	 * 
	 * @return
	 */
	@Bean
	public AutoUpdateCertificatesVerifier  getVerifier() {

		// 获取商户私钥
		PrivateKey privateKey = getPrivateKey(WxV3payConfig.appletPrivateKeyPath);

		// 私钥签名对象
		PrivateKeySigner privateKeySigner = new PrivateKeySigner(WxV3payConfig.appletMchSerialNo, privateKey);

		// 身份认证对象
		WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(WxV3payConfig.appletMchid, privateKeySigner);

		// 使用定时更新的签名验证器,不需要传入证书
		AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(wechatPay2Credentials,
				WxV3payConfig.appletApiV3Key.getBytes(StandardCharsets.UTF_8));

		return verifier;

	}
}

3.2进行下单操作

在这里插入图片描述
这里使用JSAPI下单,微信小程序JSAPI下单参考

     String appId =WxV3payConfig.appletAppid; // 
			 String mchId =WxV3payConfig.appletMchid; // 商户号
			 String privateKeyPath =WxV3payConfig.appletPrivateKeyPath;//  你的商户私钥路径

			 
			 String nonceStr = RandomUtil.randomString(32);//随机字符串
			 
			 

				// 构造订单信息
				JSONObject jsonObject = new JSONObject();
				jsonObject.put("appid",appId); // 小程序的应用appid
				jsonObject.put("mchid", mchId); // 直连商户的商户号,由微信支付生成并下发。
				String body = "订单消费";
				Project project = projectService.findOne(tempPersonalRecord.getProjectId());
				if (project != null && StringUtils.isNotBlank(project.getName())) {
					body = project.getName();
				}
				jsonObject.put("description", body); // 商品描述
				jsonObject.put("out_trade_no", outTradeNo); // 商户订单号
				logger.info("total_fee=" + Math.round(tempPersonalRecord.getMoney() * 100) + "");
				JSONObject amoutJson = new JSONObject();
				amoutJson.put("total", tempPersonalRecord.getMoney() * 100); // 总金额,订单总金额,单位为分
				amoutJson.put("currency", "CNY");// 货币类型CNY:人民币,境内商户号仅支持人民币。
				jsonObject.put("amount", amoutJson); // 订单金额信息
				jsonObject.put("notify_url", notifyUrl); // 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
				
				JSONObject payUser = new JSONObject();
				payUser.put("openid", mfUser.getOpenId());
				jsonObject.put("payer", payUser); // 支付者用户在商户appid下的唯一标识。
				
				

				// 发送请求进行JSAPI下单
				 HttpPost httpPost = new  HttpPost(APPLET_JSAPI_URL); 
				 
				 StringEntity entity = new StringEntity(jsonObject.toString(),"utf-8");
				  entity.setContentType("application/json");
			        httpPost.setEntity(entity);
			        httpPost.setHeader("Accept", "application/json");
			      //完成签名并执行请求
			        CloseableHttpResponse response = wxPayClient.execute(httpPost);
			        
			        String reString = EntityUtils.toString(response.getEntity());//响应体
			        int statusCode = response.getStatusLine().getStatusCode();//响应状态码
			        
			        if (statusCode==200) {//处理成功
			        	logger.info("微信小程序支付响应成功,返回的结果为="+reString);
						
					}else if (statusCode==204) {//处理成功无返回的boy
						logger.info("微信小程序支付响应成功");
					}else {
						logger.info("微信小程序响应失败,响应码为="+statusCode+",返回的结果为="+reString);
						return new ResultVO(ResultCode.ORDERERROR);
					}
			       


				SortedMap<String, String> params = new TreeMap<String, String>();
				String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);// 时间戳
				String preId = "prepay_id=" + JSON.parseObject(reString).getString("prepay_id");
				String paySign = appletHttpClient.getSign1(appId, timeStamp, nonceStr, preId,
						privateKeyPath);// 签名
				

				rr.put("appId", appId);// appid
				rr.put("timeStamp", timeStamp);// 时间戳
				rr.put("nonceStr", nonceStr);// 随机字符串
				rr.put("package", "prepay_id=" + JSON.parseObject(reString).getString("prepay_id"));// 订单详情扩展字符串
				rr.put("signType", "RSA");// 签名类型,默认为RSA,仅支持RSA
				rr.put("paySign", paySign);// 签名
				 rr.put("outTradeNo", tempPersonalRecord.getOutTradeNo());//订单编号
				return new ResultVO(ResultCode.SUCCESS, rr);
3.3获取返回的prepay_id稍后返回给前端

4.向前端传递调用wx.requestPayment(OBJECT)发起微信支付的参数

Api地址
特别说明:签名使用的是SHA256 with RSA签名,并对签名结果进行Base64编码得到签名值。

4.1生成签名的工具类
	
	/**
	 * V3  SHA256withRSA 签名.
	 * @param appid 
	 * @param timeStamp
	 * @param nonceStr
	 * @param prepayId
	 * @param privateKey
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws InvalidKeyException
	 * @throws SignatureException
	 */
	public static String getSign(String appid ,String timeStamp,String nonceStr ,String prepayId  , String  privateKey) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException{
		try {
			String signatureStr = Stream.of(appid, timeStamp, nonceStr, prepayId)
		            .collect(Collectors.joining("\n", "", "\n"));
			Signature	sign = Signature.getInstance("SHA256withRSA");
			PrivateKey merchantPrivateKey = PemUtil
			        .loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));
			sign.initSign(merchantPrivateKey);
			sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
			return Base64.getEncoder().encodeToString(sign.sign());
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	    
		return null;
	}
	

官方签名参考地址

4.2签名的生成
	String nonceStr = RandomUtil.randomString(32);// 随机字符串
	String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);// 时间戳
	String preId = "prepay_id=" + JSON.parseObject(reString).getString("prepay_id");
	
			String paySign = PayCommonUtil.getSign(weChatPub.getAppletAppid(), timeStamp, nonceStr, preId,
					weChatPub.getAppletPrivateKey());// 签名
			
			rr.put("appId", weChatPub.getAppletAppid());// appid
			rr.put("timeStamp", timeStamp);// 时间戳
			rr.put("nonceStr", nonceStr);// 随机字符串
			rr.put("package", "prepay_id=" + JSON.parseObject(reString).getString("prepay_id"));// 订单详情扩展字符串
			rr.put("signType", "RSA");// 签名类型,默认为RSA,仅支持RSA
			rr.put("paySign", paySign);// 签名
			return new ResultVO(ResultCode.SUCCESS, rr);
	
4.3使用对生成的签名使用官方工具验签

工具下载地址
在这里插入图片描述

5.支付成功后微信的回调

官方的验签和解密
在这里插入图片描述

// 构建request,传入必要参数
 NotificationRequest request = new NotificationRequest.Builder().withSerialNumber(wechatPaySerial)
        .withNonce(nonce)
        .withTimestamp(timestamp)
        .withSignature(signature)
        .withBody(body)
        .build();
NotificationHandler handler = new NotificationHandler(verifier, apiV3Key.getBytes(StandardCharsets.UTF_8));
// 验签和解析请求体
Notification notification = handler.parse(request);
// 从notification中获取解密报文
System.out.println(notification.getDecryptData());

相关示例代码(包含验签操作)自己实现的代码

logger.info("支付成功进入回调方法--" + DateUtil.formatDate(new Date()));
		Map<String, String> map1 = new HashMap<>();// 应答对象
		Gson gson = new Gson();
		Map<String, String> map = new HashMap<>();// 应答对象
		String outTradeNo = null;// 商户订单号
		String transactionId = null;// 微信支付订单号
		String timeEnd = null;//
		String openid = null;// 用户标识
		String tradeState = null;// 交易状态
	
			
			// 处理通知参数
			String body = appletHttpClient.readData(request);
			Map<String, Object> bodyMap = gson.fromJson(body, HashMap.class);
			String requestId = (String) bodyMap.get("id");
				// 获取验签器
				// 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)
				PrivateKey privateKey =appletHttpClient.getPrivateKey(WxV3payConfig.appletPrivateKeyPath);
				AutoUpdateCertificatesVerifier verifier = appletHttpClient.getVerifier();
				WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest = new WechatPay2ValidatorForRequest(
						verifier, requestId, body);
				// 进行验签操作
				if (!wechatPay2ValidatorForRequest.validate(request)) {// 验签成功

					logger.error("支付通知验签失败");
					// 失败应答
					response.setStatus(500);
					map.put("code", "ERROR");
					map.put("message", "通知验签失败");
					return gson.toJson(map);

				}
				logger.info("支付通知验证成功");
				// 获取明文
				String plainText = wxAPIV3AesUtil.decryptFromResource(bodyMap, WxV3payConfig.appletApiV3Key);
				// 将明文转换成map
				Map<String, String> resultMap = JSON.parseObject(plainText, HashMap.class);
				outTradeNo = resultMap.get("out_trade_no");// 商户订单号
				transactionId = resultMap.get("transaction_id");// 微信支付订单号
				timeEnd = resultMap.get("time_end");//
				openid = resultMap.get("openid");// 用户标识
				tradeState = resultMap.get("trade_state");// 交易状态
			
5.1验签的工具类
package com.wxapplet.util;

import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant;

import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.*;

public class WechatPay2ValidatorForRequest {
	 protected static final Logger log = LoggerFactory.getLogger(WechatPay2ValidatorForRequest.class);
	    /**
	     * 应答超时时间,单位为分钟
	     */
	    protected static final long RESPONSE_EXPIRED_MINUTES = 5;
	    protected final Verifier verifier;
	    protected final String requestId;
	    protected final String body;


	    public WechatPay2ValidatorForRequest(Verifier verifier, String requestId, String body) {
	        this.verifier = verifier;
	        this.requestId = requestId;
	        this.body = body;
	    }

	    protected static IllegalArgumentException parameterError(String message, Object... args) {
	        message = String.format(message, args);
	        return new IllegalArgumentException("parameter error: " + message);
	    }

	    protected static IllegalArgumentException verifyFail(String message, Object... args) {
	        message = String.format(message, args);
	        return new IllegalArgumentException("signature verify fail: " + message);
	    }

	    //·验签
	    public final boolean validate(HttpServletRequest request) throws IOException {
	        try {
	            //处理请求参数
	            validateParameters(request);

	            //构造验签名串
	            String message = buildMessage(request);

	            String serial = request.getHeader(WECHAT_PAY_SERIAL);
	            String signature = request.getHeader(WECHAT_PAY_SIGNATURE);

	            //验签
	            if (!verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature)) {
	                throw verifyFail("serial=[%s] message=[%s] sign=[%s], request-id=[%s]",
	                        serial, message, signature, requestId);
	            }
	        } catch (IllegalArgumentException e) {
	            log.warn(e.getMessage());
	            return false;
	        }

	        return true;
	    }

	    protected final void validateParameters(HttpServletRequest request) {

	        // NOTE: ensure HEADER_WECHAT_PAY_TIMESTAMP at last
	        String[] headers = {WECHAT_PAY_SERIAL, WECHAT_PAY_SIGNATURE, WECHAT_PAY_NONCE, WECHAT_PAY_TIMESTAMP};

	        String header = null;
	        for (String headerName : headers) {
	            header = request.getHeader(headerName);
	            if (header == null) {
	                throw parameterError("empty [%s], request-id=[%s]", headerName, requestId);
	            }
	        }

	        //判断请求是否过期
	        String timestampStr = header;
	        try {
	            Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestampStr));
	            // 拒绝过期请求
	            if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= RESPONSE_EXPIRED_MINUTES) {
	                throw parameterError("timestamp=[%s] expires, request-id=[%s]", timestampStr, requestId);
	            }
	        } catch (DateTimeException | NumberFormatException e) {
	            throw parameterError("invalid timestamp=[%s], request-id=[%s]", timestampStr, requestId);
	        }
	    }

	    protected final String buildMessage(HttpServletRequest request) throws IOException {
	        String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
	        String nonce = request.getHeader(WECHAT_PAY_NONCE);
	        return timestamp + "\n"
	                + nonce + "\n"
	                + body + "\n";
	    }

	    protected final String getResponseBody(CloseableHttpResponse response) throws IOException {
	        HttpEntity entity = response.getEntity();
	        return (entity != null && entity.isRepeatable()) ? EntityUtils.toString(entity) : "";
	    }

}

5.2 解密回调通知的数据相关工具类
@Component
public class WxAPIV3AesUtil {

	/**
	 * 对称解密
	 * 
	 * @param bodyMap
	 * @return
	 */
	public static String decryptFromResource(Map<String, Object> bodyMap, String apiV3Key)
			throws GeneralSecurityException {

		// 通知数据
		Map<String, String> resourceMap = (Map) bodyMap.get("resource");
		// 数据密文
		String ciphertext = resourceMap.get("ciphertext");
		// 随机串
		String nonce = resourceMap.get("nonce");
		// 附加数据
		String associatedData = resourceMap.get("associated_data");

		AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
		String plainText = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),
				nonce.getBytes(StandardCharsets.UTF_8), ciphertext);

		return plainText;
	}


}

参考链接

官方微信V3排错指南
参考1
参考2

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值