欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java实现网站微信扫码支付

程序员文章站 2023-12-22 20:25:52
一、网站微信扫码支付开发并没有现成的java示例,总结一下自己微信扫码支付心得 二、首先去微信公众平台申请账户 ** 三、账户开通、开发者认证之后就...

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

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

上一篇:

下一篇: