1. 程式人生 > 實用技巧 >微信支付——沙箱除錯環境getsignkey方法祕鑰獲取

微信支付——沙箱除錯環境getsignkey方法祕鑰獲取

官方文件 :https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=23_1

<xml>
  <return_code><![CDATA[FAIL]]></return_code>
  <return_msg><![CDATA[沙箱驗證簽名失敗,請確認沙箱簽名key是否正確(通過getsignkey呼叫生成)]]></return_msg>
</xml>

在做沙箱測試的時候,微信支付裡面的金額固定的,其他金額是無效的。需要按照文件來輸入。

這裡需要用到沙箱祕鑰來替換籤名中的key,同樣驗籤的時候也是同樣用沙箱祕鑰獲取簽名,沙箱祕鑰的獲取方法

微信支付模擬測試系統(後簡稱模擬系統)的簡化原理圖。模擬系統的API協議與正式API完全相同(API介面文件)。商戶開發者只需將正式API的呼叫URL增加一層sandboxnew路徑,即可對接到模擬系統。

例如,付款碼支付URL:https://api.mch.weixin.qq.com/pay/micropay

變更為:https://api.mch.weixin.qq.com/sandboxnew/pay/micropay。

模擬系統與生產環境完全獨立,包括儲存層。商戶在模擬系統所做的所有交易(如下單、支付、查詢)均為無資金流的假資料,即:使用者無需真實扣款,商戶也不會有資金入賬。代金券同理,沙箱環境中無需商戶真實制券與發券,亦不會出現真實扣券情況。驗收模擬測試系統的API驗籤金鑰需從API獲取:

需要引入

commons-lang-2.4.jar、jdom-1.1.3.jar

getSignKeyUtils.java 工具類

package com.jetcms.weixinpay;

import org.apache.commons.lang.StringUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL; import java.net.URLConnection; import java.security.MessageDigest; import java.util.*; public class getSignKeyUtils { /** * 獲取微信支付沙箱API驗籤金鑰 (呼叫該方法生成) * @param nonce_str 隨機字串 生成方法:RandomStringUtils.random(10, N62_CHARS); * @param mchId 正式的商戶號 * @param apiKey 正式的金鑰 * @return */ public static String getSignKey(String nonce_str,String mchId,String apiKey) { Map<String, String> param = new HashMap<String, String>(); param.put("mch_id", mchId);//需要真實商戶號 param.put("nonce_str", nonce_str);//隨機字元 String sign = createSign(param, apiKey);//通過SDK生成簽名其中API_KEY為商戶對應的真實金鑰 param.put("sign", sign); String xml = assembParamToXml(param);//將map轉換為xml格式 String url = "https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey";//沙箱金鑰獲取api Map<String, String> param1 = new HashMap<String, String>(); String resXml = post(url, xml); try { if (StringUtils.isNotBlank(resXml)) { param1 = parseXMLToMap(resXml); } } catch (Exception e) { e.printStackTrace(); } String key = param1.get("sandbox_signkey"); return key; } /** * 微信支付簽名sign * @param param * @param key * @return */ @SuppressWarnings("unchecked") public static String createSign(Map<String, String> param,String key){ //簽名步驟一:按字典排序引數 List list=new ArrayList(param.keySet()); Object[] ary =list.toArray(); Arrays.sort(ary); list=Arrays.asList(ary); String str=""; for(int i=0;i<list.size();i++){ str+=list.get(i)+"="+param.get(list.get(i)+"")+"&"; } //簽名步驟二:加上key str+="key="+key; //步驟三:加密並大寫 str= MD5Encode(str,"utf-8").toUpperCase(); return str; } /** * 將需要傳遞給微信的引數轉成xml格式 * @param parameters * @return */ public static String assembParamToXml(Map<String,String> parameters){ StringBuffer sb = new StringBuffer(); sb.append("<xml>"); Set<String> es = parameters.keySet(); List<Object> list=new ArrayList<Object>(es); Object[] ary =list.toArray(); Arrays.sort(ary); list=Arrays.asList(ary); Iterator<Object> it = list.iterator(); while(it.hasNext()) { String key = (String) it.next(); String val=(String) parameters.get(key); if ("attach".equalsIgnoreCase(key)||"body".equalsIgnoreCase(key)||"sign".equalsIgnoreCase(key)) { sb.append("<"+key+">"+"<![CDATA["+val+"]]></"+key+">"); }else { sb.append("<"+key+">"+val+"</"+key+">"); } } sb.append("</xml>"); return sb.toString(); } public static String post(String urlStr,String xmlInfo) { String line1 = ""; try { URL url = new URL(urlStr); URLConnection con = url.openConnection(); con.setDoOutput(true); con.setRequestProperty("Cache-Control", "no-cache"); con.setRequestProperty("Content-Type", "text/xml"); OutputStreamWriter out = new OutputStreamWriter(con .getOutputStream()); out.write(new String(xmlInfo.getBytes("utf-8"))); out.flush(); out.close(); BufferedReader br = new BufferedReader(new InputStreamReader(con .getInputStream())); String line = ""; for (line = br.readLine(); line != null; line = br.readLine()) { line1+=line; } return new String(line1.getBytes(),"utf-8"); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } /** * 解析xml,返回第一級元素鍵值對。如果第一級元素有子節點,則此節點的值是子節點的xml資料。 * @param strxml * @return * @throws JDOMException * @throws IOException */ public static Map parseXMLToMap(String strxml) throws JDOMException, IOException { strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\""); if(null == strxml || "".equals(strxml)) { return null; } Map m = new HashMap(); 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 k = e.getName(); String v = ""; List children = e.getChildren(); if(children.isEmpty()) { v = e.getTextNormalize(); } else { v =getChildrenText(children); } m.put(k, v); } //關閉流 in.close(); return m; } /** * 62個字母和數字,含大小寫 */ public static final char[] N62_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; public static String MD5Encode(String origin,String charsetName){ String resultString=null; try{ resultString=new String(origin); MessageDigest md=MessageDigest.getInstance("MD5"); if(StringUtils.isBlank(charsetName)){ resultString=byteArrayToHexString(md.digest(resultString.getBytes())); }else{ resultString=byteArrayToHexString(md.digest(resultString.getBytes(charsetName))); } }catch(Exception e){ } return resultString; } public 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(); } /** * 獲取子結點的xml * @param children * @return String */ 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(getChildrenText(list)); } sb.append(value); sb.append("</" + name + ">"); } } return sb.toString(); } public 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 final String hexDigits[]={ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; }

微信支付模擬測試系統(後簡稱模擬系統)的簡化原理圖。模擬系統的API協議與正式API完全相同(API介面文件)。商戶開發者只需將正式API的呼叫URL增加一層sandboxnew路徑,即可對接到模擬系統。

例如,付款碼支付URL:https://api.mch.weixin.qq.com/pay/micropay

變更為:https://api.mch.weixin.qq.com/sandboxnew/pay/micropay。

模擬系統與生產環境完全獨立,包括儲存層。商戶在模擬系統所做的所有交易(如下單、支付、查詢)均為無資金流的假資料,即:使用者無需真實扣款,商戶也不會有資金入賬。代金券同理,沙箱環境中無需商戶真實制券與發券,亦不會出現真實扣券情況。驗收模擬測試系統的API驗籤金鑰需從API獲取: