当前位置: 代码迷 >> 综合 >> java项目微信公众号接入微信支付,结合微信支付开发文档,已踩坑,有详细代码,必成
  详细解决方案

java项目微信公众号接入微信支付,结合微信支付开发文档,已踩坑,有详细代码,必成

热度:29   发布时间:2023-09-05 19:39:34.0

最近为公司开发一个新功能,具体是什么不重要,主要是这个功能需要对接微信支付,需要在微信页面调起微信支付
基本上我也是初次接触微信支付的对接,所以我先去看了微信的支付开发文档地址如下:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1
在这里我采用的是JSAPI来调起微信支付,这个具体的方法和它的名字很对应,就是利用H5页面的微信内置的js来调起微信支付
当然,想要在微信公众号里调支付,我就需要知道公众号的appID,API密钥,和商户号,这是一切的基础
然后我们需要在微信的支付开发文档中找到统一下单接口的页面
java项目微信公众号接入微信支付,结合微信支付开发文档,已踩坑,有详细代码,必成
在这个页面中微信告诉了我们几点
1.他的接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder
2.需要传给微信这个地址的一系列参数
对于一些很常见的参数我就不再说明,这里我重点将几个比较容易发生错误的参数进行讲解:
1.nonce_str
java项目微信公众号接入微信支付,结合微信支付开发文档,已踩坑,有详细代码,必成
nonce_str参数需要一定的方法生成,代码如下

//生成随机字符串public static String CreateNoncestr() {String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";String res = "";for (int i = 0; i < 16; i++) {Random rd = new Random();res += chars.charAt(rd.nextInt(chars.length() - 1));}return res;}

2.out_trade_no
java项目微信公众号接入微信支付,结合微信支付开发文档,已踩坑,有详细代码,必成
商户订单号:随机生成,唯一:生成代码如下

String trade_no = UUID.randomUUID().toString().substring(0, 15);

我这里通过substring方法将其取成16个字符
3.total_feejava项目微信公众号接入微信支付,结合微信支付开发文档,已踩坑,有详细代码,必成
这里注意金额单位为分,需要自行转化
4.notify_url
java项目微信公众号接入微信支付,结合微信支付开发文档,已踩坑,有详细代码,必成
这个地址配置的是用户支付完成后,微信将会回调的一个地址,你可以配置在自己的项目中,微信回调时会带一系列的参数回来等下有详细说明
5.openid
java项目微信公众号接入微信支付,结合微信支付开发文档,已踩坑,有详细代码,必成
因为是在微信公众号里进行的,所以必须要穿用户唯一标识openid
6.sign
java项目微信公众号接入微信支付,结合微信支付开发文档,已踩坑,有详细代码,必成
签名可以说是最容易出错的一个参数了,他需要你将前面的所有参数放置在对象中后,对该对象进行重新的前面加密具体代码如下

SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();parameters.put("appid", ConfigUtil.APPID);parameters.put("mch_id", ConfigUtil.MCH_ID);parameters.put("nonce_str", WeixinPay.CreateNoncestr());parameters.put("body", message);String trade_no = UUID.randomUUID().toString().substring(0, 15);parameters.put("out_trade_no", trade_no); //订单idparameters.put("fee_type", "CNY");parameters.put("total_fee", String.valueOf(price100));parameters.put("spbill_create_ip", "127.0.0.1");parameters.put("notify_url", returnUrl);parameters.put("trade_type", "JSAPI");String openid = (String)request.getSession().getAttribute("wechat");parameters.put("openid", openid);String sign = WeixinPay.createSign("UTF-8", parameters);parameters.put("sign", sign);

createSign方法如下:

