微信小程序 支付后台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 相关文章推荐
jquery 获取json数据实现代码
Apr 27 Javascript
关于JS数组追加数组采用push.apply的问题
Jun 09 Javascript
JS获取客户端IP地址、MAC和主机名的7个方法汇总
Jul 21 Javascript
jQuery动画特效实例教程
Aug 29 Javascript
javascript实现全局匹配并替换的方法
Apr 27 Javascript
详解AngularJS过滤器的使用
Mar 11 Javascript
js当前页面登录注册框,固定div,底层阴影的实例代码
Oct 04 Javascript
微信小程序表单验证错误提示效果
May 19 Javascript
原生JS与jQuery编写简单选项卡
Oct 30 jQuery
微信小程序实现打开内置地图功能【附源码下载】
Dec 07 Javascript
p5.js绘制创意自画像
Nov 04 Javascript
javascript数组includes、reduce的基本使用
Jul 02 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
PHP循环语句笔记(foreach,list)
2011/11/29 PHP
PHP json_encode中文乱码问题的解决办法
2013/09/09 PHP
浅析php设计模式之数据对象映射模式
2016/03/03 PHP
PHP基于SMTP协议实现邮件发送实例代码
2017/04/27 PHP
使用jquery实现IE下按backspace相当于返回操作
2014/03/18 Javascript
AngularJS基础 ng-keypress 指令简单示例
2016/08/02 Javascript
使用bootstrap实现多窗口和拖动效果
2016/09/22 Javascript
Bootstrap 模态框(Modal)插件代码解析
2016/12/21 Javascript
jquery dataTable 后台加载数据并分页实例代码
2017/06/07 jQuery
javaScript封装的各种写法
2017/08/14 Javascript
解决Angular2 router.navigate刷新页面的问题
2018/08/31 Javascript
原生JS检测CSS3动画是否结束的方法详解
2019/01/27 Javascript
点击按钮弹出模态框的一系列操作代码实例
2019/03/29 Javascript
d3.js实现图形拖拽
2019/12/19 Javascript
js+for循环实现字符串自动转义的代码(把后面的字符替换前面的字符)
2020/12/24 Javascript
[04:03]辉夜杯主赛事 12月25日RECAP精彩回顾
2015/12/26 DOTA
python计算最大优先级队列实例
2013/12/18 Python
python多进程操作实例
2014/11/21 Python
用Python实现KNN分类算法
2017/12/22 Python
python增加矩阵维度的实例讲解
2018/04/04 Python
Python设计模式之抽象工厂模式原理与用法详解
2019/01/15 Python
在Python中使用turtle绘制多个同心圆示例
2019/11/23 Python
利用python读取YUV文件 转RGB 8bit/10bit通用
2019/12/09 Python
关于Keras模型可视化教程及关键问题的解决
2020/01/24 Python
Python3 io文本及原始流I/O工具用法详解
2020/03/23 Python
MxNet预训练模型到Pytorch模型的转换方式
2020/05/25 Python
python中delattr删除对象方法的代码分析
2020/12/15 Python
JoJo Maman Bébé爱尔兰官网:英国最受欢迎的精品母婴品牌
2020/12/20 全球购物
中级会计职业生涯规划书
2014/03/01 职场文书
幼儿园亲子活动总结
2014/04/26 职场文书
离职报告格式
2014/11/04 职场文书
大学生逃课检讨书
2015/05/04 职场文书
环保守法证明
2015/06/24 职场文书
Python排序算法之插入排序及其优化方案详解
2021/06/11 Python
python实现简易自习室座位预约系统
2021/06/30 Python
Django中session进行权限管理的使用
2021/07/09 Python