微信支付

实现Java (springMVC)后台端接入微信app支付,回调。

最近去实现Java端实现微信app的后台代码,自己也是查看了很多文档,最终实现了后台端代码的预订单生成,已经前段支付成功后的回调校验,好了废话不多说,直接编写我所实现的路程。(写的不好,大神们不要喷我,如果有雷同请联系我 qq:47092202)

微信APP支付实现

首先需要导入必须的jar包,根据个人而定,我是用了一下jar包

  <dependency>
    <groupId>jdom</groupId>
    <artifactId>jdom</artifactId>
    <version>1.0</version>
  </dependency>

 <dependency>
        <groupId>com.thoughtworks.xstream</groupId>
        <artifactId>xstream</artifactId>
        <version>1.3.1</version>
 </dependency>


首先先准备好,微信支付接口的申请,具体操作可以看官网的文档配置类 

public class ConfigUtil {

public final static String APPID = "";// 服务号的应用号
public final static String MCH_ID = "";// 商户号
public final static String API_KEY = "";// API密钥
public final static String SIGN_TYPE = "MD5";// 签名加密方式
/**微信支付url 不用修改*/
public final static String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
/**微信订单查询url 不用修改*/
public final static String ORDER_QUERY = "https://api.mch.weixin.qq.com/pay/orderquery";
/**回调方法,是我们后台给微信回调的接口,下面会写到*/
public final static String NOTIFY_URL = "xxxxx/pay/wxNotify";

}

controller 代码 (让安卓,ios调用的接口)

