最近公司项目急着测试,需要开发微信小程序+微信支付+微信退款,本着这几天的一些研究,决定记录一下开发的过程。

本着知识分享的原则,希望对大家有所帮助。

本篇针对的是微信小程序的支付开发,如果有对微信公众号的支付开发需要的,可以去我的github上看看,有个sell的项目很好的完成了公众号方面的支付与退款,代码很全,用的是最优秀的sdk,肯定对你们学习和工作有帮助,下面贴一下github链接: https://github.com/wenbingshen/springboot

废话不多说,开始我们的小程序支付开发之旅:

首先呢,开发之前,需要交代的是,有关微信支付的开发需要有自己的商户号和密钥,这在微信支付开发文档上面讲的很清楚,有过支付开发经验的对这一点很清楚。

微信小程序支付以及微信退款开发-LMLPHP

了解了上面的情况后咱们就开始着手开发吧!

先编写一个常量类Constant,将有关的配置常量配在里面:

public class Constant {

    public static final String DOMAIN = "http://sellbin.natapp1.cc";//配置自己的域名

    public static final String APP_ID = "填写自己的";

    public static final String APP_SECRET = "填写自己的";

    public static final String APP_KEY = "填写自己的";

    public static final String MCH_ID = "填写自己的";  //商户号

    public static final String URL_UNIFIED_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    public static final String URL_NOTIFY = Constant.DOMAIN + "/wxpay/views/payInfo.jsp";

    public static final String TIME_FORMAT = "yyyyMMddHHmmss";

    public static final int TIME_EXPIRE = 2;  //单位是day

}

支付的时候,我们需要利用发起支付的用户code去微信接口获取用户的openid,只有得到了openid才能去申请预付单获得prepayId,然后去唤起微信支付。

微信支付文档上面也写的很清楚:

微信小程序支付以及微信退款开发-LMLPHP

接下来我们编写PayController类去调用微信支付的接口:

package luluteam.wxpay.controller;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import luluteam.wxpay.constant.Constant;
import luluteam.wxpay.entity.PayInfo;
import luluteam.wxpay.util.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

@Controller
public class PayController {

    private static Logger log = Logger.getLogger(PayController.class);

