微信支付

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);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章