微信小程序 支付后台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 相关文章推荐
贴一个在Mozilla中常用的Javascript代码
Jan 09 Javascript
JavaScript中的Document文档对象
Jan 16 Javascript
javascript实现数字验证码的简单实例
Feb 10 Javascript
浅谈js 闭包引起的内存泄露问题
Jun 22 Javascript
jQuery实现淡入淡出二级下拉导航菜单的方法
Aug 28 Javascript
jQuery实现动态控制页面元素的方法分析
Dec 20 jQuery
关于js的三种使用方式(行内js、内部js、外部js)的程序代码
May 05 Javascript
解决Vue使用swiper动态加载数据,动态轮播数据显示白屏的问题
Sep 27 Javascript
nuxt中使用路由守卫的方法步骤
Jan 27 Javascript
基于ssm框架实现layui分页效果
Jul 27 Javascript
基于vue+uniapp直播项目实现uni-app仿抖音/陌陌直播室功能
Nov 12 Javascript
JS端基于download.js实现图片、视频时直接下载而不是打开预览
May 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新手上路(十四)
2006/10/09 PHP
PHP 获取MSN好友列表的代码(2009-05-14测试通过)
2009/09/09 PHP
2014年最新推荐的10款 PHP 开发框架
2014/08/01 PHP
php实现的zip文件内容比较类
2014/09/24 PHP
Zend Framework教程之模型Model用法简单实例
2016/03/04 PHP
PHP chop()函数讲解
2019/02/11 PHP
Cookie跨域问题解决方案代码示例
2020/11/24 PHP
jquery中show()、hide()和toggle()用法实例
2015/01/15 Javascript
在 Express 中使用模板引擎
2015/12/10 Javascript
js实现有过渡渐变效果的图片轮播相册(兼容IE,ff)
2016/01/19 Javascript
基于Echarts 3.19 制作常用的图形(非静态)
2016/05/19 Javascript
大型JavaScript应用程序架构设计模式
2016/06/29 Javascript
总结AngularJS开发者最常犯的十个错误
2016/08/31 Javascript
Vue.js动态组件解析
2016/09/09 Javascript
重新理解JavaScript的六种继承方式
2017/03/24 Javascript
移动端触摸滑动插件swiper使用方法详解
2017/08/11 Javascript
基于vue-cli创建的项目的目录结构及说明介绍
2017/11/23 Javascript
vue与原生app的对接交互的方法(混合开发)
2018/11/28 Javascript
vue-quill-editor的使用及个性化定制操作
2020/08/04 Javascript
Vue双向数据绑定(MVVM)的原理
2020/10/03 Javascript
Python实现多态、协议和鸭子类型的代码详解
2019/05/05 Python
Python中利用LSTM模型进行时间序列预测分析的实现
2019/07/26 Python
python3 实现爬取TOP500的音乐信息并存储到mongoDB数据库中
2019/08/24 Python
Python高阶函数、常用内置函数用法实例分析
2019/12/26 Python
在pytorch 中计算精度、回归率、F1 score等指标的实例
2020/01/18 Python
基于keras输出中间层结果的2种实现方式
2020/01/24 Python
Python类继承和多态原理解析
2020/02/05 Python
戴森英国官网:Dyson英国
2019/05/07 全球购物
优秀干部获奖感言
2014/01/31 职场文书
计算机网络专业自荐信
2014/07/04 职场文书
律师授权委托书范本
2014/10/07 职场文书
实习单位指导教师评语
2014/12/30 职场文书
如何写一份成功的商业计划书
2019/06/25 职场文书
CSS实现两列布局的N种方法
2021/08/02 HTML / CSS
Python pandas求方差和标准差的方法实例
2021/08/04 Python
Java中Dijkstra(迪杰斯特拉)算法
2022/05/20 Java/Android