当前位置: 移动技术网 > IT编程>开发语言>Java > java实现网站微信扫码支付

java实现网站微信扫码支付

2019年07月19日  | 移动技术网IT编程  | 我要评论

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

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


**

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

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 是登陆 (微信商户平台)设置的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");
 });

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网