微信支付

package com.hzbuvi.pay.wechat.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hzbuvi.pay.wechat.utils.HttpRequest;
import com.hzbuvi.pay.wechat.utils.PayCommonUtil;
import com.hzbuvi.pay.wechat.utils.WechatUtils;
import com.hzbuvi.pay.wechat.utils.XmlUtils;
import com.hzbuvi.util.basic.ValueUtil;
import com.hzbuvi.util.date.DateUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;

/**
 * Created by SJQ on 2017/2/17.
 */
@RestController
@RequestMapping("/wechat")
public class WechatPay
{
    private static final Logger log = LoggerFactory.getLogger(WechatPay.class);

    private static String APPID = "wx29983132bd04dca9";//AppID(应用ID)
    private static String MCHID= "1435678702";  
    private static String KEY= "eidiwoeid483";  //32位API密钥
    private static String BODY= "order pay";
//    private static String BODY= "订单支付";
    private static String out_trade_no= DateUtil.toString(new Date(),"yyyyMMddHHmmss");
    private static String product_id= DateUtil.toString(new Date(),"yyyyMMddHHmmss")+1;
    private static String notify_url= "http://88.88.88.211:8095/backUrl/wechatPay";   //回调地址。测试回调必须保证外网能访问到此地址

    @RequestMapping(value = "/pay",method = RequestMethod.GET)
    public String pay(@RequestParam Map<String,String> params){

        JSONObject retJson = new JSONObject();
        try {

            String jsonStr = ValueUtil.toJson(params);

            JSONObject requestObj = JSON.parseObject(jsonStr);

//            String out_trade_no = requestObj.getString("out_trade_no");
//            String order_price = requestObj.getString("order_price");

            // 正式上线的时候价格根据订单id查询
            String order_price = "1"; // 价格 注意:价格的单位是分

            SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();
            //商户信息
            packageParams.put("appid", APPID);      //公众账号ID
            packageParams.put("mch_id", MCHID);     //商户号
            packageParams.put("nonce_str", WechatUtils.getNonce_str());      //随机字符串
            packageParams.put("body",new String(BODY.getBytes("utf-8"))  );        //商品描述  eg:腾讯充值中心-QQ会员充值

            packageParams.put("trade_type", "NATIVE");      //交易类型  JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付,统一下单接口trade_type的传参可参考这里

            packageParams.put("spbill_create_ip", "123.12.12.123");     //终端IP  eg:123.12.12.123

            packageParams.put("notify_url", notify_url);        //通知地址  异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。

            packageParams.put("out_trade_no", out_trade_no);        //商户订单号
            packageParams.put("total_fee", order_price);        //标价金额  订单总金额,单位为分,详见支付金额
                                                                //MICROPAY--刷卡支付,刷卡支付有单独的支付接口,不调用统一下单接口
            packageParams.put("product_id", product_id);        //商品ID trade_type=NATIVE时(即扫码支付),此参数必传。此参数为二维码中包含的商品ID,商户自行定义。
            Calendar nowTime = Calendar.getInstance();
            packageParams.put("time_start", DateUtil.toString(      //交易起始时间  订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。
                    nowTime.getTime(), "yyyyMMddHHmmss"));
//            packageParams.put("time_expire", DateUtil.toString(     //交易结束时间  订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。
//                    nowTime.getTime(), "yyyyMMddHHmmss"));

            //生成签名字符串
            String sign = PayCommonUtil.createSign("UTF-8", packageParams, KEY);
            packageParams.put("sign", sign);    //签名

            String requestXML = PayCommonUtil.getRequestXml(packageParams);
            log.info("支付请求:" + requestXML);
            long startTime=System.currentTimeMillis();

            String resXml = HttpRequest.post(
                    "https://api.mch.weixin.qq.com/pay/unifiedorder",
                    requestXML);
            log.info("请求结果:" + resXml);

            long endTime=System.currentTimeMillis();

            Integer execute_time = (int) ((endTime-startTime)/1000);
            SortedMap map = XmlUtils.parseXmlStr(resXml);
            Boolean validateCallback = PayCommonUtil.isTenpaySign("utf-8",map,KEY);
            log.info("验证返回签名结果:" + validateCallback);
            if (!validateCallback){
                return ValueUtil.toError("999","回掉签名验证失败");
            }

            JSONObject reportParams = new JSONObject();
            reportParams.put("url", "https://api.mch.weixin.qq.com/pay/unifiedorder");
            reportParams.put("execute_time", execute_time);
            reportParams.put("return_code", map.get("return_code").toString());
            reportParams.put("return_msg", map.get("return_msg").toString());
            reportParams.put("result_code", map.get("result_code").toString());
            if (map.get("err_code") != null) {
                reportParams.put("err_code", map.get("err_code").toString());
                reportParams.put("err_code_des", map.get("err_code_des").toString());
            }
            reportParams.put("out_trade_no", out_trade_no);
            //交易保障
//            report(reportParams.toString());
            if (map.get("return_code").toString().equals("SUCCESS") && map.get("result_code").toString().equals("SUCCESS")) {
                String urlCode = (String) map.get("code_url"); //微信二维码短链接
                retJson.put("code", 0);
                retJson.put("message", "下单成功.");
                retJson.put("data", urlCode);
            } else {
                retJson.put("code", 1);
                retJson.put("message", map.get("err_code_des").toString());
                retJson.put("data", "");
            }
            return retJson.toString();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        return null;
    }

}
package com.hzbuvi.pay.wechat.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.ProtocolException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HttpsURLConnection;
import java.io.*;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by SJQ on 2017/2/17.
 */
public class PayCommonUtil {
    public static Logger log = LoggerFactory.getLogger(PayCommonUtil.class);

    /**
     * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
     * @return boolean
     */
    public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_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(!"sign".equals(k) && null != v && !"".equals(v)) {
                sb.append(k + "=" + v + "&");
            }
        }

        sb.append("key=" + API_KEY);

        //算出摘要
        String mysign = MD5.MD5Encode(sb.toString()).toLowerCase();
        String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();

        //System.out.println(tenpaySign + "    " + mysign);
        return tenpaySign.equals(mysign);
    }

/**
     * @author
     * @date 2016-4-22
     * @Description:sign签名
     * @param characterEncoding
     *            编码格式
     * @params parameters
     *            请求参数
     * @return
     */
    public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_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 = "";
            try {
                v = (String) entry.getValue();
            } catch (Exception e) {
                // TODO: handle exception
                v = entry.getValue() + "";
            }

            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + API_KEY);

        System.out.println("ASCII字典序排序::::"+sb.toString());
        String sign = MD5.MD5Encode(sb.toString()).toUpperCase();
        return sign;
    }

    /**
     * @author
     * @date 2016-4-22
     * @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();
            String k = (String) entry.getKey();
            String v = "";
            try {
                v = (String) entry.getValue();
            } catch (Exception e) {
                // TODO: handle exception
                v = entry.getValue() + "";
            }

            if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
                sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
            } else {
                sb.append("<" + k + ">" + v + "</" + k + ">");
            }
        }
        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * 取出一个指定长度大小的随机正整数.
     *
     * @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));
    }

    /**
     * 获取当前时间 yyyyMMddHHmmss
     *
     * @return String
     */
    public static String getCurrTime() {
        Date now = new Date();
        SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        String s = outFormat.format(now);
        return s;
    }


    public static JSONObject httpsRequestToJsonObject(String requestUrl,
                                                      String requestMethod, String outputStr) {
        JSONObject jsonObject = null;
        try {
            StringBuffer buffer = httpsRequest(requestUrl, requestMethod,
                    outputStr);
            jsonObject = JSON.parseObject(buffer.toString());

        } catch (ConnectException ce) {
            log.error("连接超时:" + ce.getMessage());
        } catch (Exception e) {
            log.error("https请求异常:" + e.getMessage());
        }
        return jsonObject;
    }

    private static StringBuffer httpsRequest(String requestUrl,
                                             String requestMethod, String output)
            throws NoSuchAlgorithmException, NoSuchProviderException,
            KeyManagementException, MalformedURLException, IOException,
            ProtocolException, UnsupportedEncodingException {
        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;
    }

}