@ResponseBody
@RequestMapping("/wxAppPay")
public Map<String, Object> wxpay(HttpServletRequest request, HttpServletResponse response) {
    Map<String, Object> resultMap = new HashMap<String, Object>();
    Order order = orderService.queryOrder(Long.valueOf(request.getParameter("orderNo"))); //根据唯一订单号,查询订单 //业务代码,根据自己情况实现
    BigDecimal price = order.getPayPrice();
    int priceStr = price.multiply(new BigDecimal(100)).intValue(); //微信  金额 单位是分 所以要*100转化为元
    if (priceStr <= 0) {
        resultMap.put("msg", 金额不能为0!");
        resultMap.put("code", "500");
        return resultMap;
    }
     String requestXML = OrderUtil.createOrder(request, order, price);//生成有签字的预订单
    // 调用统一下单接口
    String result = PayCommonUtil.httpsRequest(ConfigUtil.UNIFIED_ORDER_URL, "POST", requestXML);
    logger.info("\n"+result);
    try {
        SortedMap<Object, Object> paramMap = createPayInfo(result);
        resultMap.put("code", "200");
        resultMap.put("payInfo", paramMap);
    } catch (JDOMException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return resultMap;
}


微信回调地址 controller (注意回调地址接口不允许传参)

@RequestMapping("/wxNotify")
@ResponseBody
public void wxNotify(HttpServletRequest request, HttpServletResponse response) throws IOException, JDOMException, ParseException {
    logger.info("微信回调方法");
    // 读取参数
    InputStream inputStream;
    StringBuffer sb = new StringBuffer();
    inputStream = request.getInputStream();
    String s;
    BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
    while ((s = in.readLine()) != null) {
        sb.append(s);
    }
    in.close();
    inputStream.close();
    // 解析xml成map
    Map<String, Object> m = new HashMap<String, Object>();
    m = XMLUtil.doXMLParse(sb.toString());
    // 过滤空 设置 TreeMap
    SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
    Iterator<String> it = m.keySet().iterator();
    while (it.hasNext()) {
        String parameter = (String) it.next();
        Object parameterValue = m.get(parameter);
        String v = "";
        if (null != parameterValue) {
            v = ((String) parameterValue).trim();
        }
        packageParams.put(parameter, v);
    }
    // 判断签名是否正确
    String resultXml = "";
    if (PayCommonUtil.isWxSign("UTF-8", packageParams)) {
        if ("SUCCESS".equals((String) packageParams.get("result_code"))) {
            logger.info("微信支付回调,签字成功!");
            String mch_id = (String) packageParams.get("mch_id"); // 商户号
            String openid = (String) packageParams.get("openid"); // 用户标识
            String out_trade_no = (String) packageParams.get("out_trade_no"); // 商户订单号 +随机6位数
            String transaction_id = (String) packageParams.get("transaction_id"); // 微信支付订单号
            Long orderNo = Long.valueOf(out_trade_no.substring(0, out_trade_no.length()-6)); //还原数据库存放的订单号  
            String cash_fee = (String) packageParams.get("cash_fee"); // 实际支付金额(如果有优惠或者没优惠)
            String time_end = (String) packageParams.get("time_end"); //支付完成时间
            Order order = this.orderService.queryOrder(orderNo);
            if (!ConfigUtil.MCH_ID.equals(mch_id) || order == null) {
                logger.info("支付失败,错误信息:" + "参数错误");
                resultXml=PayCommonUtil.setXML("FAIL","参数错误");
            } else {
                   //自己业务处理//
                  // 订单更新 ,保存支付信息
                  resultXml=PayCommonUtil.setXML("SUCCESS","OK");
            }
        } else {
            logger.info("支付失败,错误信息:" + packageParams.get("err_code"));
            resultXml=PayCommonUtil.setXML("FAIL","报文错误");
        }
    } else {
        resultXml=PayCommonUtil.setXML("FAIL","签名验证失败");
        logger.info("签名验证失败");
    }
    logger.info("resultXml  :" + resultXml);
    BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
    out.write(resultXml.getBytes());
    out.flush();
    out.close();
}

根据微信的流水号订单查询

@ResponseBody
@RequestMapping("/wxPayOrderQuery")
public Map<String, Object> wxGetOrder(HttpServletRequest request, HttpServletResponse response,String transactionId) throws JDOMException, IOException {
      Map<String, Object> resultMap = new HashMap<String, Object>();
      SortedMap<Object, Object> reqMap = new TreeMap<Object, Object>();
      reqMap.put("appid", ConfigUtil.APPID);
      reqMap.put("mch_id", ConfigUtil.MCH_ID);
      reqMap.put("nonce_str", PayCommonUtil.CreateNoncestr());
      reqMap.put("transaction_id", transactionId); //微信支付成后的订单号
      String sign = PayCommonUtil.createSign("UTF-8", reqMap);
      reqMap.put("sign", sign);
      String requestXML = PayCommonUtil.getRequestXml(reqMap);
      String retStr =  PayCommonUtil.httpsRequest(ConfigUtil.ORDER_QUERY, "POST", requestXML);
      Map<String, Object> map = XMLUtil.doXMLParse(retStr);
      String return_code = (String) map.get("return_code");
      String result_code = (String) map.get("result_code");
      if("SUCCESS".equals(return_code) && "SUCCESS".equals(result_code)){
          resultMap.putAll(map);
      }else{
        String err_code  = (String) map.get("err_code");
        String err_code_des  = (String) map.get("err_code_des");
        resultMap.put("err_code", err_code);
        resultMap.put("err_code_des", err_code_des);
      }
   return resultMap;
}


接下来是工具类:

OrderUtil

//统一下单的预订单生成
private static String createOrder(HttpServletRequest request, PordOrder order,BigDecimal price) {
    SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
    parameters.put("appid", ConfigUtil.APPID);
    parameters.put("mch_id", ConfigUtil.MCH_ID);
    parameters.put("nonce_str", PayCommonUtil.CreateNoncestr());
    parameters.put("body", order.getOrderDesc());
    String orderNo = order.getOrderNo() +PayCommonUtil.getRandByNum(6); //订单号后面带上6位随机数  解决数据库同一订单号不能多次提交给微信问题
    parameters.put("out_trade_no", orderNo); // 订单编号+随机6位随机数
    parameters.put("fee_type", "CNY");
    parameters.put("total_fee", String.valueOf(price)); //支付总金额
    parameters.put("spbill_create_ip", request.getParameter("createIp"));
    parameters.put("notify_url", ConfigUtil.NOTIFY_URL);
    parameters.put("trade_type", "APP");
    // 设置签名
    String sign = PayCommonUtil.createSign("UTF-8", parameters);
    parameters.put("sign", sign);
    // 封装请求参数结束
    String requestXML = PayCommonUtil.getRequestXml(parameters);
    return requestXML;
}

/** 根据预订单参数,生成ios,安卓使用的必要参数* */
private static SortedMap<Object, Object> createPayInfo(String result) throws JDOMException, IOException {
    Map<String, Object> map = XMLUtil.doXMLParse(result);
    SortedMap<Object, Object> paramMap = new TreeMap<Object, Object>();
    paramMap.put("appid", ConfigUtil.APPID);
    paramMap.put("partnerid", ConfigUtil.MCH_ID);
    paramMap.put("prepayid", map.get("prepay_id"));
    paramMap.put("package", "Sign=WXPay");
    paramMap.put("noncestr", PayCommonUtil.CreateNoncestr());
    // 本来生成的时间戳是13位,但是ios必须是10位,所以截取了一下
    paramMap.put("timestamp", Long
            .valueOf(Long.parseLong(String.valueOf(System.currentTimeMillis()).toString().substring(0, 10))));
    String sign2 = PayCommonUtil.createSign("UTF-8", paramMap);
    paramMap.put("sign", sign2);
    return paramMap;
}


MD5Util

private static String byteArrayToHexString(byte b[]) {
    StringBuffer resultSb = new StringBuffer();
    for (int i = 0; i < b.length; i++)
        resultSb.append(byteToHexString(b[i]));

    return resultSb.toString();
}

private static String byteToHexString(byte b) {
    int n = b;
    if (n < 0)
        n += 256;
    int d1 = n / 16;
    int d2 = n % 16;
    return hexDigits[d1] + hexDigits[d2];
}

public static String MD5Encode(String origin, String charsetname) {
    String resultString = null;
    try {
        resultString = new String(origin);
        MessageDigest md = MessageDigest.getInstance("MD5");
        if (charsetname == null || "".equals(charsetname))
            resultString = byteArrayToHexString(md.digest(resultString
                    .getBytes()));
        else
            resultString = byteArrayToHexString(md.digest(resultString
                    .getBytes(charsetname)));
    } catch (Exception exception) {
    }
    return resultString;
}

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


PayCommonUtil

/**
 * 默认16 位随机字符串
 */
public static String CreateNoncestr() {
    String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    String res = "";
    for (int i = 0; i < 16; i++) {
        Random rd = new Random();
        res += chars.charAt(rd.nextInt(chars.length() - 1));
    }
    return res;
}

/** 
 * 是否签名正确,
 */  
public static boolean isWxSign(String characterEncoding, SortedMap<Object, Object> packageParams) {  
    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(!"sign".equals(k) && null != v && !"".equals(v)) {  
            sb.append(k + "=" + v + "&");  
        }  
    }  
    sb.append("key=" + ConfigUtil.API_KEY);  
    String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();  
    String tenpaySign = ((String)packageParams.get("sign")).toUpperCase();  
    return tenpaySign.equals(mysign);  
}  

/**
 * 签名工具
 * @Description:sign签名
 * @param characterEncoding 编码格式 UTF-8
 * @param parameters 请求参数
 * @return
 */
public static String createSign(String characterEncoding,Map<String, Object> parameters) {
    StringBuffer sb = new StringBuffer();
    Iterator<Entry<String, Object>> it = parameters.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry <String,Object>entry = (Map.Entry<String,Object>) it.next();
        String key = (String) entry.getKey();
        Object value = entry.getValue();//去掉带sign的项
        if (null != value && !"".equals(value) && !"sign".equals(key)
                && !"key".equals(key)) {
            sb.append(key + "=" + value + "&");
        }
    }
    sb.append("key=" + ConfigUtil.API_KEY);
    //注意sign转为大写
    return MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
}

