首页 > 开发 > Java > 正文

java实现网站微信扫码支付

2024-07-14 08:41:38
字体:
来源:转载
供稿:网友

一、网站微信扫码支付开发并没有现成的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 = "A6gB0Dy4dsfdssuPCPsdfdshkSCDQcr3eXS"; 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 = “A6gB0Dy4dsfdssuPCPsdfdshkSCDQcr3eXS”; 
这里的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"); });

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持VeVb武林网。


注:相关教程知识阅读请移步到JAVA教程频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表