微信小程序 支付后台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 相关文章推荐
基于prototype扩展的JavaScript常用函数库
Nov 30 Javascript
javascript标签在页面中的位置探讨
Apr 11 Javascript
浅谈Javascript 执行顺序
Dec 18 Javascript
jquery实现textarea 高度自适应
Mar 11 Javascript
JavaScript中神奇的call()方法
Mar 12 Javascript
纯前端JavaScript实现Excel IO案例分享
Aug 26 Javascript
jQuery操作复选框(CheckBox)的取值赋值实现代码
Jan 10 Javascript
AngularJS页面传参的5种方式
Apr 01 Javascript
javascript中json对象json数组json字符串互转及取值方法
Apr 19 Javascript
在一个页面实现两个zTree联动的方法
Dec 20 Javascript
jQuery实现网页拼图游戏
Apr 22 jQuery
js实现无缝滚动双图切换效果
Jul 09 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的sprintf函数的用法 控制浮点数格式
2014/02/14 PHP
实例讲解yii2.0在php命令行中运行的步骤
2015/12/01 PHP
PHP+mysql+ajax轻量级聊天室实现方法详解
2016/10/17 PHP
Windows下php+mysql5.7配置教程
2017/05/16 PHP
php使用scandir()函数扫描指定目录下所有文件示例
2019/06/08 PHP
基于jQuery的可以控制左右滚动及自动滚动效果的代码
2010/07/25 Javascript
js onkeypress与onkeydown 事件区别详细说明
2012/12/13 Javascript
jQuery实现类似滑动门切换效果的层切换
2013/09/23 Javascript
setinterval()与clearInterval()JS函数的调用方法
2015/01/21 Javascript
PHP+jQuery实现随意拖动层并即时保存拖动位置
2015/04/30 Javascript
jQuery插件Timelinr 实现时间轴特效
2015/10/04 Javascript
基于jquery fly插件实现加入购物车抛物线动画效果
2016/04/05 Javascript
功能强大的Bootstrap使用手册(一)
2016/08/02 Javascript
Vue.js如何实现路由懒加载浅析
2017/08/14 Javascript
详解cordova打包成webapp的方法
2017/10/18 Javascript
微信小程序使用checkbox显示多项选择框功能【附源码下载】
2017/12/11 Javascript
Vue自定义指令写法与个人理解
2019/02/09 Javascript
Node4-5静态资源服务器实战以及优化压缩文件实例内容
2019/08/29 Javascript
js实现盒子移动动画效果
2020/08/09 Javascript
vue 封装面包屑组件教程
2020/11/16 Javascript
Python探索之pLSA实现代码
2017/10/25 Python
对python 多线程中的守护线程与join的用法详解
2019/02/18 Python
GitHub 热门:Python 算法大全,Star 超过 2 万
2019/04/29 Python
Django框架实现的普通登录案例【使用POST方法】
2019/05/15 Python
selenium+python自动化测试环境搭建步骤
2019/06/03 Python
Python序列化与反序列化pickle用法实例
2019/11/11 Python
独特的礼品和创新的科技产品:The Grommet
2018/02/24 全球购物
俄罗斯购买剧院和演唱会门票网站:Parter.ru
2019/11/09 全球购物
安德玛菲律宾官网:Under Armour菲律宾
2020/07/28 全球购物
大学生校园创业计划书
2014/02/08 职场文书
书香家庭事迹材料
2014/05/09 职场文书
房屋租赁合同补充协议
2014/10/11 职场文书
2015世界地球日活动总结
2015/02/09 职场文书
大学生村官驻村工作心得体会
2016/01/23 职场文书
springboot临时文件存储目录配置方式
2021/07/01 Java/Android
SpringRetry重试框架的具体使用
2021/07/25 Java/Android