软件编程
位置:首页>> 软件编程>> java编程>> java实现网站微信扫码支付

java实现网站微信扫码支付

作者:猎狐尊者  发布时间:2023-06-18 18:58:19 

标签:java,微信,扫码支付

一、网站微信扫码支付开发并没有现成的java示例,总结一下自己微信扫码支付心得

二、首先去微信公众平台申请账户

https://mp.weixin.qq.com
**

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

1、微信统一下单接口调用获取预支付id,以及生成二维码所需的codeUrl


/**
* 保存订单,并生成二维码所需的codeUrl
*
* @param request
* @param response
* @param notifyURLBuf
* @param order
* @return
* @throws Exception
*/
@Override
public Map<String, String> getWechatOrderInfo(String ip, Cuser user, String notifyUrl, Order order) throws Exception {
Map<String, String> resultMap = new HashMap<String, String>();
// 生成并保存订单
order.setUserId(user.getId());
// 支付方式 0:银联 1:支付宝 2:网上银行 3:微信 4:其他
order.setPayType("3");
// 生成订单号
order.setOrderNo(OrderNoGenerator.getOrderNo());
// 订单类型 1:消费 2:退款
order.setOrderType("1");
// 订单创建时间
order.setCreateTime(new Date());
// 订单更新时间
order.setUpdateTime(new Date());
// 订单状态 0: 交易中 1:完成 2:已取消
order.setOrderStatus("0");
// 付款状态 0:失败 1:成功 2、待付款
order.setPayStatus("2");
// 设置订单失效时间
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.HOUR, 2);
order.setExpireTime(calendar.getTime());
Integer orderId = this.balanceDao.saveOrder(order);

Map<String, String> payPreIdMap = new HashMap<>();
payPreIdMap = WechatUtil.getPayPreId(String.valueOf(orderId), "体检报告", notifyUrl, ip,
 String.valueOf((order.getMoney().multiply(new BigDecimal(100)).intValue())), orderId.toString());
String prePayId = payPreIdMap.get("prepay_id");
// 更新
order.setId(orderId);
order.setPrepayId(prePayId);
order.setCodeUrl(payPreIdMap.get("code_url"));
this.balanceDao.updateOrder(order);
// return WechatUtil.QRfromGoogle(order.getCodeUrl(), 300, 0);
resultMap.put("codeUrl", order.getCodeUrl());
resultMap.put("orderId", String.valueOf(order.getId()));
return resultMap;
}

此方法返回的数据如下


<xml><return_code><![CDATA[SUCCESS]]></return_code>
<return_msg><![CDATA[OK]]></return_msg>
<appid><![CDATA[wxaf0b*****8afbf]]></appid>
<mch_id><![CDATA[1408****02]]></mch_id>
<nonce_str><![CDATA[zf0vGvdtVycBliwB]]></nonce_str>
<sign><![CDATA[A2910F16086211153D747058063B3368]]></sign>
<result_code><![CDATA[SUCCESS]]></result_code>
<prepay_id><![CDATA[wx201701191109388037e9a12310276591827]]></prepay_id>
<trade_type><![CDATA[NATIVE]]></trade_type>
<code_url><![CDATA[weixin://wxpay/bizpayurl?pr=1UjorNX]]></code_url>
</xml>

2、服务器端接受微信支付结果通知


/**
 * 保存微信通知结果
 *
 * @param request
 * @param response
 * @return
 * @throws Exception
 */
@Override
public String saveWechatNotify(String notifyInfoXml) throws Exception {
 Map<String, String> noticeMap = XMLUtil.doXMLParse(notifyInfoXml);
 // 这个其实是订单 的id
 String outTradeNo = noticeMap.get("out_trade_no");
 Order order = this.balanceDao.getOrderById(Integer.valueOf(outTradeNo));
 // 如果支付通知信息不为,说明请求已经处理过,直接返回
 if (StringUtil.isNotEmpty(order.getNotifyInfo())) {
  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"))) {
    // 商户订单号

// 订单更新时间
    order.setUpdateTime(new Date());
    // ------------------------------
    // 处理业务开始
    // ------------------------------
    // 是否交易成功,1:成功0:失败
    // 微信支付成功
    order.setPayStatus("1");
    // 订单状态 0: 交易中 1:完成 2:已取消
    order.setOrderStatus("1");
    // 保存通知信息
    order.setNotifyInfo(notifyInfoXml);
    this.balanceDao.updateOrder(order);
    // 处理业务完毕
   } else {
    // 错误时,返回结果未签名,记录retcode、retmsg看失败详情。
    logger.info("查询验证签名失败或业务错误");
    logger.info("retcode:" + noticeMap.get("retcode") + " retmsg:" + noticeMap.get("retmsg"));
   }
   return "SUCCESS";
  } else {
   logger.info("后台调用通信失败");
  }
  return "SUCCESS";
 } else {
  logger.info("通知签名验证失败");
 }
 return null;
}

3、上面代码用到的工具方法都在WechatUtil.java工具类中


