首页 > 技术文章 > 微信App支付

lmcc 2017-12-06 16:03 原文

微信开发文档地址:https://pay.weixin.qq.com/wiki/doc/api/index.html

应用场景

商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易回话标识后再在APP里面调起支付。

接口链接

URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder

是否需要证书

不需要

  1 package com.kpcx.pay.weixin;
  2 
  3 import java.io.IOException;
  4 import java.net.URLDecoder;
  5 import java.util.ResourceBundle;
  6 import java.util.SortedMap;
  7 import java.util.TreeMap;
  8 import javax.servlet.ServletException;
  9 import javax.servlet.annotation.WebServlet;
 10 import javax.servlet.http.HttpServlet;
 11 import javax.servlet.http.HttpServletRequest;
 12 import javax.servlet.http.HttpServletResponse;
 13 import org.dom4j.DocumentException;
 14 import com.alibaba.fastjson.JSONObject;
 15 import com.kpcx.controller.NotifyController;
 16 import com.kpcx.pay.weixin.entity.OrderCreateRequest;
 17 import com.kpcx.util.HttpXmlUtils;
 18 import com.kpcx.util.RandCharsUtils;
 19 import com.kpcx.util.Tool;
 20 import com.kpcx.util.WXSignUtils;
 21 
 22 /**
 23  * 微信创建订单接口
 24  * 先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易回话标识后再在APP里面调起支付
 25  */
 26 @WebServlet("/create/WxCreate")
 27 public class WxOrderCreate extends HttpServlet {
 28     private static final long serialVersionUID = 1L;
 29     
 30 
 31     /**
 32      * 微信创建订单接口
 33      */
 34     protected void doGet(HttpServletRequest request, HttpServletResponse response)
 35             throws ServletException, IOException {
 36         // 接受参数
 37         String param = URLDecoder.decode(request.getParameter("param"),"UTF-8");
 38         // 转成javabean
 39         OrderCreateRequest orderCreate = JSONObject.parseObject(param, OrderCreateRequest.class);
 40         NotifyController nController = new NotifyController();
 41         String payMoney = Tool.fenToYuan(orderCreate.getTotal_fee());
 42         //校验订单
 43         String checkOrders = nController.checkOrders(orderCreate.getOut_trade_no(), payMoney);
 44         JSONObject checkData = JSONObject.parseObject(checkOrders);
 45         String code = checkData.getString("Code");
 46         //订单校验通过
 47         if(code != null && "0".equals(code)){
 48             // 参数:开始生成签名
 49             // 获取配置文件
 50             ResourceBundle resource = ResourceBundle.getBundle("weixin");
 51             //随机字符串
 52             String nonce_str = RandCharsUtils.getRandomString(16);
 53             //订单开始时间
 54             String timeStart = RandCharsUtils.timeStart();
 55             //订单结束时间
 56             String timeExpire = RandCharsUtils.timeExpire();
 57             
 58             SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
 59             parameters.put("appid", resource.getString("appid"));
 60             parameters.put("mch_id", resource.getString("mch_id"));
 61             parameters.put("nonce_str", nonce_str);
 62             parameters.put("body", orderCreate.getBody());
 63             parameters.put("detail", orderCreate.getDetail());
 64             parameters.put("attach", orderCreate.getAttach());
 65             parameters.put("out_trade_no", orderCreate.getOut_trade_no());
 66             parameters.put("total_fee", orderCreate.getTotal_fee());
 67             parameters.put("time_start", timeStart);
 68             parameters.put("time_expire", timeExpire);
 69             parameters.put("notify_url", resource.getString("notify_url"));
 70             parameters.put("trade_type", orderCreate.getTrade_type());
 71             parameters.put("spbill_create_ip", orderCreate.getSpbill_create_ip());
 72             parameters.put("scene_info", orderCreate.getScene_info());
 73             //生成微信签名
 74             String sign = WXSignUtils.createSign("UTF-8", parameters);
 75 
 76             // 参数转换成xml格式
 77             orderCreate.setAppid(resource.getString("appid"));
 78             orderCreate.setMch_id(resource.getString("mch_id"));
 79             orderCreate.setNonce_str(nonce_str);
 80             orderCreate.setTime_start(timeStart);
 81             orderCreate.setTime_expire(timeExpire);
 82             orderCreate.setNotify_url(resource.getString("notify_url"));
 83             orderCreate.setSign(sign);
 84             
 85             //参数转换成xml格式
 86             String xmlInfo = HttpXmlUtils.xmlInfo(orderCreate);
 87             String method = "POST";
 88             //请求微信下单     返回值是xml类型的字符串
 89             String json = HttpXmlUtils.httpsRequest(resource.getString("wxUrl"), method, xmlInfo).toString();
 90             //xml 转json     
 91             String result = "";
 92             try {
 93                 result = Tool.xmlChangeJson(json);
 94             } catch (DocumentException e) {
 95                 // TODO Auto-generated catch block
 96                 e.printStackTrace();
 97             }
 98             JSONObject resultTemp = new JSONObject();
 99             JSONObject object = JSONObject.parseObject(result);
100             if("SUCCESS".equals(object.getString("return_code"))){
101                 object.put("createTime", System.currentTimeMillis()/1000);
102                 object.put("package", "Sign=WXPay");
103                 //生成签名给前端
104                 SortedMap<Object,Object> parameters1 = new TreeMap<Object,Object>();
105                 parameters1.put("appid", object.getString("appid"));
106                 parameters1.put("noncestr", object.getString("nonce_str"));
107                 parameters1.put("package",object.getString("package"));
108                 parameters1.put("partnerid", object.getString("mch_id"));
109                 parameters1.put("prepayid", object.getString("prepay_id"));
110                 parameters1.put("timestamp",object.getString("createTime"));
111                 sign = WXSignUtils.createSign("UTF-8", parameters1);
112                 //转化返回值的格式
113                 resultTemp.put("Code", "0");
114                 object.put("sign", sign);
115                 resultTemp.put("Result", object);
116             }else{
117                 resultTemp.put("Code", "-1");
118                 resultTemp.put("Result", object);
119             }
120             response.getWriter().write(resultTemp.toJSONString());
121             //结果返回到filter,记录到日志中
122             request.setAttribute("outStr", resultTemp.toJSONString());
123         }else{
124             response.getWriter().write(checkOrders);
125             //结果返回到filter,记录到日志中
126             request.setAttribute("outStr", checkOrders);
127         }
128         
129     }
130 
131     /**
132      * post方法
133      */
134     protected void doPost(HttpServletRequest request, HttpServletResponse response)
135             throws ServletException, IOException {
136         // TODO Auto-generated method stub
137         doGet(request, response);
138     }
139 
140 }
package com.kpcx.util;

