微信小程序 支付后台java实现实例


Posted in Javascript onMay 09, 2017

微信小程序 支付后台java实现实例

前言:

前些天使用 LeanCloud 云引擎写了个小程序的支付相关 以前只做过 APP 支付 这次在小程序支付爬了两天的坑 把代码也分享出来

支付流程:

1.小程序前端获取微信 openId 以及订单号 传给后台
2,后台根据 openId 和订单号进行签名 post 微信统一下单接口
3.后台获取微信返回的xml字符串 解析 二次签名以后返回给前端
4.前端调起支付微信支付 API

先看支付函数:

//获取支付信息
  @EngineFunction("getPayInformation")
  public static Map<String, Object> getPayInformation(
      @EngineFunctionParam("orderId") String orderId
  ) throws AVException, UnsupportedEncodingException, DocumentException {
    Map<String, Object> reqMap = new TreeMap<String, Object>(
        new Comparator<String>() {
          public int compare(String obj1, String obj2) {
            // 升序排序
            return obj1.compareTo(obj2);
          }
        });
    if (AVUser.getCurrentUser() != null) {
      String authDataJson = JSONArray.toJSONString(AVUser.getCurrentUser().get("authData"));
      JSONObject jsonObject = JSON.parseObject(authDataJson);
      jsonObject.get("lc_weapp");
      JSONObject j2 = JSON.parseObject(jsonObject.get("lc_weapp").toString());
      String openId = (String) j2.get("openid");

      AVQuery<Order> query = AVObject.getQuery(Order.class);
      Order order = query.get(orderId);
      reqMap.put("appid", System.getenv("appid"));
      reqMap.put("mch_id", System.getenv("mch_id"));
      reqMap.put("nonce_str", WXPayUtil.getNonce_str());
      reqMap.put("body", new String(order.getDishesList().toString().getBytes("UTF-8")));
      reqMap.put("openid", openId);
      reqMap.put("out_trade_no", order.getObjectId());
      reqMap.put("total_fee", 1); //订单总金额,单位为分
      reqMap.put("spbill_create_ip", "192.168.0.1"); //用户端ip
      reqMap.put("notify_url", System.getenv("notify_url")); //通知地址
      reqMap.put("trade_type", System.getenv("trade_type")); //trade_type=JSAPI时(即公众号支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识
      String reqStr = WXPayUtil.map2Xml(reqMap);
      String resultXml = HttpRequest.sendPost(reqStr);
      System.out.println("微信请求返回:" + resultXml);
      //解析微信返回串 如果状态成功 则返回给前端
      if (WXPayUtil.getReturnCode(resultXml) != null && WXPayUtil.getReturnCode(resultXml).equals("SUCCESS")) {
        //成功
        Map<String, Object> resultMap = new TreeMap<>(
            new Comparator<String>() {
              public int compare(String obj1, String obj2) {
                // 升序排序
                return obj1.compareTo(obj2);
              }
            });
        resultMap.put("appId", System.getenv("appid"));
        resultMap.put("nonceStr", WXPayUtil.getNonceStr(resultXml));//解析随机字符串
        resultMap.put("package", "prepay_id=" + WXPayUtil.getPrepayId(resultXml));
        resultMap.put("signType", "MD5");
        resultMap.put("timeStamp", String.valueOf((System.currentTimeMillis() / 1000)));//时间戳
        String paySign = WXPayUtil.getSign(resultMap);
        resultMap.put("paySign", paySign);
        return resultMap;
      } else {
        throw new AVException(999, "微信请求支付失败");
      }
    } else {
      throw new AVException(98, "当前未登录用户");
    }
  }

其中appid和mch_id可以用系统常量

PS:这里注意一个坑

二次签名的时候使用 appId nonceStr package signType timeStamp 这五个key生成签名(这里无视微信官方文档 以及注意 appId 的大小写)

前端调起API支付时 按照官方文档就可以

网络请求类:

HttpRequest

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;