    @ResponseBody
    @RequestMapping(value = "/prepay", produces = "text/html;charset=UTF-8")
    public String prePay(String code, ModelMap model, HttpServletRequest request) {

        System.out.println("code:"+code);
        String content = null;
        Map map = new HashMap();
        ObjectMapper mapper = new ObjectMapper();

        boolean result = true;
        String info = "";

        log.error("\n======================================================");
        log.error("code: " + code);

        String openId = getOpenId(code);
        System.out.println("获取openid啊"+openId);
        if(StringUtils.isBlank(openId)) {
            result = false;
            info = "获取到openId为空";
        } else {
            openId = openId.replace("\"", "").trim();

            String clientIP = CommonUtil.getClientIp(request);

            log.error("openId: " + openId + ", clientIP: " + clientIP);

            String randomNonceStr = RandomUtils.generateMixString(32);
            String prepayId = unifiedOrder(openId, clientIP, randomNonceStr);

            log.error("prepayId: " + prepayId);

            if(StringUtils.isBlank(prepayId)) {
                result = false;
                info = "出错了,未获取到prepayId";
            } else {
                map.put("prepayId", prepayId);
                map.put("nonceStr", randomNonceStr);
            }
        }

        try {
            map.put("result", result);
            map.put("info", info);
            content = mapper.writeValueAsString(map);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return content;
    }


    private String getOpenId(String code) {
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + Constant.APP_ID +
                "&secret=" + Constant.APP_SECRET + "&js_code=" + code + "&grant_type=authorization_code";

        HttpUtil httpUtil = new HttpUtil();
        try {

            HttpResult httpResult = httpUtil.doGet(url, null, null);

            if(httpResult.getStatusCode() == 200) {

                JsonParser jsonParser = new JsonParser();
                JsonObject obj = (JsonObject) jsonParser.parse(httpResult.getBody());

                log.error("getOpenId: " + obj.toString());

                if(obj.get("errcode") != null) {
                    log.error("getOpenId returns errcode: " + obj.get("errcode"));
                    return "";
                } else {
                    return obj.get("openid").toString();
                }
                //return httpResult.getBody();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 调用统一下单接口
     * @param openId
     */
    private String unifiedOrder(String openId, String clientIP, String randomNonceStr) {

        try {

            String url = Constant.URL_UNIFIED_ORDER;

            PayInfo payInfo = createPayInfo(openId, clientIP, randomNonceStr);
            String md5 = getSign(payInfo);
            payInfo.setSign(md5);

            log.error("md5 value: " + md5);

            String xml = CommonUtil.payInfoToXML(payInfo);
            xml = xml.replace("__", "_").replace("<![CDATA[1]]>", "1");
            //xml = xml.replace("__", "_").replace("<![CDATA[", "").replace("]]>", "");
            log.error(xml);

            StringBuffer buffer = HttpUtil.httpsRequest(url, "POST", xml);
            log.error("unifiedOrder request return body: \n" + buffer.toString());
            Map<String, String> result = CommonUtil.parseXml(buffer.toString());


            String return_code = result.get("return_code");
            if(StringUtils.isNotBlank(return_code) && return_code.equals("SUCCESS")) {

                String return_msg = result.get("return_msg");
                if(StringUtils.isNotBlank(return_msg) && !return_msg.equals("OK")) {
                    //log.error("统一下单错误!");
                    return "";
                }

                String prepay_Id = result.get("prepay_id");
                return prepay_Id;

            } else {
                return "";
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return "";
    }

    private PayInfo createPayInfo(String openId, String clientIP, String randomNonceStr) {

        Date date = new Date();
        String timeStart = TimeUtils.getFormatTime(date, Constant.TIME_FORMAT);
        String timeExpire = TimeUtils.getFormatTime(TimeUtils.addDay(date, Constant.TIME_EXPIRE), Constant.TIME_FORMAT);

        String randomOrderId = CommonUtil.getRandomOrderId();

        PayInfo payInfo = new PayInfo();
        payInfo.setAppid(Constant.APP_ID);
        payInfo.setMch_id(Constant.MCH_ID);
        payInfo.setDevice_info("WEB");
        payInfo.setNonce_str(randomNonceStr);
        payInfo.setSign_type("MD5");  //默认即为MD5
        payInfo.setBody("JSAPI支付测试");
        payInfo.setAttach("支付测试4luluteam");
        payInfo.setOut_trade_no(randomOrderId);
        payInfo.setTotal_fee(1);
        payInfo.setSpbill_create_ip(clientIP);
        payInfo.setTime_start(timeStart);
        payInfo.setTime_expire(timeExpire);
        payInfo.setNotify_url(Constant.URL_NOTIFY);
        payInfo.setTrade_type("JSAPI");
        payInfo.setLimit_pay("no_credit");
        payInfo.setOpenid(openId);

        return payInfo;
    }

    private String getSign(PayInfo payInfo) throws Exception {
        StringBuffer sb = new StringBuffer();
        sb.append("appid=" + payInfo.getAppid())
                .append("&attach=" + payInfo.getAttach())
                .append("&body=" + payInfo.getBody())
                .append("&device_info=" + payInfo.getDevice_info())
                .append("&limit_pay=" + payInfo.getLimit_pay())
                .append("&mch_id=" + payInfo.getMch_id())
                .append("&nonce_str=" + payInfo.getNonce_str())
                .append("&notify_url=" + payInfo.getNotify_url())
                .append("&openid=" + payInfo.getOpenid())
                .append("&out_trade_no=" + payInfo.getOut_trade_no())
                .append("&sign_type=" + payInfo.getSign_type())
                .append("&spbill_create_ip=" + payInfo.getSpbill_create_ip())
                .append("&time_expire=" + payInfo.getTime_expire())
                .append("&time_start=" + payInfo.getTime_start())
                .append("&total_fee=" + payInfo.getTotal_fee())
                .append("&trade_type=" + payInfo.getTrade_type())
                .append("&key=" + Constant.APP_KEY);

        log.error("排序后的拼接参数:" + sb.toString());

        return CommonUtil.getMD5(sb.toString().trim()).toUpperCase();
    }



}

小程序端通过wx.request发起网络请求,通过服务器发起预支付,获取prepayId以及其他支付需要签名的参数后,利用wx.requestPayment发起支付。

// 1. 完成页面结构、布局、样式
// 2. 设计数据结构
// 3. 完成数据绑定
// 4. 设计交互操作事件
// 5. 数据存储
var app = getApp()   //实例化小程序,从而获取全局数据或者使用全局函数
// console.log(app.globalData)
var MD5Util = require('../../utils/md5.js');

Page({
  // ===== 页面数据对象 =====
  data: {
    input: '',
    todos: [],
    leftCount: 0,
    allCompleted: false,
    logs: [],
    price: 0.01,
    number: 18820000000,
    deviceNo: 10080925
  },

  // ===== 页面生命周期方法 =====
  onLoad: function () {

  },
  // ===== 事件处理函数 =====
  wxPay: function (e) {
    var code = ''     //传给服务器以获得openId
    var timestamp = String(Date.parse(new Date()))   //时间戳
    var nonceStr = ''   //随机字符串,后台返回
    var prepayId = ''    //预支付id,后台返回
    var paySign = ''     //加密字符串

    //获取用户登录状态
    wx.login({
      success: function (res) {
        if (res.code) {
          code = res.code
          //发起网络请求,发起的是HTTPS请求,向服务端请求预支付
          wx.request({
            url: 'http://sellbin.natapp1.cc/prepay',
            data: {
              code: res.code
            },
            success: function (res) {
              console.log(res.data);
              if (res.data.result == true) {
                nonceStr = res.data.nonceStr
                prepayId = res.data.prepayId
                // 按照字段首字母排序组成新字符串
                var payDataA = "appId=" + app.globalData.appId + "&nonceStr=" + res.data.nonceStr + "&package=prepay_id=" + res.data.prepayId + "&signType=MD5&timeStamp=" + timestamp;
                var payDataB = payDataA + "&key=" + app.globalData.key;
                // 使用MD5加密算法计算加密字符串
                paySign = MD5Util.MD5(payDataB).toUpperCase();
                // 发起微信支付
                wx.requestPayment({
                  'timeStamp': timestamp,
                  'nonceStr': nonceStr,
                  'package': 'prepay_id=' + prepayId,
                  'signType': 'MD5',
                  'paySign': paySign,
                  'success': function (res) {
                    // 保留当前页面,跳转到应用内某个页面,使用wx.nevigeteBack可以返回原页面
                    wx.navigateTo({
                      url: '../pay/pay'
                    })
                  },
                  'fail': function (res) {
                    console.log(res.errMsg)
                  }
                })
              } else {
                console.log('请求失败' + res.data.info)
              }
            }
          })
        } else {
          console.log('获取用户登录态失败!' + res.errMsg)
        }
      }
    });
  },
   formSubmit: function (e) {
    console.log('form发生了submit事件,携带数据为:', e.detail.value)
  },
  formReset: function () {
    console.log('form发生了reset事件')
  }
})

接下来,我们贴一下微信退款相关的代码RefundController

package luluteam.wxpay.controller;

import luluteam.wxpay.constant.Constant;
import luluteam.wxpay.entity.WxRefundInfoEntity;
import luluteam.wxpay.service.WxRefundInfoService;
import luluteam.wxpay.util.common.PayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import java.text.DecimalFormat;
import java.util.*;

@Controller
public class RefundController extends HttpServlet {

    private static Logger log = Logger.getLogger(PayController.class);

    @Autowired
    private WxRefundInfoService wxRefundInfoService;

    @RequestMapping(params = "refund", method = RequestMethod.POST)
    @Transactional
    public @ResponseBody
    Map<String, Object> refund(String openid, String orderId, HttpServletRequest request) {
        Map<String, Object> result = new HashMap<String, Object>();
        String currTime = PayUtils.getCurrTime();
        String strTime = currTime.substring(8, currTime.length());
        String strRandom = PayUtils.buildRandom(4) + "";
        String nonceStr = strTime + strRandom;
        String outRefundNo = "wx@re@" + PayUtils.getTimeStamp();
        String outTradeNo = "";
        String transactionId = "";

        String unionId = openid;
        String appid = Constant.APP_ID;
        String mchid = Constant.MCH_ID;
        String key =  Constant.APP_KEY;//mch_key
//        String key = ResourceUtil.getConfigByName("wx.application.mch_key");
        if (StringUtils.isNotEmpty(orderId)) {
            int total_fee = 1;
            //商户侧传给微信的订单号32位
            outTradeNo = "115151sdasdsadsadsadas";
            DecimalFormat df = new DecimalFormat("0.00");
            //String fee = String.valueOf(df.format((float)total_fee/100));
            String fee = String.valueOf(total_fee);
            SortedMap<String, String> packageParams = new TreeMap<String, String>();
            packageParams.put("appid", appid);
            packageParams.put("mch_id", mchid);//微信支付分配的商户号
            packageParams.put("nonce_str", nonceStr);//随机字符串,不长于32位
            packageParams.put("op_user_id", mchid);//操作员帐号, 默认为商户号
            //out_refund_no只能含有数字、字母和字符_-|*@
            packageParams.put("out_refund_no", outRefundNo);//商户系统内部的退款单号,商户系统内部唯一,同一退款单号多次请求只退一笔
            packageParams.put("out_trade_no", outTradeNo);//商户侧传给微信的订单号32位
            packageParams.put("refund_fee", fee);
            packageParams.put("total_fee", fee);
            packageParams.put("transaction_id", transactionId);//微信生成的订单号,在支付通知中有返回
            String sign = PayUtils.createSign(packageParams, key);

            String refundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund";
            String xmlParam = "<xml>" +
                    "<appid>" + appid + "</appid>" +
                    "<mch_id>" + mchid + "</mch_id>" +
                    "<nonce_str>" + nonceStr + "</nonce_str>" +
                    "<op_user_id>" + mchid + "</op_user_id>" +
                    "<out_refund_no>" + outRefundNo + "</out_refund_no>" +
                    "<out_trade_no>" + outTradeNo + "</out_trade_no>" +
                    "<refund_fee>" + fee + "</refund_fee>" +
                    "<total_fee>" + fee + "</total_fee>" +
                    "<transaction_id>" + transactionId + "</transaction_id>" +
                    "<sign>" + sign + "</sign>" +
                    "</xml>";
            log.info("---------xml返回:" + xmlParam);
            String resultStr = PayUtils.post(refundUrl, xmlParam);
            log.info("---------退款返回:" + resultStr);
            //解析结果
            try {
                Map map = PayUtils.doXMLParse(resultStr);
                String returnCode = map.get("return_code").toString();
                if (returnCode.equals("SUCCESS")) {
                    String resultCode = map.get("result_code").toString();
                    if (resultCode.equals("SUCCESS")) {
                        //保存退款记录,可在数据库建一个退款表记录
                        WxRefundInfoEntity refundInfoEntity = new WxRefundInfoEntity();
                        refundInfoEntity.setCreateDate(new Date());
                        refundInfoEntity.setAppid(appid);
                        refundInfoEntity.setMchId(mchid);
                        refundInfoEntity.setNonceStr(nonceStr);
                        refundInfoEntity.setSign(sign);
                        refundInfoEntity.setOutRefundNo(outRefundNo);
                        refundInfoEntity.setOutTradeNo(outTradeNo);
                        refundInfoEntity.setTotalFee(total_fee);
                        refundInfoEntity.setRefundFee(total_fee);
                        refundInfoEntity.setUnionid(unionId);
                        wxRefundInfoService.save(refundInfoEntity);
                        result.put("status", "success");
                    } else {
                        result.put("status", "fail");
                    }
                } else {
                    result.put("status", "fail");
                }
            } catch (Exception e) {
                e.printStackTrace();
                result.put("status", "fail");
            }
        }
        return result;
    }
}

支付与退款有关的工具类和实体类一并贴在下面,如果有不清楚的,可以去我的github上面下载源码:

https://github.com/wenbingshen/wechatpay

entity包:

package luluteam.wxpay.entity;

import java.io.Serializable;

public class PayInfo implements Serializable {

    private String appid;
    private String mch_id;
    private String device_info; //设备号,小程序传"WEB"
    private String nonce_str;
    private String sign;
    private String sign_type;  //签名类型
    private String body;
    //private String detail;
    private String attach;
    private String out_trade_no;
    private int total_fee;
    private String spbill_create_ip;
    private String time_start;
    private String time_expire;
    private String notify_url;
    private String trade_type; //交易类型,JSAPI
    private String limit_pay;  //指定支付方式,no_credit
    private String openid;

    public String getAppid() {
        return appid;
    }

    public void setAppid(String appid) {
        this.appid = appid;
    }

    public String getMch_id() {
        return mch_id;
    }

    public void setMch_id(String mch_id) {
        this.mch_id = mch_id;
    }

    public String getDevice_info() {
        return device_info;
    }

    public void setDevice_info(String device_info) {
        this.device_info = device_info;
    }

    public String getNonce_str() {
        return nonce_str;
    }

    public void setNonce_str(String nonce_str) {
        this.nonce_str = nonce_str;
    }

    public String getSign() {
        return sign;
    }

    public void setSign(String sign) {
        this.sign = sign;
    }

    public String getSign_type() {
        return sign_type;
    }

    public void setSign_type(String sign_type) {
        this.sign_type = sign_type;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }

    public String getAttach() {
        return attach;
    }

    public void setAttach(String attach) {
        this.attach = attach;
    }

    public String getOut_trade_no() {
        return out_trade_no;
    }

    public void setOut_trade_no(String out_trade_no) {
        this.out_trade_no = out_trade_no;
    }

    public int getTotal_fee() {
        return total_fee;
    }

    public void setTotal_fee(int total_fee) {
        this.total_fee = total_fee;
    }

    public String getSpbill_create_ip() {
        return spbill_create_ip;
    }

    public void setSpbill_create_ip(String spbill_create_ip) {
        this.spbill_create_ip = spbill_create_ip;
    }

    public String getTime_start() {
        return time_start;
    }

    public void setTime_start(String time_start) {
        this.time_start = time_start;
    }

    public String getTime_expire() {
        return time_expire;
    }

    public void setTime_expire(String time_expire) {
        this.time_expire = time_expire;
    }

    public String getNotify_url() {
        return notify_url;
    }

    public void setNotify_url(String notify_url) {
        this.notify_url = notify_url;
    }

    public String getTrade_type() {
        return trade_type;
    }

    public void setTrade_type(String trade_type) {
        this.trade_type = trade_type;
    }

    public String getLimit_pay() {
        return limit_pay;
    }

    public void setLimit_pay(String limit_pay) {
        this.limit_pay = limit_pay;
    }

    public String getOpenid() {
        return openid;
    }

    public void setOpenid(String openid) {
        this.openid = openid;
    }
}
WxRefundInfoEntity类,用来退款后向数据添加退款的记录,方面查账:
package luluteam.wxpay.entity;

import javax.persistence.Entity;
import java.io.Serializable;
import java.util.Date;

@Entity
public class WxRefundInfoEntity implements Serializable {
    private Date createDate;

    private String appid;

    private String mchId;

    private String nonceStr;

    private String sign;

    private String outRefundNo;

    private String outTradeNo;

    private int totalFee;

    private int refundFee;

    private String unionid;

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public String getAppid() {
        return appid;
    }

    public void setAppid(String appid) {
        this.appid = appid;
    }

    public String getMchId() {
        return mchId;
    }

    public void setMchId(String mchId) {
        this.mchId = mchId;
    }

    public String getNonceStr() {
        return nonceStr;
    }

    public void setNonceStr(String nonceStr) {
        this.nonceStr = nonceStr;
    }

    public String getSign() {
        return sign;
    }

    public void setSign(String sign) {
        this.sign = sign;
    }

    public String getOutRefundNo() {
        return outRefundNo;
    }

    public void setOutRefundNo(String outRefundNo) {
        this.outRefundNo = outRefundNo;
    }

    public String getOutTradeNo() {
        return outTradeNo;
    }

    public void setOutTradeNo(String outTradeNo) {
        this.outTradeNo = outTradeNo;
    }

    public int getTotalFee() {
        return totalFee;
    }

    public void setTotalFee(int totalFee) {
        this.totalFee = totalFee;
    }

    public int getRefundFee() {
        return refundFee;
    }

    public void setRefundFee(int refundFee) {
        this.refundFee = refundFee;
    }

    public String getUnionid() {
        return unionid;
    }

    public void setUnionid(String unionid) {
        this.unionid = unionid;
    }
}

util包:

package luluteam.wxpay.util.common;

import java.security.MessageDigest;

public class MD5 {
    private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7",
            "8", "9", "a", "b", "c", "d", "e", "f"};

    /**
     * 转换字节数组为16进制字串
     * @param b 字节数组
     * @return 16进制字串
     */
    public static String byteArrayToHexString(byte[] b) {
        StringBuilder resultSb = new StringBuilder();
        for (byte aB : b) {
            resultSb.append(byteToHexString(aB));
        }
        return resultSb.toString();
    }

    /**
     * 转换byte到16进制
     * @param b 要转换的byte
     * @return 16进制格式
     */
    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0) {
            n = 256 + n;
        }
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }

    /**
     * MD5编码
     * @param origin 原始字符串
     * @return 经过MD5加密之后的结果
     */
    public static String MD5Encode(String origin) {
        String resultString = null;
        try {
            resultString = origin;
            MessageDigest md = MessageDigest.getInstance("MD5");
            resultString = byteArrayToHexString(md.digest(resultString.getBytes("utf-8")));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultString;
    }

}
package luluteam.wxpay.util.common;import java.io.BufferedReader;import java.io.ByteArrayInputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.UnsupportedEncodingException;import java.net.URLEncoder;import java.security.KeyStore;import java.security.MessageDigest;import java.text.SimpleDateFormat;import java.util.Date;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.Random;import java.util.Set;import java.util.SortedMap;import java.util.TreeMap;import javax.net.ssl.SSLContext;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import luluteam.wxpay.constant.Constant;import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.conn.HttpClientConnectionManager;import org.apache.http.conn.ssl.SSLConnectionSocketFactory;import org.apache.http.entity.StringEntity;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.ssl.SSLContexts;import org.apache.http.util.EntityUtils;import org.jdom.Document;import org.jdom.Element;import org.jdom.input.SAXBuilder;@SuppressWarnings("deprecation")public class PayUtils {    private static Object Server;    @SuppressWarnings("deprecation")    public static DefaultHttpClient httpclient;    private static SortedMap parameters;    static {        httpclient = new DefaultHttpClient();        // httpclient = (DefaultHttpClient) HttpClientConnectionManager.getSSLInstance(httpclient);        parameters = new TreeMap();    }    public static final String KEY_PATH = "E:/wxzf/cert/apiclient_cert.p12";    /**     * 把对象转换成字符串     *     * @param obj     * @return String 转换成字符串,若对象为null,则返回空字符串.     */    public static String toString(Object obj) {        if (obj == null)            return "";        return obj.toString();    }    /**     * 把对象转换为int数值.     *     * @param obj     *            包含数字的对象.     * @return int 转换后的数值,对不能转换的对象返回0。     */    public static int toInt(Object obj) {        int a = 0;        try {            if (obj != null) {                a = Integer.parseInt(obj.toString());            }        } catch (Exception e) {            e.printStackTrace();        }        return a;    }    /**     * 获取从1970年开始到现在的秒数     *     * @param     * @return     */    public static String getTimeStamp() {        long seconds = System.currentTimeMillis() / 1000;        return String.valueOf(seconds);    }    /**     * 获取当前时间 yyyyMMddHHmmss     * @return String     */    public static String getCurrTime() {        Date now = new Date();        SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");        String s = outFormat.format(now);        return s;    }    /**     * 获取当前日期 yyyyMMdd     * @param date     * @return String     */    public static String formatDate(Date date) {        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");        String strDate = formatter.format(date);        return strDate;    }    /**     * 取出一个指定长度大小的随机正整数.     * @param length  int 设定所取出随机数的长度。length小于11     * @return int 返回生成的随机数。     */    public static int buildRandom(int length) {        int num = 1;        double random = Math.random();        if (random < 0.1) {            random = random + 0.1;        }        for (int i = 0; i < length; i++) {            num = num * 10;        }        return (int) ((random * num));    }    /**     * 获取编码字符集     * @param request     * @param response     * @return String     */    public static String getCharacterEncoding(HttpServletRequest request, HttpServletResponse response) {        if (null == request || null == response) {            return "utf-8";        }        String enc = request.getCharacterEncoding();        if (null == enc || "".equals(enc)) {            enc = response.getCharacterEncoding();        }        if (null == enc || "".equals(enc)) {            enc = "utf-8";        }        return enc;    }    public static String URLencode(String content) {        String URLencode;        URLencode = replace(Server.equals(content), "+", "%20");        return URLencode;    }    private static String replace(boolean equals, String string, String string2) {        return null;    }    /**     * 获取unix时间,从1970-01-01 00:00:00开始的秒数     * @param date     * @return long     */    public static long getUnixTime(Date date) {        if (null == date) {            return 0;        }        return date.getTime() / 1000;    }    public static String QRfromGoogle(String chl) {        int widhtHeight = 300;        String EC_level = "L";        int margin = 0;        String QRfromGoogle;        chl = URLencode(chl);        QRfromGoogle = "http://chart.apis.google.com/chart?chs=" + widhtHeight + "x" + widhtHeight + "&cht=qr&chld="                + EC_level + "|" + margin + "&chl=" + chl;        return QRfromGoogle;    }    /**     * 时间转换成字符串     * @param date  时间     * @param formatType  格式化类型     * @return String     */    public static String date2String(Date date, String formatType) {        SimpleDateFormat sdf = new SimpleDateFormat(formatType);        return sdf.format(date);    }    /**     * 创建签名SHA1     * @param signParams     * @return     * @throws Exception     */    public static String createSHA1Sign(SortedMap<String, String> signParams) throws Exception {        StringBuffer sb = new StringBuffer();        Set es = signParams.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();            sb.append(k + "=" + v + "&");            // 要采用URLENCODER的原始值!        }        String params = sb.substring(0, sb.lastIndexOf("&"));        return getSha1(params);    }    /**     * Sha1签名     * @param str     * @return     */    public static String getSha1(String str) {        if (str == null || str.length() == 0) {            return null;        }        char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };        try {            MessageDigest mdTemp = MessageDigest.getInstance("SHA1");            mdTemp.update(str.getBytes("UTF-8"));            byte[] md = mdTemp.digest();            int j = md.length;            char buf[] = new char[j * 2];            int k = 0;            for (int i = 0; i < j; i++) {                byte byte0 = md[i];                buf[k++] = hexDigits[byte0 >>> 4 & 0xf];                buf[k++] = hexDigits[byte0 & 0xf];            }            return new String(buf);        } catch (Exception e) {            e.printStackTrace();            return null;        }    }    /**     * 获得预支付订单号     * @param url     * @param xmlParam     * @return     */    public static String getPayNo(String url, String xmlParam) {        String prepay_id = "";        try {            String jsonStr = postWithXmlParams(url, xmlParam);            if (jsonStr.indexOf("FAIL") != -1) {                return prepay_id;            }            Map<String, Object> map = doXMLParse(jsonStr);            prepay_id = (String) map.get("prepay_id");            System.out.println("prepay_id:" + prepay_id);        } catch (Exception e) {            e.printStackTrace();        }        return prepay_id;    }    /**     * 发送请求     * @param url 请求路径     * @param xmlParams xml字符串     * @return     */    public static String postWithXmlParams(String url, String xmlParams) {        HttpPost httpost = new HttpPost(url);        try {            httpost.setEntity(new StringEntity(xmlParams, "UTF-8"));            HttpResponse response = httpclient.execute(httpost);            return EntityUtils.toString(response.getEntity(), "UTF-8");        } catch (Exception e) {            e.printStackTrace();            return "";        }    }    /**     * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。     * @param strxml     * @return     * @throws IOException     */    public static Map doXMLParse(String strxml) throws Exception {        if (null == strxml || "".equals(strxml)) {            return null;        }        Map m = new HashMap();        InputStream in = String2Inputstream(strxml);        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 = PayUtils.getChildrenText(children);            }            m.put(k, v);        }        // 关闭流        in.close();        return m;    }    /**     * 获取子结点的xml     * @param children     * @return String     */    public static String getChildrenText(List children) {        StringBuffer sb = new StringBuffer();        if(!children.isEmpty()) {            Iterator it = children.iterator();            while(it.hasNext()) {                Element e = (Element) it.next();                String name = e.getName();                String value = e.getTextNormalize();                List list = e.getChildren();                sb.append("<" + name + ">");                if(!list.isEmpty()) {                    sb.append(PayUtils.getChildrenText(list));                }                sb.append(value);                sb.append("</" + name + ">");            }        }        return sb.toString();    }    public static InputStream String2Inputstream(String str) {        return new ByteArrayInputStream(str.getBytes());    }    public String getParameter(String parameter) {        String s = (String) this.parameters.get(parameter);        return (null == s) ? "" : s;    }    /**     * 特殊字符处理     * @param src     * @return     * @throws UnsupportedEncodingException     */    public String UrlEncode(String src) throws UnsupportedEncodingException {        return URLEncoder.encode(src, "UTF-8").replace("+", "%20");    }    /**     * 获取package的签名包     * @param packageParams     * @param key     * @return     * @throws UnsupportedEncodingException     */    public String genPackage(SortedMap<String, String> packageParams, String key) throws UnsupportedEncodingException {        String sign = createSign(packageParams, key);        StringBuffer sb = new StringBuffer();        Set es = packageParams.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();            sb.append(k + "=" + UrlEncode(v) + "&");        }        // 去掉最后一个&        String packageValue = sb.append("sign=" + sign).toString();        return packageValue;    }    /**     * 创建md5摘要,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。     */    public static String createSign(SortedMap<String, String> packageParams, String key) {        StringBuffer sb = new StringBuffer();        Set es = packageParams.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 (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {                sb.append(k + "=" + v + "&");            }        }        sb.append("key=" + key);        System.out.println("md5:" + sb.toString());        String sign =  MD5.MD5Encode(sb.toString()).toUpperCase();        System.out.println("packge签名:" + sign);        return sign;    }    /**     * 创建package签名     */    public boolean createMd5Sign(String signParams) {        StringBuffer sb = new StringBuffer();        Set es = this.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 (!"sign".equals(k) && null != v && !"".equals(v)) {                sb.append(k + "=" + v + "&");            }        }        // 算出摘要        String sign = MD5.MD5Encode(sb.toString()).toUpperCase();        String paySign = this.getParameter("sign").toLowerCase();        return paySign.equals(sign);    }    /**     * 输出XML     * @return     */    public String parseXML() {        StringBuffer sb = new StringBuffer();        sb.append("<xml>");        Set es = this.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 (null != v && !"".equals(v) && !"appkey".equals(k)) {                sb.append("<" + k + ">" + getParameter(k) + "</" + k + ">\n");            }        }        sb.append("</xml>");        return sb.toString();    }    public static String post(String url, String xmlParam) {        StringBuilder sb = new StringBuilder();        try {            KeyStore keyStore = KeyStore.getInstance("PKCS12");            FileInputStream instream = new FileInputStream(new File(KEY_PATH));//            String mchid = ResourceUtil.getConfigByName("wx.application.mch_id");            String mchid = Constant.MCH_ID;            try {                keyStore.load(instream, mchid.toCharArray());            } finally {                instream.close();            }            // 证书            SSLContext sslcontext = SSLContexts.custom()                    .loadKeyMaterial(keyStore, mchid.toCharArray())                    .build();            // 只允许TLSv1协议            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(                    sslcontext,                    new String[]{"TLSv1"},                    null,                    SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);            //创建基于证书的httpClient,后面要用到            CloseableHttpClient client = HttpClients.custom()                    .setSSLSocketFactory(sslsf)                    .build();            HttpPost httpPost = new HttpPost(url);//退款接口            StringEntity reqEntity = new StringEntity(xmlParam);            // 设置类型            reqEntity.setContentType("application/x-www-form-urlencoded");            httpPost.setEntity(reqEntity);            CloseableHttpResponse response = client.execute(httpPost);            try {                HttpEntity entity = response.getEntity();                System.out.println(response.getStatusLine());                if (entity != null) {                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));                    String text = "";                    while ((text = bufferedReader.readLine()) != null) {                        sb.append(text);                    }                }                EntityUtils.consume(entity);            } catch (Exception e) {                e.printStackTrace();            } finally {                try {                    response.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            /*try {                HttpGet httpget = new HttpGet("https://api.mch.weixin.qq.com/secapi/pay/refund");                System.out.println("executing request" + httpget.getRequestLine());                CloseableHttpResponse response = httpclient.execute(httpget);                try {                    HttpEntity entity = response.getEntity();                    System.out.println("----------------------------------------");                    System.out.println(response.getStatusLine());                    if (entity != null) {                        System.out.println("Response content lengt
10-06 22:14