package com.hzbuvi.pay.wechat.utils;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import java.util.*;

import static javax.swing.text.html.HTML.Tag.BODY;
import static javax.swing.text.html.HTML.Tag.HEAD;

/**
 * Created by SJQ on 2017/2/17.
 */
public class XmlUtils {
    /**
     * 解析XML字符串
     *
     * @param xml
     * @return
     * @throws
     */
    public static SortedMap<String,Object> parseXmlStr(String xml){
        SortedMap<String,Object> map = new TreeMap<String, Object>();
        try {
            Document document = DocumentHelper.parseText(xml);
            Element root = document.getRootElement();
            Iterator<Element> it = root.elementIterator();
            while (it.hasNext()) {
                Element element = it.next();
                map.put(element.getName(), element.getTextTrim());
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return map;
    }
}
package com.hzbuvi.pay.wechat.utils;

import java.security.MessageDigest;

/**
 * User: rizenguo
 * Date: 2014/10/23
 * Time: 15:43
 */
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[] digest) {
        StringBuffer hexValue = new StringBuffer();

        for (int i = 0; i < digest.length; i++) {
            int val = ((int) digest[i]) & 0xff;
            if (val < 16)
                hexValue.append("0");
            hexValue.append(Integer.toHexString(val));
        }

        return hexValue.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()));//原文件内容,可能原因是:win2003时系统缺省编码为GBK,win7为utf-8
            resultString = byteArrayToHexString(md.digest(resultString.getBytes("utf-8")));//正确的写法
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultString;
    }

}
package com.hzbuvi.pay.wechat.utils;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Map;

/**
 * Created by SJQ on 2017/2/17.
 */
public class HttpRequest {
    public static String post(String url,String pama) throws IOException{
        URL postUrl = new URL(url);
        // 打开连接
        HttpURLConnection connection = (HttpURLConnection) postUrl.openConnection();

        // 设置是否向connection输出,因为这个是post请求,参数要放在
        // http正文内,因此需要设为true
        connection.setDoOutput(true);
        //设置超时时间
        connection.setConnectTimeout(10000);
        // Read from the connection. Default is true.
        connection.setDoInput(true);
        // 默认是 GET方式
        connection.setRequestMethod("POST");

        // Post 请求不能使用缓存
        connection.setUseCaches(false);

        connection.setInstanceFollowRedirects(true);

        // 配置本次连接的Content-type,配置为application/x-www-form-urlencoded的
        // 意思是正文是urlencoded编码过的form参数,下面我们可以看到我们对正文内容使用URLEncoder.encode
        // 进行编码
        connection.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
        // 连接,从postUrl.openConnection()至此的配置必须要在connect之前完成,
        // 要注意的是connection.getOutputStream会隐含的进行connect。
        connection.connect();
        DataOutputStream out = new DataOutputStream(connection
                .getOutputStream());
        // The URL-encoded contend
        // 正文,正文内容其实跟get的URL中 '? '后的参数字符串一致

        // DataOutputStream.writeBytes将字符串中的16位的unicode字符以8位的字符形式写到流里面
        out.writeBytes(pama);

        out.flush();
        out.close();

        int status =  connection.getResponseCode();
        if(status==200){
            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(),"utf-8"));
            String line;
            StringBuffer sb = new StringBuffer("");
            while ((line = reader.readLine()) != null){

                line = new String(line);
                sb.append(line);
            }
            reader.close();
            connection.disconnect();
            if(sb.toString().length()>0){
                return sb.toString();
            }
            return String.valueOf(status);
        }
        return String.valueOf(status);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章