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 getWechatOrderInfo(String ip, Cuser user, String notifyUrl, Order order) throws Exception {
 Map resultMap = new HashMap();
 // 生成并保存订单
 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 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;
 }

此方法返回的数据如下











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

/**
  * 保存微信通知结果
  * 
  * @param request
  * @param response
  * @return
  * @throws Exception
  */
 @Override
 public String saveWechatNotify(String notifyInfoXml) throws Exception {
  Map 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 params) {

  List keys = new ArrayList(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 Params, String sign) {
  // 过滤空值、sign与sign_type参数
  // Map 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 params) {
  StringBuilder sb = new StringBuilder();
  sb.append("");
  for (int i = 0; i < params.size(); i++) {
   sb.append("<" + params.get(i).getName() + ">");
   sb.append(params.get(i).getValue());
   sb.append("");
  }
  sb.append("");
  return sb.toString();
 }

 /**
  * 生成签名
  */
 public static String genPackageSign(List 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("");
   List packageParams = new LinkedList();
   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 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 genPayReq(String prepayId) {
  Map resultMap = new HashMap();
  String timeStamp = getTimeStamp();
  String nonceStr = getNonceStr();
  List signParams = new LinkedList();
  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 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 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


  
   com.google.zxing
   core
   3.3.0
  
  
   com.google.zxing
   javase
   3.3.0
  

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("");
   }
  }

  return sb.toString();
 }

 /**
  * 将requestxml通知结果转出啊成map
  * @param request
  * @return
  * @throws Exception
  */
 public static Map parseXml(HttpServletRequest request) throws Exception {
  // 解析结果存储在HashMap
  Map map = new HashMap();
  InputStream inputStream = request.getInputStream();
  // 读取输入流
  SAXReader reader = new SAXReader();
  org.dom4j.Document document = reader.read(inputStream);
  // 得到xml根元素
  org.dom4j.Element root = document.getRootElement();
  // 得到根元素的所有子节点
  List 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");
 });

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持创新互联。


网站标题:java实现网站微信扫码支付
文章地址:http://ybzwz.com/article/ijipjh.html