package com.caifu.tencent.common;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URLEncoder;
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;

import com.caifu.login.utils.XMLUtil;

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=" + Configure.getKey();
 // 获得签名验证结果
 String resultSign = MD5.MD5Encode(preSignStr).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(Configure.getKey());
 String packageSign = MD5.MD5Encode(sb.toString());
 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, String productId) {
 StringBuffer xml = new StringBuffer();
 try {
  String nonceStr = getNonceStr();
  xml.append("</xml>");
  List<NameValuePair> packageParams = new LinkedList<NameValuePair>();
  packageParams.add(new BasicNameValuePair("appid", Configure.getAppid()));
  packageParams.add(new BasicNameValuePair("body", body));
  packageParams.add(new BasicNameValuePair("mch_id", Configure.getMchid()));
  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("product_id", productId));
  packageParams.add(new BasicNameValuePair("spbill_create_ip", ip));
  packageParams.add(new BasicNameValuePair("total_fee", totalFee));
  packageParams.add(new BasicNameValuePair("trade_type", "NATIVE"));
  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;
 }
}

/**
 * 生成支付签名
 *
 * @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(Configure.getKey());
 String appSign = MD5.MD5Encode(sb.toString()).toUpperCase();
 logger.info("orion", appSign);
 return appSign;
}

/**
 * 生成调用微信支付所需参数
 *
 * @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", Configure.getAppid()));
 signParams.add(new BasicNameValuePair("noncestr", nonceStr));
 signParams.add(new BasicNameValuePair("package", "Sign=WXPay"));
 signParams.add(new BasicNameValuePair("partnerid", Configure.getMchid()));
 signParams.add(new BasicNameValuePair("prepayid", prepayId));
 signParams.add(new BasicNameValuePair("timestamp", timeStamp));
 String sign = genAppSign(signParams);
 resultMap.put("appid", Configure.getAppid());
 resultMap.put("noncestr", nonceStr);
 resultMap.put("packageValue", "Sign=WXPay");
 resultMap.put("partnerid", Configure.getMchid());
 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, String productId) throws Exception {
 String paramsXml = genProductArgs(goodOrderNo, body, noticeUrl, ip, totalFee, productId);
 logger.info("orion", paramsXml);
 byte[] buf = WechatUtil.httpPost(Configure.UNIFIEDORDER_API, 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.MD5Encode(String.valueOf(random.nextInt(10000)));
}

public static String getTimeStamp() {
 return String.valueOf(System.currentTimeMillis() / 1000);
}

/**
 * 生成支付二维码
 * @param request
 * @param response
 * @param width
 * @param height
 * @param text 微信生成预定id时,返回的codeUrl
 */
public static void getQRcode(HttpServletRequest request, HttpServletResponse response, Integer width, Integer height, String text) {
 if (width == null) {
  width = 300;
 }
 if (height == null) {
  height = 300;
 }
 String format = "jpg";
 Hashtable hints = new Hashtable();
 hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
 BitMatrix bitMatrix;
 try {
  bitMatrix = new MultiFormatWriter().encode(text, BarcodeFormat.QR_CODE, width, height, hints);
  MatrixToImageWriter.writeToStream(bitMatrix, format, response.getOutputStream());
 } catch (WriterException e) {
  e.printStackTrace();
 } catch (IOException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }
}
}

生成二维码需要两jar


<!-- google zxing 二维码jar begin -->
 <dependency>
  <groupId>com.google.zxing</groupId>
  <artifactId>core</artifactId>
  <version>3.3.0</version>
 </dependency>
 <dependency>
  <groupId>com.google.zxing</groupId>
  <artifactId>javase</artifactId>
  <version>3.3.0</version>
 </dependency>
<!-- google zxing 二维码jar begin -->

4、下面是用到的配置类


package com.caifu.tencent.common;

