1. 程式人生 > >微信支付統一下單,簽名錯誤

微信支付統一下單,簽名錯誤

測試白名單也已設定。

請求路徑:https://api.mch.weixin.qq.com/pay/unifiedorder
1、引數拼接(回車排序一下方便看):
appid=wxb5e39527f2f3eb32
&body=SourceTest
&mch_id=1271438801
&nonce_str=ns64Mu07nTYX2iPZ
&notify_url=http://fanbingjiang.gicp.net/s/store/front/wxpay
&openid=oi18Jv38WxdxKEXF9ER-8mIyYya4
&out_trade_no=1234567891
&spbill_create_ip=58.42.242.98


&total_fee=1123
&trade_type=JSAPI
&key=36cd38f49b9afa08222c0dc9ebfe35eb
2、生成sign:54511A905603EDE7CED60F5643845EC7
3、拼接請求xml檔案(順序與第1步引數拼接一樣):

<xml>
<appid><![CDATA[wxb5e39527f2f3eb32]]></appid>
<body><![CDATA[SourceTest]]></body>
<mch_id><![CDATA[1271438801]]></mch_id>
<nonce_str><![CDATA[ns64Mu07nTYX2iPZ]]></nonce_str>
<notify_url><![CDATA[http://fanbingjiang.gicp.net/s/store/front/wxpay]]></notify_url>
<openid><![CDATA[oi18Jv38WxdxKEXF9ER-8mIyYya4]]></openid>
<out_trade_no><![CDATA[1234567891]]></out_trade_no>
<spbill_create_ip><![CDATA[58.42.242.98]]></spbill_create_ip>
<total_fee><![CDATA[1123]]></total_fee>
<trade_type><![CDATA[JSAPI]]></trade_type>
<sign><![CDATA[54511A905603EDE7CED60F5643845EC7]]></sign>
</xml>


4、請求返回值:
<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[簽名錯誤]]></return_msg></xml>


5、到https://pay.weixin.qq.com/wiki/tools/signverify/ 介面測試去驗證了一下,結果與我的一樣。

globals這個類是讀取配置檔案的類


微信支付工具類

package com.thinkgem.jeesite.common.wechat;

import com.thinkgem.jeesite.common.config.Global;

import java.text.DecimalFormat;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;

/**
 * 微信支付
 * Created by yuhaiming on 2016/11/18 0018.
 */
public class WeChatUtil {
    /**
     * 基本常量設定
     */

    /**
     * APPID
     */
    public static String APP_ID = Global.getConfig("wechat.appid");
    /**
     * 微信支付商戶號
     */
    public static String MCH_ID= Global.getConfig("wechat.mch_id");
    /**
     * 請求路徑
     */
    public static String UFDODER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    /**
     * 密匙
     */
    public static String API_KEY = Global.getConfig("wechat.key");
    /**
     * 發起支付IP
     */
    public static String CREATE_IP = "112.117.94.77";
    /**
     * 回撥url
     */
    public static String NOTIFY_URL = "http%3a%2f%2fqqz33fe9841.ngrok.wendal.cn%2fapp%2fhome%2f";

    /**
     * 生成微信簽名
     * @param order_id
     *         訂單ID
     * @param body
     *         描述
     * @param order_price
     *         價格
     * @return
     */
    public static  String GetWeChatXML(String order_id, String body, double order_price ){
        String currTime = PayCommonUtil.getCurrTime();
        String strTime = currTime.substring(8, currTime.length());
        String strRandom = PayCommonUtil.buildRandom(4) + "";
        //隨機字串
        String nonce_str = strTime + strRandom;//UUID.randomUUID().toString();
        nonce_str=nonce_str.substring(0,16);
        // 獲取發起電腦 ip
        String spbill_create_ip = WeChatUtil.CREATE_IP;
        // 回撥介面
        String notify_url = WeChatUtil.NOTIFY_URL;
        //交易型別
        String trade_type = "JSAPI";
        //微信價格最小單位分 轉換為整數
        DecimalFormat df = new DecimalFormat("#######.##");
        order_price = order_price * 100;
        order_price = Math.ceil(order_price);
        String price = df.format(order_price);
        SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
        packageParams.put("appid", APP_ID);
        packageParams.put("body", body);
        packageParams.put("mch_id", MCH_ID);
        packageParams.put("nonce_str", nonce_str);
        packageParams.put("notify_url", notify_url);
        packageParams.put("out_trade_no", order_id);
        packageParams.put("spbill_create_ip", spbill_create_ip);
        packageParams.put("total_fee", price);
        packageParams.put("trade_type", trade_type);
        packageParams.put("openid", "o_6gNwi23RLC97cSPfHE-DEg3OLA");
        String sign = PayCommonUtil.createSign("UTF-8", packageParams,API_KEY);
        packageParams.put("sign", sign);
        String requestXML = PayCommonUtil.getRequestXml(packageParams);
        System.out.println(requestXML);
        return requestXML;
    }


}
package com.thinkgem.jeesite.common.wechat;

import java.text.SimpleDateFormat;
import java.util.*;

public class PayCommonUtil {
	/** 
     * 是否簽名正確,規則是:按引數名稱a-z排序,遇到空值的引數不參加簽名。 
     * @return boolean 
     */  
    @SuppressWarnings("unchecked")
	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 = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();  
        String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();  
          
        //System.out.println(tenpaySign + "    " + mysign);  
        return tenpaySign.equals(mysign);  
    }  

    /**
     * @Description:sign簽名
     * @param characterEncoding
     *          編碼格式
     * @param packageParams
     *          請求引數
     * @param API_KEY
     * @return
     */
    @SuppressWarnings("unchecked")
	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 = (String) entry.getValue();  
//            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k) && !"notify_url".equals(k)&& !"out_trade_no".equals(k)&& !"spbill_create_ip".equals(k)&& !"total_fee".equals(k)&& !"trade_type".equals(k)) { 
            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k) ) {  
                sb.append(k + "=" + v + "&");  
            }  
        }  
        sb.append("key=" + API_KEY);  