/**
 * @Description:sign签名
 * @param characterEncoding 编码格式
 * @param parameters 请求参数
 * @return
 */
public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){
    StringBuffer sb = new StringBuffer();
    Set es = parameters.entrySet();
    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="+ConfigUtil.API_KEY);
    String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
    return sign;
}

/**
 * @Description:将请求参数转换为xml格式的string
 * @param parameters  请求参数
 * @return
 */
public static String getRequestXml(SortedMap<Object,Object> parameters){
    StringBuffer sb = new StringBuffer();
    sb.append("<xml>");
    Set es = parameters.entrySet();
    Iterator it = es.iterator();
    while(it.hasNext()) {
        Map.Entry entry = (Map.Entry)it.next();
        Object ks = entry.getKey();
        Object vs = entry.getValue();
        String k = ks.toString();
        String v = vs.toString();
        if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)) {
            sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");
        }else {
            sb.append("<"+k+">"+v+"</"+k+">");
        }
    }
    sb.append("</xml>");
    return sb.toString();
}
/**
 * @Description:返回给微信的参数
 * @param return_code 返回编码
 * @param return_msg  返回信息
 * @return
 */
public static String setXML(String return_code, String return_msg) {
    return "<xml><return_code><![CDATA[" + return_code
            + "]]></return_code><return_msg><![CDATA[" + return_msg
            + "]]></return_msg></xml>";
}

