微信支付統一下單,簽名錯誤
阿新 • • 發佈:2019-01-04
測試白名單也已設定。
請求路徑:https://api.mch.weixin.qq.com/pay/unifiedorder
1、引數拼接(回車排序一下方便看):
appid=wxb5e39527f2f3eb32
&body=SourceTest
&mch_id=1271438801
&nonce_str=ns64Mu07nTYX2iPZ
¬ify_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安全-->金鑰設定