/**
* User: rizenguo
* Date: 2014/10/29
* Time: 14:40
* 这里放置各种配置数据
*/
public class Configure {
//这个就是自己要保管好的私有Key了(切记只能放在自己的后台代码里,不能放在任何可能被看到源代码的客户端程序中)
// 每次自己Post数据给API的时候都要用这个key来对所有字段进行签名,生成的签名会放在Sign这个字段,API收到Post数据的时候也会用同样的签名算法对Post过来的数据进行签名和验证
// 收到API的返回的时候也要用这个key来对返回的数据算下签名,跟API的Sign数据进行比较,如果值不一致,有可能数据被第三方给篡改

private static String key = "A6gB0Dy4dsfdssu * sdfdshkSCDQcr3eXS";

private static String appSecret="7584sdfdsfe4f26fadsfsdfs56f10728a";

//微信分配的公众号ID(开通公众号之后可以获取到)
private static String appID = "wxaf0b86sdfsdf8afbf";

//微信支付分配的商户号ID(开通公众号的微信支付功能之后可以获取到)
private static String mchID = "14012313702";

//受理模式下给子商户分配的子商户号
private static String subMchID = "";

//HTTPS证书的本地路径
private static String certLocalPath = "";

//HTTPS证书密码,默认密码等于商户号MCHID
private static String certPassword = "";

//是否使用异步线程的方式来上报API测速,默认为异步模式
private static boolean useThreadToDoReport = true;

//机器IP
private static String ip = "";

//以下是几个API的路径:
//1)被扫支付API
public static String UNIFIEDORDER_API = "https://api.mch.weixin.qq.com/pay/unifiedorder";

public static String PAY_API = "https://api.mch.weixin.qq.com/pay/micropay";

//2)被扫支付查询API
public static String PAY_QUERY_API = "https://api.mch.weixin.qq.d/pay/orderquery";

//3)退款API
public static String REFUND_API = "https://api.mch.weixin.qq.com/secapi/pay/refund";

//4)退款查询API
public static String REFUND_QUERY_API = "https://api.mch.weixin.qq.com/pay/refundquery";

//5)撤销API
public static String REVERSE_API = "https://api.mch.weixin.qq.com/secapi/pay/reverse";

//6)下载对账单API
public static String DOWNLOAD_BILL_API = "https://api.mch.weixin.qq.com/pay/downloadbill";

//7) 统计上报API
public static String REPORT_API = "https://api.mch.weixin.qq.com/payitil/report";

public static boolean isUseThreadToDoReport() {
 return useThreadToDoReport;
}

public static void setUseThreadToDoReport(boolean useThreadToDoReport) {
 Configure.useThreadToDoReport = useThreadToDoReport;
}

public static String HttpsRequestClassName = "com.tencent.common.HttpsRequest";

public static void setKey(String key) {
 Configure.key = key;
}

public static void setAppID(String appID) {
 Configure.appID = appID;
}

public static void setMchID(String mchID) {
 Configure.mchID = mchID;
}

public static void setSubMchID(String subMchID) {
 Configure.subMchID = subMchID;
}

public static void setCertLocalPath(String certLocalPath) {
 Configure.certLocalPath = certLocalPath;
}

public static void setCertPassword(String certPassword) {
 Configure.certPassword = certPassword;
}

public static void setIp(String ip) {
 Configure.ip = ip;
}

public static String getKey(){
 return key;
}

public static String getAppid(){
 return appID;
}

public static String getMchid(){
 return mchID;
}

public static String getSubMchid(){
 return subMchID;
}

public static String getCertLocalPath(){
 return certLocalPath;
}

public static String getCertPassword(){
 return certPassword;
}

public static String getIP(){
 return ip;
}

public static void setHttpsRequestClassName(String name){
 HttpsRequestClassName = name;
}

}

在这里需要注意的配置
private static String key = “A6gB0Dy4dsfdssu * sdfdshkSCDQcr3eXS”;
这里的key 是登陆https://pay.weixin.qq.com/index.php/core/info (微信商户平台)设置的api_key

5、xml 解析工具类


package com.caifu.login.utils;

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、整个后台服务已经完成,最后关闭页面微信支付二维码,告知用户支付已经完成了


var f;
/* 定时任务方法,异步请求去查询订单是否支付*/
function GetOrder() {
 var orderId = $('#orderId').val();
 if (orderId != '') {
  $.ajax({
   url : "${base}/balance/auth/isPay?orderId=" + orderId,
   type : "GET",
   async : false,
   success : function(d) {
    if (d == "1") {
     //当获取到微信支付结果时,关闭二维码div
     $(".weixinpay").css("display", "none");
     $("#zhichutankuang").css("display", "block");
     ////当获取到微信支付结果时,关闭定时任务
     clearInterval(f);
     // layer.alert('付款成功', {
     // skin : 'layui-layer-molv', // 样式类名
     // closeBtn : 0
     // }, function() {
     // location.href = "${base}/balance/auth/presentation?tjNo=" + $("#tjNo").val();
     // });

}
   }
  });
 }
}
//异步请求获取生成二维码的url
$(".paylast").click(function() {
 var $payType = $('input:radio:checked').val();
 var $money = $("#money").val();
 var $tjReportType = $("#tjReportType").val();
 var $tjNo = $("#tjNo").val();
 $.ajax({
  url : "${base}/balance/auth/wechatInfo",
  type : "POST",
  async : false,
  data : {
   payType : $payType,
   money : $money,
   tjNo : $tjNo,
   tjReportType : $tjReportType
  },
  success : function(d) {
   if (d.resultCode == "1000") {
    //当请求成功时,设置二维码图片地址
    $("#codeImg").attr('src', d.obj);
    $("#orderId").val(d.attributes.orderId);
    ////当请求成功时,启动定时任务,每隔3秒去后台查询一次订单是否成功
    f = setInterval(GetOrder, 3000);
    // GetOrder(true);
   }
  }
 });
 $(".selpaycon").css("display", "none");
 $(".weixinpay").css("display", "block");
});

来源:https://blog.csdn.net/liehuzunzhe/article/details/54615136

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com