/**
 * 发送https请求
 * @param requestUrl 请求地址
 * @param requestMethod 请求方式(GET、POST)
 * @param outputStr 提交的数据
 * @return 返回微信服务器响应的信息
 */
public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
    try {
        // 创建SSLContext对象,并使用我们指定的信任管理器初始化
        TrustManager[] tm = { new WechatTrustManager() };
        SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
        sslContext.init(null, tm, new java.security.SecureRandom());
        // 从上述SSLContext对象中得到SSLSocketFactory对象
        SSLSocketFactory ssf = sslContext.getSocketFactory();
        URL url = new URL(requestUrl);
        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
        //conn.setSSLSocketFactory(ssf);
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setUseCaches(false);
        // 设置请求方式(GET/POST)
        conn.setRequestMethod(requestMethod);
        conn.setRequestProperty("content-type", "application/x-www-form-urlencoded"); 
        // 当outputStr不为null时向输出流写数据
        if (null != outputStr) {
            OutputStream outputStream = conn.getOutputStream();
            // 注意编码格式
            outputStream.write(outputStr.getBytes("UTF-8"));
            outputStream.close();
        }
        // 从输入流读取返回内容
        InputStream inputStream = conn.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;
        conn.disconnect();
        return buffer.toString();
    } catch (ConnectException ce) {
        ce.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

public static String urlEncodeUTF8(String source){
    String result = source;
    try {
        result = java.net.URLEncoder.encode(source,"utf-8");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    return result;
}

/** 
 * 产生num位的随机数 
 */  
public static String getRandByNum(int num){  
    String length = "1";  
    for(int i=0;i<num;i++){  
        length += "0";  
    }  

    Random rad=new Random();  

    String result  = rad.nextInt(Integer.parseInt(length)) +"";  

    if(result.length()!=num){  
        return getRandByNum(num);  
    }  
    return result;  
}


XMLUtil

/**
 * 解析xml,返回第一级元素键值对。
 * 如果第一级元素有子节点,
 * 则此节点的值是子节点的xml数据。
 * @param strxml
 * @return
 * @throws JDOMException
 * @throws IOException
 */
public static SortedMap<String, Object> doXMLParse(String strxml)
        throws JDOMException, IOException {
    strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
    if (null == strxml || "".equals(strxml)) {
        return null;
    }
    SortedMap<String, Object> map = new TreeMap<String, Object>();
    InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
    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 key = e.getName();
        String value = "";
        List children = e.getChildren();
        if (children.isEmpty()) {
            value = e.getTextNormalize();
        } else {
            value = XMLUtil.getChildrenText(children);
        }
        map.put(key, value);
    }
    // 关闭流
    in.close();
    return map;
}

/**
 * 获取子结点的xml
 * @param children
 * @return
 */
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(XMLUtil.getChildrenText(list));
            }
            sb.append(value);
            sb.append("</" + name + ">");
        }
    }
    return sb.toString();
}


WechatTrustManager

/* 证书信任管理器(用于https请求)/ 
public class WechatTrustManager implements X509TrustManager{

public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {  
}  
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {  
}  
public X509Certificate[] getAcceptedIssuers() {  
    return null;  
}  

}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值