public class HttpRequest {
  /**
   * 向指定URL发送GET方法的请求
   *
   * @param url  发送请求的URL
   * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
   * @return URL 所代表远程资源的响应结果
   */
  public static String sendGet(String url, String param) {
    String result = "";
    BufferedReader in = null;
    try {
      String urlNameString = url + "?" + param;
      URL realUrl = new URL(urlNameString);
      // 打开和URL之间的连接
      URLConnection connection = realUrl.openConnection();
      // 设置通用的请求属性
      connection.setRequestProperty("accept", "*/*");
      connection.setRequestProperty("connection", "Keep-Alive");
      connection.setRequestProperty("user-agent",
          "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
      // 建立实际的连接
      connection.connect();
      // 获取所有响应头字段
      Map<String, List<String>> map = connection.getHeaderFields();
      // 遍历所有的响应头字段
      for (String key : map.keySet()) {
        System.out.println(key + "--->" + map.get(key));
      }
      // 定义 BufferedReader输入流来读取URL的响应
      in = new BufferedReader(new InputStreamReader(
          connection.getInputStream()));
      String line;
      while ((line = in.readLine()) != null) {
        result += line;
      }
    } catch (Exception e) {
      System.out.println("发送GET请求出现异常!" + e);
      e.printStackTrace();
    }
    // 使用finally块来关闭输入流
    finally {
      try {
        if (in != null) {
          in.close();
        }
      } catch (Exception e2) {
        e2.printStackTrace();
      }
    }
    return result;
  }

  /**
   * 向指定 URL 发送POST方法的请求
   *
   * @param url  发送请求的 URL
   * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
   * @return 所代表远程资源的响应结果
   */
  public static String sendPost(String param) {
    PrintWriter out = null;
    BufferedReader in = null;
    String result = "";
    try {
      URL realUrl = new URL("https://api.mch.weixin.qq.com/pay/unifiedorder");
      // 打开和URL之间的连接
      URLConnection conn = realUrl.openConnection();
      // 设置通用的请求属性
      conn.setRequestProperty("accept", "*/*");
      conn.setRequestProperty("connection", "Keep-Alive");
      conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
//      conn.setRequestProperty("Pragma:", "no-cache");
//      conn.setRequestProperty("Cache-Control", "no-cache");
//      conn.setRequestProperty("Content-Type", "text/xml;charset=utf-8");
      // 发送POST请求必须设置如下两行
      conn.setDoOutput(true);
      conn.setDoInput(true);
      // 获取URLConnection对象对应的输出流
      out = new PrintWriter(conn.getOutputStream());
      // 发送请求参数
      out.print(param);
      // flush输出流的缓冲
      out.flush();
      // 定义BufferedReader输入流来读取URL的响应
      in = new BufferedReader(
          new InputStreamReader(conn.getInputStream()));
      String line;
      while ((line = in.readLine()) != null) {
        result += line;
      }
    } catch (Exception e) {
      System.out.println("发送 POST 请求出现异常!" + e);
      e.printStackTrace();
    }
    //使用finally块来关闭输出流、输入流
    finally {
      try {
        if (out != null) {
          out.close();
        }
        if (in != null) {
          in.close();
        }
      } catch (IOException ex) {
        ex.printStackTrace();
      }
    }
    return result;
  }
}

XML解析工具类

WXPayUtil

import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.Random;


public class WXPayUtil {

  //生成随机字符串
  public static String getNonce_str() {
    String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    Random random = new Random();
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 15; i++) {
      int number = random.nextInt(base.length());
      sb.append(base.charAt(number));
    }
    return sb.toString();
  }

  //map转xml 加上签名信息
  public static String map2Xml(Map<String, Object> map) throws UnsupportedEncodingException {
    StringBuffer sb = new StringBuffer();
    StringBuilder sb2 = new StringBuilder();
    sb2.append("<xml>");
    for (String key : map.keySet()) {
      sb.append(key);
      sb.append('=');
      sb.append(map.get(key));
      sb.append('&');
      // sb2是用来做请求的xml参数
      sb2.append("<" + key + ">");
//      sb2.append("<![CDATA[" + map.get(key) + "]]>");
      sb2.append(map.get(key));
      sb2.append("</" + key + ">");
    }
    sb.append(System.getenv("signKey"));
    String sign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
    sb2.append("<sign>");
    sb2.append(sign);
    sb2.append("</sign>");
    sb2.append("</xml>");
    return sb2.toString();
  }

  //解析微信返回return_code SUCCESS或FILE
  //根据微信返回resultXml再次生成签名
  public static String getSign(Map<String, Object> map) {
    StringBuffer sb = new StringBuffer();
    for (String key : map.keySet()) {
      sb.append(key);
      sb.append('=');
      sb.append(map.get(key));
      sb.append('&');
    }
    sb.append(System.getenv("signKey"));
    System.out.println("第二次签名内容:" + sb);
    System.out.println("第二次签名SING:" + MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase());
    return MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
  }