public static String createSign(String characterEncoding, SortedMap<Object, Object> parameters) {StringBuffer sb = new StringBuffer();Set es = parameters.entrySet();Iterator it = es.iterator();while (it.hasNext()) {Map.Entry entry = (Map.Entry) it.next();String k = (String) entry.getKey();Object v = entry.getValue();if (null != v && !"".equals(v)&& !"sign".equals(k) && !"key".equals(k)) {sb.append(k + "=" + v + "&");}}//ConfigUtil.API_KEY为API秘钥sb.append("key=" + ConfigUtil.API_KEY);//MD5加密String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();return sign;}

好,现在我们已经将所有参数都封装到map对象中了
然后我们需要将该map对象转化为String字符串代码如下:

//将map数组拼装成xmlpublic static String getRequestXml(SortedMap<Object, Object> parameters) {StringBuffer sb = new StringBuffer();sb.append("<xml>");Set es = parameters.entrySet();Iterator it = es.iterator();while (it.hasNext()) {Map.Entry entry = (Map.Entry) it.next();String k = (String) entry.getKey();String v = (String) entry.getValue();if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k)) {sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");} else {sb.append("<" + k + ">" + v + "</" + k + ">");}}sb.append("</xml>");return sb.toString();}

最终我们获得了我们需要的入参,这时我们就需要给微信接口发送我的入参
代码如下:

/*** 发送https请求*SSL协议发起http请求* @param requestUrl 请求地址* @param requestMethod 请求方式(GET、POST)* @param outputStr 提交的数据* @return 返回微信服务器响应的信息*/public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {try {// 创建SSLContext对象,并使用我们指定的信任管理器初始化TrustManager[] tm = {new MyX509TrustManager()};SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");sslContext.init(null, tm, new java.security.SecureRandom());// 从上述SSLContext对象中得到SSLSocketFactory对象SSLSocketFactory ssf = sslContext.getSocketFactory();URL url = new URL(requestUrl);HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();//conn.setSSLSocketFactory(ssf);conn.setDoOutput(true);conn.setDoInput(true);conn.setUseCaches(false);// 设置请求方式(GET/POST)conn.setRequestMethod(requestMethod);conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");// 当outputStr不为null时向输出流写数据if (null != outputStr) {OutputStream outputStream = conn.getOutputStream();// 注意编码格式outputStream.write(outputStr.getBytes("UTF-8"));outputStream.close();}// 从输入流读取返回内容InputStream inputStream = conn.getInputStream();InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");BufferedReader bufferedReader = new BufferedReader(inputStreamReader);String str = null;StringBuffer buffer = new StringBuffer();while ((str = bufferedReader.readLine()) != null) {buffer.append(str);}// 释放资源bufferedReader.close();inputStreamReader.close();inputStream.close();inputStream = null;conn.disconnect();return buffer.toString();} catch (ConnectException ce) {
// log.error("连接超时:{}", ce);} catch (Exception e) {
// log.error("https请求异常:{}", e);}return null;}

然后微信接口会返回给我们参数,接下来就是需要将这些数据取出来了,我们将这个String字符串转化为Map类型的,代码如下:

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 = getChildrenText(children);}m.put(k, v);}//关闭流in.close();return m;}

在这个Map中我们需要取出来的数据有:

resultMap.get("appid");
resultMap.get("nonce_str")
resultMap.get("prepay_id")

然后我们需要将数据传给H5页面,在H5页面中通过这些参数去调起微信支付

		data.put("appId", resultMap.get("appid"));data.put("nonceStr", resultMap.get("nonce_str"));String prepay_id = "prepay_id="+resultMap.get("prepay_id");data.put("package", prepay_id);//注意前面有一个prepay_id=多data.put("signType", ConfigUtil.SIGN_TYPE);//这里采用MD5Long time =  System.currentTimeMillis();String time1 = time.toString();String Timestamp = time1.substring(0,10);data.put("timeStamp", Timestamp);String paySign = WeixinPay.createSign("UTF-8", data);data.put("paySign", paySign);ResultUtil.writeResult(response, data.toString());

然后将其返回给前端H5

public static void writeResult(HttpServletResponse response, String res) throws IOException {response.setCharacterEncoding("UTF-8");response.setContentType("text/html");PrintWriter writer = response.getWriter();writer.write(res);writer.flush();}

接下来就是前面H5的js代码了
代码开发文档里有在这里:
java项目微信公众号接入微信支付,结合微信支付开发文档,已踩坑,有详细代码,必成

WeixinJSBridge.invoke('getBrandWCPayRequest', {"appId": m.appId,                  //公众号名称,由商户传入"timeStamp": m.timeStamp,          //戳,自 1970 年以来的秒数"nonceStr": m.nonceStr,         //随机串"package": m.package,      //<span style="font-family:微软雅黑;">商品包信息</span>"signType": m.signType,        //微信签名方式:"paySign": m.paySign           //微信签名},

通过AJAX把数据传到了H5,通过调用这个JS会跳出支付界面,支付完成后,微信会来找你先前传给他的回调地址,并且会给你带来参数
java项目微信公众号接入微信支付,结合微信支付开发文档,已踩坑,有详细代码,必成
参数再开发文档中已列出
然后再这里也可以进行一些业务操作
但是你一定要返回参数给微信支付
代码如下

response.getWriter().write("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>");

并且你的代码需要有处理微信重复请求的能力,不然就会出事哦

再来说一些微信公众平台的配置问题吧,这里的坑也十分的多
首先,微信公众号和商户号一定要建立联系,并且商户号要是普通商户
操作如下
java项目微信公众号接入微信支付,结合微信支付开发文档,已踩坑,有详细代码,必成
java项目微信公众号接入微信支付,结合微信支付开发文档,已踩坑,有详细代码,必成
而且在微信公众号中一定要将你的域名设置为授权回调域名,一个公众号最多设置两个回调域名方法见https://blog.csdn.net/shuzishij/article/details/82312983

最后你还需要在商户号里
java项目微信公众号接入微信支付,结合微信支付开发文档,已踩坑,有详细代码,必成
java项目微信公众号接入微信支付,结合微信支付开发文档,已踩坑,有详细代码,必成
下载好安全证书后,将你需要调起支付的页面地址配置上去,比如说你的地址是https://test.com/weixin/beinhospital.htm
那么你需要填写的是https://test.com/weixin/

  相关解决方案