微信公衆號支付大致分爲以下幾步:
1.獲取openID:微信公衆號支付一個重要的參數就是openID,此步驟可以查閱相關文檔
2.獲取支付相關參數:appid:公衆賬號ID,mch_id:商戶號,nonce_str:隨機字符串,sign_type:簽名類型,out_trade_no:商戶訂單號,total_fee:標價金額(交易金額默認爲人民幣交易,接口中參數支付金額單位爲【分】,參數值不能帶小數),spbill_create_ip:終端IP,notify_url:通知地址,trade_type:交易類型,openid:用戶標識,sign:簽名
TreeMap paramMap = new TreeMap();
paramMap.put("appid", PaymentConst.WECHAT_APPID);//公衆賬號ID
paramMap.put("mch_id", PaymentConst.WECHAT_MCHID);//商戶號
paramMap.put("nonce_str", Util.getRandomString(30, null));//隨機字符串
paramMap.put("sign_type", PaymentConst.WECHAT_SIGNTYPE);//簽名類型
//paramMap.put("body", "愛檢查醫療套餐支付");//商品描述
paramMap.put("body", "Melical Fee");//商品描述
paramMap.put("out_trade_no", orderMain.getNumber());//商戶訂單號
//交易金額默認爲人民幣交易,接口中參數支付金額單位爲【分】,參數值不能帶小數。
paramMap.put("total_fee", (int)(orderMain.getTotalamount()*100));//標價金額
paramMap.put("spbill_create_ip", Util.getIpAddr(request));//終端IP
paramMap.put("notify_url", weChatNotifyUrl);//通知地址
paramMap.put("trade_type", PaymentConst.WECHAT_TRADETYPE_JSAPI);//交易類型
paramMap.put("openid", openid);//用戶標識
paramMap.put("sign",Signature.createSign("UTF-8", paramMap));//簽名
3.簽名算法:
public static String createSign(String characterEncoding,SortedMap<String,Object> parameters){
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
Object v = entry.getValue();
if(null != v && !"".equals(v)
&& !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + PaymentConst.WECHAT_KEY);
System.out.println("簽名:"+sb.toString());
String sign =MD5.GetMD5Code(sb.toString()).toUpperCase();
return sign;
}
4.請求:
String response = HttpClientUtil.post(PaymentConst.wechatPayUrl,new String( XMLUtils.simpleMapToXml(paramMap).getBytes(), "UTF-8"));
5.XMLUtils.simpleMapToXml 方法:
public static String simpleMapToXml(Map<String, Object> map){
StringBuilder xmlString = new StringBuilder();
xmlString.append("<?xml version='1.0' encoding='UTF-8'?>").append(RT);
xmlString.append("<xml>").append(RT);
for (Map.Entry<String, Object> entry : map.entrySet()){
String key = entry.getKey();
xmlString.append("<" + key + ">" + entry.getValue() + "</" + key + ">").append(RT);
}
xmlString.append("</xml>").append(RT);
return xmlString.toString();
}
6.返回預支付結果,二次簽名
System.out.println("預支付返回結果:"+response);
Map<String, String> resultParams = getWechatXmlToMap(response);
if (PaymentConst.WECHAT_STATUSCODE_FAIL.equals(resultParams.get("return_code"))){
throw new MessageException(resultParams.get("return_msg"));
}else{
if (PaymentConst.WECHAT_STATUSCODE_FAIL.equals(resultParams.get("result_code"))){
throw new MessageException(resultParams.get("return_msg"));
}else{
String prepay_id = resultParams.get("prepay_id");
TreeMap paramMap1 = new TreeMap();
paramMap1.put("appId", paramMap.get("appid"));//公衆賬號ID
String s = String.valueOf(Util.getSecond());
paramMap1.put("timeStamp", s);
paramMap1.put("nonceStr", Util.getRandomString(30, null));
paramMap1.put("package", "prepay_id="+prepay_id);//簽名類型
paramMap1.put("signType", paramMap.get("sign_type"));//簽名類型
paramMap1.put("sign1",Signature.createSign("UTF-8", paramMap1));//簽名
LOGGER.info("最終簽名:"+paramMap1.get("sign1"));
//生成前臺支付需要的數據
Map<String, Object> map = new HashMap<String, Object>();
map.put("appId", paramMap.get("appid"));
String s1 = String.valueOf(Util.getSecond());
map.put("timeStamp", s1);
map.put("nonceStr", Util.getRandomString(30, null));
map.put("package", "prepay_id="+prepay_id);
map.put("signType", paramMap.get("sign_type"));
map.put("paySign", paramMap1.get("sign1"));
System.out.println("tttttttttttttttttttttttttttttt9999999999999tttttttttttttttttttttttttttttttttttttt");
result.setT(map);
}
}
7./**
* @Title:getWechatXmlToMap
* @Description:微信返回xml結果轉爲Map
* @param result
* @return
* @throws Exception Map<String,String> 返回類型
*/
private Map<String, String> getWechatXmlToMap(String result) throws Exception{
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
XMLHandler xmlHandler = new XMLHandler();
parser.parse(new ByteArrayInputStream(result.getBytes("UTF-8")), xmlHandler);
return xmlHandler.getParams();
}
8.授權成功回調方法
/**
* @Title:weixinPayBack
* @Description:支付回調(微信支付)
* @param request
* @return String 返回類型
*/
@RequestMapping(value="/weiXinPayNotify")
public String weixinPayBack(HttpServletRequest request){
try {
InputStream inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
outSteam.close();
inStream.close();
String resultStr = new String(outSteam.toByteArray(),"utf-8");
System.out.println("微信支付的回調:"+resultStr);
Map<String, String> resultMap = getWechatXmlToMap(resultStr);
String return_code = resultMap.get("return_code");
if("SUCCESS".equals(return_code)){
//檢驗API返回的數據裏面的簽名是否合法,避免數據在傳輸的過程中被第三方篡改
if(!Signature.checkIsSignValidFromResponseString(resultStr)){
throw new MessageException("微信回調參數簽名不合法");
}
String result_code = resultMap.get("result_code");
if("SUCCESS".equals(result_code)){
//支付成功的業務邏輯
//通知微信.異步確認成功.必寫.不然微信會一直通知後臺.八次之後就認爲交易失敗了.
return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
}
}
} catch (Exception e) {
LOGGER.error(e.getMessage() ,e);
}
return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[ERROR]]></return_msg></xml>";
}
9.簽名錯誤測試方法:
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=20_1
填入如下參數
點擊生成簽名:出現如下xml
複製xml:
重新選擇xml校驗方式
生成簽名出現下面的提示,校驗通過,說明簽名正確
其他簽名錯誤情況檢查參數是否正確