  //解析微信返回return_code SUCCESS或FILE
  public static String getReturnCode(String resultXml) {
    String nonceStr;
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder;
    try {
      builder = dbf.newDocumentBuilder();
      InputStream inputStream = new ByteArrayInputStream(resultXml.getBytes());
      org.w3c.dom.Document doc = builder.parse(inputStream); //
      // 下面开始读取
      org.w3c.dom.Element root = doc.getDocumentElement(); // 获取根元素
      NodeList nl = root.getElementsByTagName("return_code");
      org.w3c.dom.Element el = (org.w3c.dom.Element) nl.item(0);
      org.w3c.dom.Node nd = el.getFirstChild();
      nonceStr = nd.getNodeValue();
      return nonceStr;
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  //解析微信返回return_msg
  public static String getReturn_msg(String resultXml) {
    String nonceStr;
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder;
    try {
      builder = dbf.newDocumentBuilder();
      InputStream inputStream = new ByteArrayInputStream(resultXml.getBytes());
      org.w3c.dom.Document doc = builder.parse(inputStream); //
      // 下面开始读取
      org.w3c.dom.Element root = doc.getDocumentElement(); // 获取根元素
      NodeList nl = root.getElementsByTagName("return_msg");
      org.w3c.dom.Element el = (org.w3c.dom.Element) nl.item(0);
      org.w3c.dom.Node nd = el.getFirstChild();
      nonceStr = nd.getNodeValue();
      return nonceStr;
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  //解析微信返回appid
  public static String getAppId(String resultXml) {
    String nonceStr;
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder;
    try {
      builder = dbf.newDocumentBuilder();
      InputStream inputStream = new ByteArrayInputStream(resultXml.getBytes());
      org.w3c.dom.Document doc = builder.parse(inputStream); //
      // 下面开始读取
      org.w3c.dom.Element root = doc.getDocumentElement(); // 获取根元素
      NodeList nl = root.getElementsByTagName("appid");
      org.w3c.dom.Element el = (org.w3c.dom.Element) nl.item(0);
      org.w3c.dom.Node nd = el.getFirstChild();
      nonceStr = nd.getNodeValue();
      return nonceStr;
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  //解析微信返回mch_id
  public static String getMchId(String resultXml) {
    String nonceStr;
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder;
    try {
      builder = dbf.newDocumentBuilder();
      InputStream inputStream = new ByteArrayInputStream(resultXml.getBytes());
      org.w3c.dom.Document doc = builder.parse(inputStream); //
      // 下面开始读取
      org.w3c.dom.Element root = doc.getDocumentElement(); // 获取根元素
      NodeList nl = root.getElementsByTagName("mch_id");
      org.w3c.dom.Element el = (org.w3c.dom.Element) nl.item(0);
      org.w3c.dom.Node nd = el.getFirstChild();
      nonceStr = nd.getNodeValue();
      return nonceStr;
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  //解析微信返回nonce_str
  public static String getNonceStr(String resultXml) {
    String nonceStr;
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder;
    try {
      builder = dbf.newDocumentBuilder();
      InputStream inputStream = new ByteArrayInputStream(resultXml.getBytes());
      org.w3c.dom.Document doc = builder.parse(inputStream); //
      // 下面开始读取
      org.w3c.dom.Element root = doc.getDocumentElement(); // 获取根元素
      NodeList nl = root.getElementsByTagName("nonce_str");
      org.w3c.dom.Element el = (org.w3c.dom.Element) nl.item(0);
      org.w3c.dom.Node nd = el.getFirstChild();
      nonceStr = nd.getNodeValue();
      return nonceStr;
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  //解析微信返回prepay_id
  public static String getPrepayId(String resultXml) {
    String nonceStr;
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder;
    try {
      builder = dbf.newDocumentBuilder();
      InputStream inputStream = new ByteArrayInputStream(resultXml.getBytes());
      org.w3c.dom.Document doc = builder.parse(inputStream); //
      // 下面开始读取
      org.w3c.dom.Element root = doc.getDocumentElement(); // 获取根元素
      NodeList nl = root.getElementsByTagName("prepay_id");
      org.w3c.dom.Element el = (org.w3c.dom.Element) nl.item(0);
      org.w3c.dom.Node nd = el.getFirstChild();
      nonceStr = nd.getNodeValue();
      return nonceStr;
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }


}

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

Javascript 相关文章推荐
Extjs Ajax 乱码问题解决方案
Apr 15 Javascript
jQuery 操作option的实现代码
Mar 03 Javascript
用js判断输入是否为中文的函数
Mar 10 Javascript
通过伪协议解决父页面与iframe页面通信的问题
Apr 05 Javascript
js实现图片放大和拖拽特效代码分享
Sep 05 Javascript
JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)
Dec 14 Javascript
ThinkPHP+jquery实现“加载更多”功能代码
Mar 11 Javascript
详解Node.js中exports和module.exports的区别
Apr 19 Javascript
vue绑定设置属性的多种方式(5)
Aug 16 Javascript
详解vue-router导航守卫
Jan 19 Javascript
解决layui下拉框监听问题(监听不到值的变化)
Sep 28 Javascript
JavaScript代理模式原理与用法实例详解
Mar 10 Javascript
使用 Vue.js 仿百度搜索框的实例代码
May 09 #Javascript
详解如何将angular-ui的图片轮播组件封装成一个指令
May 09 #Javascript
使用ES6语法重构React代码详解
May 09 #Javascript
JQuery实现定时刷新功能代码
May 09 #jQuery
React.js中常用的ES6写法总结(推荐)
May 09 #Javascript
js上传图片预览的实现方法
May 09 #Javascript
react开发中如何使用require.ensure加载es6风格的组件
May 09 #Javascript
You might like
BBS(php &amp; mysql)完整版(六)
2006/10/09 PHP
php设计模式 Builder(建造者模式)
2011/06/26 PHP
PHP函数篇之掌握ord()与chr()函数应用
2011/12/05 PHP
php分页思路以及在ZF中的使用
2012/05/30 PHP
ThinkPHP内置jsonRPC的缺陷分析
2014/12/18 PHP
ext combox 下拉框不出现自动提示,自动选中的解决方法
2010/02/24 Javascript
Jquery倒数计时按钮setTimeout的实例代码
2013/07/04 Javascript
javascript为下拉列表动态添加数据项
2014/05/23 Javascript
javascript使用switch case实现动态改变超级链接文字及地址
2014/12/16 Javascript
元素绑定click点击事件方法
2015/06/08 Javascript
JS实现自动定时切换的简洁网页选项卡效果
2015/10/13 Javascript
javascript操作cookie
2017/01/17 Javascript
axios学习教程全攻略
2017/03/26 Javascript
vue.js中v-on:textInput无法执行事件问题的解决过程
2017/07/12 Javascript
React Native 使用Fetch发送网络请求的示例代码
2017/12/02 Javascript
JS 实现微信扫一扫功能
2018/09/14 Javascript
vue实现点击隐藏与显示实例分享
2019/02/13 Javascript
vue+iview使用树形控件的具体使用
2020/11/02 Javascript
[02:52]2017DOTA2国际邀请赛中国区预选赛晋级之路
2017/07/03 DOTA
Python如何生成树形图案
2018/01/03 Python
使用Python通过win32 COM打开Excel并添加Sheet的方法
2018/05/02 Python
python 中文件输入输出及os模块对文件系统的操作方法
2018/08/27 Python
Python匿名函数/排序函数/过滤函数/映射函数/递归/二分法
2019/06/05 Python
HTML5计时器小例子
2013/10/15 HTML / CSS
美国最便宜的旅游网站:CheapTickets
2017/07/09 全球购物
什么是View State?
2013/01/27 面试题
经济管理专业毕业生自荐信范文
2014/01/02 职场文书
档案接收函范文
2014/01/10 职场文书
骨干教师培训制度
2014/01/13 职场文书
会走路的树教学反思
2014/02/20 职场文书
上课看小说检讨书
2014/02/22 职场文书
益达广告词
2014/03/14 职场文书
职业规划实施方案
2014/06/10 职场文书
2015年见习期工作总结
2014/12/12 职场文书
HTML中的表单Form实现居中效果
2021/05/25 HTML / CSS
Python FuzzyWuzzy实现模糊匹配
2022/04/28 Python