import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESKeySpec; import javax.crypto.spec.IvParameterSpec; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import org.dom4j.Element; public class Tool {// 这个方法是将xml字符串转成Json public static String xmlChangeJson(String XML) throws DocumentException { Document document = DocumentHelper.parseText(XML); Element root = document.getRootElement(); Iterator it = root.elementIterator(); String json = "{"; while (it.hasNext()) { Element element = (Element) it.next(); String j = checkChildEle(element); if (j == "") { json += "\"" + element.getName() + "\":\"" + element.getText() + "\","; } else { json += j; } } json = json.substring(0, json.length() - 1); json += "}"; return json; } /** * * 功能描述:金额字符串转换:单位分转成单元 * * @param str * 传入需要转换的金额字符串 * @return 转换后的金额字符串 */ public static String fenToYuan(Object o) { if (o == null) { return "0.00"; } String s = o.toString(); int len = -1; StringBuilder sb = new StringBuilder(); if (s != null && s.trim().length() > 0 && !s.equalsIgnoreCase("null")) { s = removeZero(s); if (s != null && s.trim().length() > 0 && !s.equalsIgnoreCase("null")) { len = s.length(); int tmp = s.indexOf("-"); if (tmp >= 0) { if (len == 2) { sb.append("-0.0").append(s.substring(1)); } else if (len == 3) { sb.append("-0.").append(s.substring(1)); } else { sb.append(s.substring(0, len - 2)).append(".").append(s.substring(len - 2)); } } else { if (len == 1) { sb.append("0.0").append(s); } else if (len == 2) { sb.append("0.").append(s); } else { sb.append(s.substring(0, len - 2)).append(".").append(s.substring(len - 2)); } } } else { sb.append("0.00"); } } else { sb.append("0.00"); } return sb.toString(); } /** * * 功能描述:金额字符串转换:单位元转成单分 * * @param str * 传入需要转换的金额字符串 * @return 转换后的金额字符串 */ public static String yuanToFen(Object o) { if (o == null) return "0"; String s = o.toString(); int posIndex = -1; String str = ""; StringBuilder sb = new StringBuilder(); if (s != null && s.trim().length() > 0 && !s.equalsIgnoreCase("null")) { posIndex = s.indexOf("."); if (posIndex > 0) { int len = s.length(); if (len == posIndex + 1) { str = s.substring(0, posIndex); if (str == "0") { str = ""; } sb.append(str).append("00"); } else if (len == posIndex + 2) { str = s.substring(0, posIndex); if (str == "0") { str = ""; } sb.append(str).append(s.substring(posIndex + 1, posIndex + 2)).append("0"); } else if (len == posIndex + 3) { str = s.substring(0, posIndex); if (str == "0") { str = ""; } sb.append(str).append(s.substring(posIndex + 1, posIndex + 3)); } else { str = s.substring(0, posIndex); if (str == "0") { str = ""; } sb.append(str).append(s.substring(posIndex + 1, posIndex + 3)); } } else { sb.append(s).append("00"); } } else { sb.append("0"); } str = removeZero(sb.toString()); if (str != null && str.trim().length() > 0 && !str.trim().equalsIgnoreCase("null")) { return str; } else { return "0"; } } /** * * 功能描述:去除字符串首部为"0"字符 * * @param str * 传入需要转换的字符串 * @return 转换后的字符串 */ public static String removeZero(String str) { char ch; String result = ""; if (str != null && str.trim().length() > 0 && !str.trim().equalsIgnoreCase("null")) { try { for (int i = 0; i < str.length(); i++) { ch = str.charAt(i); if (ch != '0') { result = str.substring(i); break; } } } catch (Exception e) { result = ""; } } else { result = ""; } return result; } /** * @Title: 获取sign加密时需要的字符串 * @author: Administrator * @param: @param t * @return:String */ public static <T> String getSignParam(T t) throws IllegalArgumentException, IllegalAccessException { List<String> names = new ArrayList<>(); Map<String, Object> maps = new HashMap<>(); // ----------------参数名变小写 升序排序----------------- Class c = t.getClass(); Field[] fs = c.getDeclaredFields(); for (int i = 0; i < fs.length; i++) { Field f = fs[i]; f.setAccessible(true); // 设置些属性是可以访问的 String name = f.getName(); Object val = f.get(t); if (val != null && !"".equals(val) && !"null".equals(val)) { name = name.toLowerCase(); names.add(name); maps.put(name, val); } } Collections.sort(names); StringBuffer strAsc = new StringBuffer();// 按照参数升序后的字符串 for (int i = 0; i < names.size(); i++) { if (i != names.size() - 1) { strAsc.append(names.get(i) + "=" + maps.get(names.get(i)) + "&"); } else { strAsc.append(names.get(i) + "=" + maps.get(names.get(i))); } } return strAsc.toString(); } }
package com.kpcx.pay.weixin.entity;

/**
 * 统一下单提交为微信的参数
 * @author iYjrg_xiebin
 * @date 2015年11月26日上午10:17:06
 */
public class OrderCreateRequest {
    
    private String appid;//微信分配的公众账号ID(企业号corpid即为此appId),例如:wxd678efh567hg6787
    private String mch_id;//商户id
    private String device_info;//终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB"
    private String nonce_str;//随机字符串:数字+大写字母的组合,32位
    private String sign;//签名
    private String body;//商品或支付单简要描述
    private String detail;//商品名称明细列表
    private String attach;//附加参数
    private String out_trade_no;//商户系统内部的订单号
    private String fee_type;//货币类型:符合ISO 4217标准的三位字母代码,默认人民币:CNY
    private int total_fee;//总金额
    private String spbill_create_ip;//APP和网页支付提交[用户端ip],Native支付填调用微信支付API的机器IP。
    private String time_start;//订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010
    private String time_expire;//订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010;最短失效时间间隔必须大于5分钟[支付宝是30分钟,同样30分钟]
    private String goods_tag;//商品标记,代金券或立减优惠功能的参数
    private String notify_url;//接收微信支付异步通知回调地址
    private String trade_type;//交易类型:JSAPI,NATIVE,APP
    private String product_id;//trade_type=NATIVE,此参数必传。此id为二维码中包含的商品ID,商户自行定义。
    private String limit_pay;//no_credit--指定不能使用信用卡支付
    private String openid;//trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识
    private String scene_info;//上报支付的场景信息
    
    public String getAppid() {
        return appid;
    }
    public String getMch_id() {
        return mch_id;
    }
    public String getDevice_info() {
        return device_info;
    }
    public String getNonce_str() {
        return nonce_str;
    }
    public String getSign() {
        return sign;
    }
    public String getBody() {
        return body;
    }
    public String getDetail() {
        return detail;
    }
    public String getAttach() {
        return attach;
    }
    public String getOut_trade_no() {
        return out_trade_no;
    }
    public String getFee_type() {
        return fee_type;
    }
    public int getTotal_fee() {
        return total_fee;
    }
    public String getSpbill_create_ip() {
        return spbill_create_ip;
    }
    public String getTime_start() {
        return time_start;
    }
    public String getTime_expire() {
        return time_expire;
    }
    public String getGoods_tag() {
        return goods_tag;
    }
    public String getNotify_url() {
        return notify_url;
    }
    public String getTrade_type() {
        return trade_type;
    }
    public String getProduct_id() {
        return product_id;
    }
    public String getLimit_pay() {
        return limit_pay;
    }
    public String getOpenid() {
        return openid;
    }
    public void setAppid(String appid) {
        this.appid = appid;
    }
    public void setMch_id(String mch_id) {
        this.mch_id = mch_id;
    }
    public void setDevice_info(String device_info) {
        this.device_info = device_info;
    }
    public void setNonce_str(String nonce_str) {
        this.nonce_str = nonce_str;
    }
    public void setSign(String sign) {
        this.sign = sign;
    }
    public void setBody(String body) {
        this.body = body;
    }
    public void setDetail(String detail) {
        this.detail = detail;
    }
    public void setAttach(String attach) {
        this.attach = attach;
    }
    public void setOut_trade_no(String out_trade_no) {
        this.out_trade_no = out_trade_no;
    }
    public void setFee_type(String fee_type) {
        this.fee_type = fee_type;
    }
    public void setTotal_fee(int total_fee) {
        this.total_fee = total_fee;
    }
    public void setSpbill_create_ip(String spbill_create_ip) {
        this.spbill_create_ip = spbill_create_ip;
    }
    public void setTime_start(String time_start) {
        this.time_start = time_start;
    }
    public void setTime_expire(String time_expire) {
        this.time_expire = time_expire;
    }
    public void setGoods_tag(String goods_tag) {
        this.goods_tag = goods_tag;
    }
    public void setNotify_url(String notify_url) {
        this.notify_url = notify_url;
    }
    public void setTrade_type(String trade_type) {
        this.trade_type = trade_type;
    }
    public void setProduct_id(String product_id) {
        this.product_id = product_id;
    }
    public void setLimit_pay(String limit_pay) {
        this.limit_pay = limit_pay;
    }
    public void setOpenid(String openid) {
        this.openid = openid;
    }
    public String getScene_info() {
        return scene_info;
    }
    public void setScene_info(String scene_info) {
        this.scene_info = scene_info;
    }
    

}
package com.kpcx.util;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * nonce_str随即字符串
 * @author iYjrg_xiebin
 * @date 2015年11月25日下午5:10:32
 */
public class RandCharsUtils {
    private static SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");

    public static String getRandomString(int length) { //length表示生成字符串的长度
        String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";   
        Random random = new Random();   
        StringBuffer sb = new StringBuffer();
        int number = 0;
        for (int i = 0; i < length; i++) {   
            number = random.nextInt(base.length());   
            sb.append(base.charAt(number));   
        }   
        return sb.toString();   
    }   
    
    /*
     * 订单开始交易的时间
     */
    public static String timeStart(){
        return df.format(new Date());
    }
    
    /*
     * 订单开始交易的时间
     */
    public static String timeExpire(){
        Calendar now=Calendar.getInstance();
        now.add(Calendar.MINUTE,30);
        return df.format(now.getTimeInMillis());
    }
    


    public static void main(String[] args) {
        /*for (int i = 0; i < 10; i++) {
            System.out.println("第"+i+"次是:"+getRandomString(32));
        }*/
        
        System.out.println(getRandomString(16));
//        System.out.println("开始时间是:"+timeStart());
//        System.out.println("开始时间是:"+timeExpire());
    }
    /**
     * 获取编码字符集
     * @param request
     * @param response
     * @return String
     */

    public static String getCharacterEncoding(HttpServletRequest request,
            HttpServletResponse response) {
        
        if(null == request || null == response) {
            return "gbk";
        }
        
        String enc = request.getCharacterEncoding();
        if(null == enc || "".equals(enc)) {
            enc = response.getCharacterEncoding();
        }
        
        if(null == enc || "".equals(enc)) {
            enc = "gbk";
        }
        
        return enc;
    }

}
package com.kpcx.util;

import java.util.Iterator;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.SortedMap;
import com.kpcx.pay.weixin.entity.NotifyResult;

/**
 * 微信支付签名
 * @author iYjrg_xiebin
 * @date 2015年11月25日下午4:47:07
 */
public class WXSignUtils {
    //http://mch.weixin.qq.com/wiki/doc/api/index.php?chapter=4_3
    //商户Key:改成公司申请的即可
    //32位密码设置地址:http://www.sexauth.com/  jdex1hvufnm1sdcb0e81t36k0d0f15nc
    static ResourceBundle rb = ResourceBundle.getBundle("weixin");
    private static String Key = rb.getString("key");

    /**
     * 微信支付签名算法sign
     * @param characterEncoding
     * @param parameters
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序)
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            Object v = entry.getValue();
            if(null != v && !"".equals(v) 
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        
        sb.append("key=" + Key);
//        System.out.println("字符串拼接后是:"+sb.toString());
        String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
        return sign;
    }
    
    
    /**
     * @Title: 签名算法方法
     * @author: Administrator
     * @param: @param characterEncoding  字符编码
     * @param: @param notifyResult    回调参数实体类
     * @return:String
     */
    public static String createSign(String characterEncoding,NotifyResult notifyResult) throws IllegalArgumentException, IllegalAccessException{
        StringBuffer sb = new StringBuffer();
        String getValue = "";
        try {
            getValue = Tool.getSignParam(notifyResult);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        sb.append(getValue);
        sb.append("&key=" + Key);
//        System.out.println("字符串拼接后是:"+sb.toString());
        String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
        return sign;
    }

    

}
package com.kpcx.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
import com.kpcx.pay.weixin.entity.OrderCreateRequest;
import com.kpcx.pay.weixin.entity.OrderRefundCondition;

import net.sf.json.JSONObject;


/**
 * post提交xml格式的参数
 * @author 孟丛丛    
 * @date 2015年11月25日下午3:33:38
 */
public class HttpXmlUtils {

    /**
     * 开始post提交参数到接口
     * 并接受返回
     * @param url
     * @param xml
     * @param method
     * @param contentType
     * @return
     */
    public static String xmlHttpProxy(String url,String xml,String method,String contentType){
        InputStream is = null;
        OutputStreamWriter os = null;

        try {
            URL _url = new URL(url);
            HttpURLConnection conn = (HttpURLConnection) _url.openConnection();
            conn.setDoInput(true);   
            conn.setDoOutput(true);   
            conn.setRequestProperty("Content-type", "text/xml");
            conn.setRequestProperty("Pragma:", "no-cache");  
            conn.setRequestProperty("Cache-Control", "no-cache");  
            conn.setRequestMethod("POST");
            os = new OutputStreamWriter(conn.getOutputStream());
            os.write(new String(xml.getBytes(contentType)));
            os.flush();

            //返回值
            is = conn.getInputStream();
            return getContent(is, "utf-8");
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            try {
                if(os!=null){os.close();}
                if(is!=null){is.close();}
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 解析返回的值
     * @param is
     * @param charset
     * @return
     */
    public static String getContent(InputStream is, String charset) {
        String pageString = null;
        InputStreamReader isr = null;
        BufferedReader br = null;
        StringBuffer sb = null;
        try {
            isr = new InputStreamReader(is, charset);
            br = new BufferedReader(isr);
            sb = new StringBuffer();
            String line = null;
            while ((line = br.readLine()) != null) {
                sb.append(line + "\n");
            }
            pageString = sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null){
                    is.close();
                }
                if(isr!=null){
                    isr.close();
                }
                if(br!=null){
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            sb = null;
        }
        return pageString;
    }

    /**
     * 构造xml参数
     * @param xml
     * @return
     */
    public static String xmlInfo(OrderCreateRequest orderCreate){
        //构造xml参数的时候,至少又是个必传参数
        /*
         * <xml>
               <appid>wx2421b1c4370ec43b</appid>
               <attach>支付测试</attach>
               <body>JSAPI支付测试</body>
               <mch_id>10000100</mch_id>
               <nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
               <notify_url>http://wxpay.weixin.qq.com/pub_v2/pay/notify.v2.php</notify_url>
               <openid>oUpF8uMuAJO_M2pxb1Q9zNjWeS6o</openid>
               <out_trade_no>1415659990</out_trade_no>
               <spbill_create_ip>14.23.150.211</spbill_create_ip>
               <total_fee>1</total_fee>
               <trade_type>JSAPI</trade_type>
               <sign>0CB01533B8C1EF103065174F50BCA001</sign>
            </xml>
         */

        if(orderCreate!=null){
            StringBuffer bf = new StringBuffer();
            bf.append("<xml>");

            bf.append("<appid><![CDATA[");
            bf.append(orderCreate.getAppid());
            bf.append("]]></appid>");

            bf.append("<mch_id><![CDATA[");
            bf.append(orderCreate.getMch_id());
            bf.append("]]></mch_id>");

            bf.append("<nonce_str><![CDATA[");
            bf.append(orderCreate.getNonce_str());
            bf.append("]]></nonce_str>");

            bf.append("<sign><![CDATA[");
            bf.append(orderCreate.getSign());
            bf.append("]]></sign>");

            bf.append("<body><![CDATA[");
            bf.append(orderCreate.getBody());
            bf.append("]]></body>");

            bf.append("<detail><![CDATA[");
            bf.append(orderCreate.getDetail());
            bf.append("]]></detail>");

            bf.append("<attach><![CDATA[");
            bf.append(orderCreate.getAttach());
            bf.append("]]></attach>");

            bf.append("<out_trade_no><![CDATA[");
            bf.append(orderCreate.getOut_trade_no());
            bf.append("]]></out_trade_no>");

            bf.append("<total_fee><![CDATA[");
            bf.append(orderCreate.getTotal_fee());
            bf.append("]]></total_fee>");

            bf.append("<spbill_create_ip><![CDATA[");
            bf.append(orderCreate.getSpbill_create_ip());
            bf.append("]]></spbill_create_ip>");

            bf.append("<time_start><![CDATA[");
            bf.append(orderCreate.getTime_start());
            bf.append("]]></time_start>");

            bf.append("<time_expire><![CDATA[");
            bf.append(orderCreate.getTime_expire());
            bf.append("]]></time_expire>");

            bf.append("<notify_url><![CDATA[");
            bf.append(orderCreate.getNotify_url());
            bf.append("]]></notify_url>");

            bf.append("<trade_type><![CDATA[");
            bf.append(orderCreate.getTrade_type());
            bf.append("]]></trade_type>");
            bf.append("</xml>");
            
            return bf.toString();
        }

        return "";
    }
    
    /**
     * @Title: 查询订单请求参数转xml
     * @param: @param json
     */
    public static String toXml(JSONObject json){

        if(json!=null){
            StringBuffer bf = new StringBuffer();
            bf.append("<xml>");

            bf.append("<appid><![CDATA[");
            bf.append(json.getString("appid"));
            bf.append("]]></appid>");

            bf.append("<mch_id><![CDATA[");
            bf.append(json.getString("mch_id"));
            bf.append("]]></mch_id>");

            bf.append("<nonce_str><![CDATA[");
            bf.append(json.getString("nonce_str"));
            bf.append("]]></nonce_str>");

            bf.append("<sign><![CDATA[");
            bf.append(json.getString("sign"));
            bf.append("]]></sign>");

            bf.append("<out_trade_no><![CDATA[");
            bf.append(json.getString("out_trade_no"));
            bf.append("]]></out_trade_no>");
            
            bf.append("</xml>");
            return bf.toString();
        }
        return "";
    }
    
    
    /**
     * @Title: 退款接口请求数据转成xml方法
     * @author: gaohaijun
     * @param: @param orderRefund 退款接口参数实体类
     * @return:String
     * @date:2017年6月19日上午9:14:01
     */
    public static String RefundXmlInfo(OrderRefundCondition orderRefund){
        
        if(orderRefund != null){
            StringBuffer bf = new StringBuffer();
            bf.append("<xml>");
            
            bf.append("<appid><![CDATA[");
            bf.append(orderRefund.getAppid());
            bf.append("]]></appid>");
            
            bf.append("<mch_id><![CDATA[");
            bf.append(orderRefund.getMch_id());
            bf.append("]]></mch_id>");
            
            bf.append("<nonce_str><![CDATA[");
            bf.append(orderRefund.getNonce_str());
            bf.append("]]></nonce_str>");
            
            bf.append("<out_refund_no><![CDATA[");
            bf.append(orderRefund.getOut_refund_no());
            bf.append("]]></out_refund_no>");
            
            bf.append("<out_trade_no><![CDATA[");
            bf.append(orderRefund.getOut_trade_no());
            bf.append("]]></out_trade_no>");
            
            bf.append("<refund_fee><![CDATA[");
            bf.append(orderRefund.getRefund_fee());
            bf.append("]]></refund_fee>");
            
            bf.append("<total_fee><![CDATA[");
            bf.append(orderRefund.getTotal_fee());
            bf.append("]]></total_fee>");
            
            bf.append("<refund_desc><![CDATA[");
            bf.append(orderRefund.getRefund_desc());
            bf.append("]]></refund_desc>");
            
            bf.append("<sign><![CDATA[");
            bf.append(orderRefund.getSign());
            bf.append("]]></sign>");
            
            bf.append("</xml>");
            return bf.toString();
        }
        return "";
    }
    
    
    /**
     * post请求并得到返回结果
     * @param requestUrl
     * @param requestMethod
     * @param output
     * @return
     */
    public static String httpsRequest(String requestUrl, String requestMethod, String output) {
        try{
            URL url = new URL(requestUrl);
            HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setDoInput(true);
            connection.setUseCaches(false);
            connection.setRequestMethod(requestMethod);
            if (null != output) {
                OutputStream outputStream = connection.getOutputStream();
                outputStream.write(output.getBytes("UTF-8"));
                outputStream.close();
            }
            // 从输入流读取返回内容
            InputStream inputStream = connection.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            StringBuffer buffer = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            bufferedReader.close();
            inputStreamReader.close();
            inputStream.close();
            inputStream = null;
            connection.disconnect();
            return buffer.toString();
        }catch(Exception ex){
            ex.printStackTrace();
        }

        return "";
    }
    
    /** 
     * 回调后将结果返回给微信 
     * @param return_code 
     * @param return_msg 
     * @return 
     */  
    public static String backWeixin(String return_code,String return_msg){  
        try{  
            StringBuffer bf = new StringBuffer();  
            bf.append("<xml>");  
              
            bf.append("<return_code><![CDATA[");  
            bf.append(return_code);  
            bf.append("]]></return_code>");  
              
            bf.append("<return_msg><![CDATA[");  
            bf.append(return_msg);  
            bf.append("]]></return_msg>");  
              
            bf.append("</xml>");  
            return bf.toString();  
        }catch(Exception ex){  
            ex.printStackTrace();  
        }  
  
        return "";  
    }  
    /**
     * @Title: 查询对账订单请求参数转xml
     * @param: @param json
     */
    public static String toBillXml(JSONObject json){

        if(json!=null){
            StringBuffer bf = new StringBuffer();
            bf.append("<xml>");

            bf.append("<appid><![CDATA[");
            bf.append(json.getString("appid"));
            bf.append("]]></appid>");

            bf.append("<mch_id><![CDATA[");
            bf.append(json.getString("mch_id"));
            bf.append("]]></mch_id>");

            bf.append("<nonce_str><![CDATA[");
            bf.append(json.getString("nonce_str"));
            bf.append("]]></nonce_str>");

            bf.append("<sign><![CDATA[");
            bf.append(json.getString("sign"));
            bf.append("]]></sign>");

            bf.append("<bill_date><![CDATA[");
            bf.append(json.getString("bill_date"));
            bf.append("]]></bill_date>");
            
            bf.append("<bill_type><![CDATA[");
            bf.append(json.getString("bill_type"));
            bf.append("]]></bill_type>");
            
            bf.append("</xml>");
            return bf.toString();
        }
        return "";
    }

}

 二、微信回调

  

package com.kpcx.pay.weixin;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.log4j.MDC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.kpcx.controller.NotifyController;
import com.kpcx.controller.RefundController;
import com.kpcx.pay.weixin.entity.NotifyResult;
import com.kpcx.service.PaymentService;
import com.kpcx.service.impl.PaymentServiceImpl;
import com.kpcx.util.HttpXmlUtils;
import com.kpcx.util.JdomParseXmlUtils;
import com.kpcx.util.MybatisUtil;
import com.kpcx.util.Tool;
import com.kpcx.util.WXSignUtils;
import net.sf.json.JSONObject;

/**
 * 微信支付回调接口
 * 
 * @author 孟丛丛 支付成功后,微信会回调我们把支付结果和用户信息发给我们
 */
@WebServlet("/WxOrderNotify")
public class WxOrderNotifyNew extends HttpServlet {
    private static final long serialVersionUID = 1L;
    ResourceBundle resource = ResourceBundle.getBundle("jkUrl");

    /**
     * 
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        NotifyController nController = new NotifyController();
        Logger logger = LoggerFactory.getLogger(getClass());
        PaymentService paymentService = new PaymentServiceImpl();
        try {
            BufferedReader reader = request.getReader();
            String line = "";
            StringBuffer inputString = new StringBuffer();
            try {
                PrintWriter writer = response.getWriter();
                while ((line = reader.readLine()) != null) {
                    inputString.append(line);
                }
                if (reader != null) {
                    reader.close();
                }
                
                logger.info("------------------------[微信回调]接收到的报文-----" + inputString.toString() + "----------------------");
                if (!StringUtils.isEmpty(inputString.toString())) {
                    NotifyResult notifyResult = JdomParseXmlUtils.getWXPayResult(inputString.toString());
                    String wxSign = notifyResult.getSign();
                    notifyResult.setSign(null);
                    if ("SUCCESS".equals(notifyResult.getReturn_code()) && "SUCCESS".equals(notifyResult.getResult_code())) {
                        // 查询订单详情
                        String ticketType = JSONObject.fromObject(notifyResult.getAttach()).getString("ticketType");
                        com.alibaba.fastjson.JSONObject orders = nController.OrderDetail(notifyResult.getOut_trade_no(),Integer.parseInt(ticketType));
                        if (!"0".equals(orders.getString("Code"))) {
                            // 查不到这个订单
                            writer.write(HttpXmlUtils.backWeixin("FAIL", "无该订单信息"));
                        } else {
                             // 如果回调成功,进行服务器端订单处理
                            /**
                             * 一系列的判断处理
                             * 1、查看该订单是否是自己的单子
                             * 2、查看该订单的支付状态是否是未支付
                             * 3、查看该订单的金额和支付宝返回的金额是否一致
                             * 4、保存支付数据
                             * 5、更新支付状态
                             * 6、判断完成、插完支付数据自己想要做的操作
                             * 注意:4、5部要加同步锁,防止几个回调同时对数据的操作
                             * 
                             */

                                logger.info("返回微信标志---" + HttpXmlUtils.backWeixin("SUCCESS", "OK"));
                                writer.write(HttpXmlUtils.backWeixin("SUCCESS", "OK"));

                            } else {
                                writer.write(HttpXmlUtils.backWeixin("FAIL", "签名失败"));
                                logger.info("-------微信反校验签名【失败】");
                            }
                        }
                    } else {
                        writer.write(HttpXmlUtils.backWeixin("FAIL", notifyResult.getReturn_msg()));
                    }
                    if (writer != null) {
                        writer.close();
                    }
                } else {
                    writer.write(HttpXmlUtils.backWeixin("FAIL", "未获取到微信返回的结果"));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * post请求
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }

}
  1 package com.kpcx.util;
  2 
  3 import java.io.IOException;
  4 import java.io.StringReader;
  5 import java.util.List;
  6 import org.jdom.Document;
  7 import org.jdom.Element;
  8 import org.jdom.JDOMException;
  9 import org.jdom.input.SAXBuilder;
 10 import org.xml.sax.InputSource;
 11 import com.kpcx.pay.weixin.entity.OrderCreateResponse;
 12 import com.kpcx.pay.weixin.entity.NotifyResult;
 13 
 14 
 15 
 16 /**
 17  * 微信解析xml:带有CDATA格式的
 18  * @author iYjrg_xiebin
 19  * @date 2015年11月26日上午10:18:07
 20  */
 21 public class JdomParseXmlUtils {
 22     
 23     /**
 24      * 1、统一下单获取微信返回
 25      * 解析的时候自动去掉CDMA
 26      * @param xml
 27      */
 28     @SuppressWarnings("unchecked")
 29     public static OrderCreateResponse getUnifiedorderResult(String xml){
 30         OrderCreateResponse unifieorderResult = new OrderCreateResponse();
 31         try { 
 32             StringReader read = new StringReader(xml);
 33             // 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
 34             InputSource source = new InputSource(read);
 35             // 创建一个新的SAXBuilder
 36             SAXBuilder sb = new SAXBuilder();
 37             // 通过输入源构造一个Document
 38             Document doc;
 39             doc = (Document) sb.build(source);
 40 
 41             Element root = doc.getRootElement();// 指向根节点
 42             List<Element> list = root.getChildren();
 43 
 44             if(list!=null&&list.size()>0){
 45                 for (Element element : list) {
 46                     
 47                     
 48                     if("return_code".equals(element.getName())){
 49                         unifieorderResult.setReturn_code(element.getText());
 50                     }
 51                     
 52                     if("return_msg".equals(element.getName())){
 53                         unifieorderResult.setReturn_msg(element.getText());
 54                     }
 55                     
 56                     if("appid".equals(element.getName())){
 57                         unifieorderResult.setAppid(element.getText());
 58                     }
 59                     
 60                     
 61                     if("mch_id".equals(element.getName())){
 62                         unifieorderResult.setMch_id(element.getText());
 63                     }
 64                     
 65                     if("nonce_str".equals(element.getName())){
 66                         unifieorderResult.setNonce_str(element.getText());
 67                     }
 68                     
 69                     if("sign".equals(element.getName())){
 70                         unifieorderResult.setSign(element.getText());
 71                     }
 72                     
 73                     if("result_code".equals(element.getName())){
 74                         unifieorderResult.setResult_code(element.getText());
 75                     }
 76                     
 77                     if("prepay_id".equals(element.getName())){
 78                         unifieorderResult.setPrepay_id(element.getText());
 79                     }
 80                     
 81                     if("trade_type".equals(element.getName())){
 82                         unifieorderResult.setTrade_type(element.getText());
 83                     }
 84                 }
 85             }
 86 
 87         } catch (JDOMException e) {
 88             e.printStackTrace();
 89         }  catch (IOException e) {
 90             e.printStackTrace();
 91         }catch (Exception e) {
 92             e.printStackTrace();
 93         }
 94         
 95         return unifieorderResult;
 96     }
 97     
 98     
 99     /**
100      * 2、微信回调后参数解析
101      * 解析的时候自动去掉CDMA
102      * @param xml
103      */
104     @SuppressWarnings("unchecked")
105     public static NotifyResult getWXPayResult(String xml){
106         NotifyResult wXPayResult = new NotifyResult();
107         try { 
108             StringReader read = new StringReader(xml);
109             // 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入
110             InputSource source = new InputSource(read);
111             // 创建一个新的SAXBuilder
112             SAXBuilder sb = new SAXBuilder();
113             // 通过输入源构造一个Document
114             Document doc;
115             doc = (Document) sb.build(source);
116 
117             Element root = doc.getRootElement();// 指向根节点
118             List<Element> list = root.getChildren();
119 
120             if(list!=null&&list.size()>0){
121                 for (Element element : list) {
122                     
123                     if("appid".equals(element.getName())){
124                         wXPayResult.setAppid(element.getText());
125                     }else if("attach".equals(element.getName())){
126                         wXPayResult.setAttach(element.getText());
127                     }else if("bank_type".equals(element.getName())){
128                         wXPayResult.setBank_type(element.getText());
129                     }else if("cash_fee".equals(element.getName())){
130                         wXPayResult.setCash_fee(element.getText());
131                     }else if("fee_type".equals(element.getName())){
132                         wXPayResult.setFee_type(element.getText());
133                     }else if("is_subscribe".equals(element.getName())){
134                         wXPayResult.setIs_subscribe(element.getText());
135                     }else if("mch_id".equals(element.getName())){
136                         wXPayResult.setMch_id(element.getText());
137                     }else if("nonce_str".equals(element.getName())){
138                         wXPayResult.setNonce_str(element.getText());
139                     }else if("openid".equals(element.getName())){
140                         wXPayResult.setOpenid(element.getText());
141                     }else if("out_trade_no".equals(element.getName())){
142                         wXPayResult.setOut_trade_no(element.getText());
143                     }else if("result_code".equals(element.getName())){
144                         wXPayResult.setResult_code(element.getText());
145                     }else if("return_code".equals(element.getName())){
146                         wXPayResult.setReturn_code(element.getText());
147                     }else if("sign".equals(element.getName())){
148                         wXPayResult.setSign(element.getText());
149                     }else if("time_end".equals(element.getName())){
150                         wXPayResult.setTime_end(element.getText());
151                     }else if("total_fee".equals(element.getName())){
152                         wXPayResult.setTotal_fee(element.getText());
153                     }else if("trade_type".equals(element.getName())){
154                         wXPayResult.setTrade_type(element.getText());
155                     }else if("transaction_id".equals(element.getName())){
156                         wXPayResult.setTransaction_id(element.getText());
157                     }else if("device_info".equals(element.getName())){
158                         wXPayResult.setDevice_info(element.getText());
159                     }else if("err_code".equals(element.getName())){
160                         wXPayResult.setErr_code(element.getText());
161                     }else if("err_code_des".equals(element.getName())){
162                         wXPayResult.setErr_code_des(element.getText());
163                     }else if("cash_fee_type".equals(element.getName())){
164                         wXPayResult.setCash_fee_type(element.getText());
165                     }else if("coupon_fee".equals(element.getName())){
166                         wXPayResult.setCoupon_fee(element.getText());
167                     }else if("coupon_count".equals(element.getName())){
168                         wXPayResult.setCoupon_count(element.getText());
169                     }
170                     
171                 }
172             }
173 
174         } catch (JDOMException e) {
175             e.printStackTrace();
176         }  catch (IOException e) {
177             e.printStackTrace();
178         }catch (Exception e) {
179             e.printStackTrace();
180         }
181         
182         return wXPayResult;
183     }
184 
185 }

 

推荐阅读