//        String stringA="appid=wxc7b425229b570867&mch_id=1406330002&nonce_str=1702585759&key=ab42e0b7aa6bce35164a2d14855d7264";
        String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();  
//        String sign = MD5Util.MD5Encode(stringA, characterEncoding).toUpperCase();
//        System.out.println(sign);
//        System.out.println(MD5Util.MD5Encode("appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA&key=192006250b4c09247ec02edce69f6a2d", characterEncoding).toUpperCase());
//        System.out.println(sign);
        return sign;  
    }  
  
    /** 
     * @author 
     * @date 2016-4-22 
     * @Description:將請求引數轉換為xml格式的string 
     * @param parameters 
     *            請求引數 
     * @return 
     */  
    @SuppressWarnings("unchecked")
	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 = (String) 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;  
    }
}

傳送請求 工具類
package com.thinkgem.jeesite.common.wechat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;

public class HttpUtil {
//	private static final Log logger = Logs.get();  
    private final static int CONNECT_TIMEOUT = 5000; // in milliseconds  
    private final static String DEFAULT_ENCODING = "UTF-8";  
      
    public static String postData(String urlStr, String data){  
        return postData(urlStr, data, null);  
    }  
      
    public static String postData(String urlStr, String data, String contentType){  
        BufferedReader reader = null;  
        try {  
            URL url = new URL(urlStr);  
            URLConnection conn = url.openConnection();  
            conn.setDoOutput(true);  
            conn.setConnectTimeout(CONNECT_TIMEOUT);  
            conn.setReadTimeout(CONNECT_TIMEOUT);  
            if(contentType != null)  
                conn.setRequestProperty("content-type", contentType);  
            OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);  
            if(data == null)  
                data = "";  
            writer.write(data);   
            writer.flush();  
            writer.close();    
  
            reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));  
            StringBuilder sb = new StringBuilder();  
            String line = null;  
            while ((line = reader.readLine()) != null) {  
                sb.append(line);  
                sb.append("\r\n");  
            }  
            return sb.toString();  
        } catch (IOException e) {  
//            logger.logError("Error connecting to " + urlStr + ": " + e.getMessage());  
        } finally {  
            try {  
                if (reader != null)  
                    reader.close();  
            } catch (IOException e) {  
            }  
        }  
        return null;  
    }  
}

呼叫方法
 String order_id="e3177146e5";
        String body="彼得潘";
        Double order =0.01;
        Map<String,String> xmlMap = new HashMap<>();
        Map<String,String> resMap = new HashMap<>();
        //生成簽名
        String xml = WeChatUtil.GetWeChatXML(order_id,body,order);
        xmlMap = XMLUtil.doXMLParse(xml);

        //統一下單
        String SubmitResult = HttpUtil.postData(WeChatUtil.UFDODER_URL,xml);
        SortedMap<Object,Object> SubmitMap = new TreeMap<Object,Object>();
        //解析XML
        resMap = XMLUtil.doXMLParse(SubmitResult);
        String result_code  = resMap.get("result_code");
        if("SUCCESS".equals(result_code)) {
            //appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式為Sign=WXPay
            SubmitMap.put("appid", WeChatUtil.APP_ID);
            SubmitMap.put("partnerid", WeChatUtil.MCH_ID);
            SubmitMap.put("prepayid", resMap.get("prepay_id"));
            SubmitMap.put("noncestr", resMap.get("nonce_str"));
            Long time = (System.currentTimeMillis() / 1000);
            SubmitMap.put("timestamp", time.toString());
            SubmitMap.put("package", "Sign=WXPay");
            //第二次生成簽名
            String sign = PayCommonUtil.createSign("UTF-8", SubmitMap, WeChatUtil.API_KEY);
            SubmitMap.put("sign", sign);
        }

在stringA最後拼接上key得到stringSignTemp字串,並對stringSignTemp進行MD5運算,再將得到的字串所有字元轉換為大寫,得到sign值signValue。

最後 提醒各位 注意一下幾點

1.支付簽名是需要簽名兩次

2.檢查key設定是否正確

嘗試很多種方法都是簽名錯誤 最後懷疑是不是KEY的問題 沒想到真是
key設定路徑:微信商戶平臺(pay.weixin.qq.com)-->賬戶設定-->API安全-->金鑰設定