1. 程式人生 > >微信支付之前的統一下單

微信支付之前的統一下單

stream date() ive area 封裝微信 流轉 算法 tag ntb

前言:想調用微信支付的小夥伴們,在看我給予的案例之前,我們先看懂微信的支付流程。

我總結了一下,就比較簡單了(要看明細流轉,就到微信官網)【微信簽名這一塊我們拿出來單獨簡介】【報酬求助聯系:1124904642】

1.客戶下單,該單據保存在自己的庫存中

2.在點擊確認支付的時候,調用微信的統一下單接口

3.統一下單接口會根據你提供的回調接口反饋統一下單信息,自己去解析返回的XML術語對比是否成功,成功與否,把信息返回給微信(微信會反復回調你的接口至少兩次,確保統一下單成功)

4.告訴微信,統一下單成功後,微信會返回成功之後的信息,自己解析XML術語,根據術語中的字段,生成微信調用SDK的必要參數.

一.微信統一下單

1.統一下單接口講解

統一下單接口:https://api.mch.weixin.qq.com/pay/unifiedorder

請求參數

字段名變量名必填類型示例值描述
公眾賬號ID appid String(32) wxd678efh567hg6787 微信支付分配的公眾賬號ID(企業號corpid即為此appId)
商戶號 mch_id String(32) 1230000109 微信支付分配的商戶號
設備號 device_info String(32) 013467007045764 自定義參數,可以為終端設備號(門店號或收銀設備ID),PC網頁或公眾號內支付可以傳"WEB"
隨機字符串 nonce_str String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 隨機字符串,長度要求在32位以內。推薦隨機數生成算法
簽名 sign String(32) C380BEC2BFD727A4B6845133519F3AD6 通過簽名算法計算得出的簽名值,詳見簽名生成算法
簽名類型 sign_type String(32) MD5 簽名類型,默認為MD5,支持HMAC-SHA256和MD5。
商品描述 body String(128) 騰訊充值中心-QQ會員充值

商品簡單描述,該字段請按照規範傳遞,具體請見參數規定

商品詳情 detail String(6000) 商品詳細描述,對於使用單品優惠的商戶,改字段必須按照規範上傳,詳見“單品優惠參數說明”
附加數據 attach String(127) 深圳分店 附加數據,在查詢API和支付通知中原樣返回,可作為自定義參數使用。
商戶訂單號 out_trade_no String(32) 20150806125346 商戶系統內部訂單號,要求32個字符內,只能是數字、大小寫字母_-|*@ ,且在同一個商戶號下唯一。詳見商戶訂單號
標價幣種 fee_type String(16) CNY 符合ISO 4217標準的三位字母代碼,默認人民幣:CNY,詳細列表請參見貨幣類型
標價金額 total_fee Int 88 訂單總金額,單位為分,詳見支付金額
終端IP spbill_create_ip String(16) 123.12.12.123 APP和網頁支付提交用戶端ip,Native支付填調用微信支付API的機器IP。
交易起始時間 time_start String(14) 20091225091010 訂單生成時間,格式為yyyyMMddHHmmss,如2009年12月25日9點10分10秒表示為20091225091010。其他詳見時間規則
交易結束時間 time_expire String(14) 20091227091010

訂單失效時間,格式為yyyyMMddHHmmss,如2009年12月27日9點10分10秒表示為20091227091010。訂單失效時間是針對訂單號而言的,由於在請求支付的時候有一個必傳參數prepay_id只有兩小時的有效期,所以在重入時間超過2小時的時候需要重新請求下單接口獲取新的prepay_id。其他詳見時間規則

建議:最短失效時間間隔大於1分鐘

訂單優惠標記 goods_tag String(32) WXG 訂單優惠標記,使用代金券或立減優惠功能時需要的參數,說明詳見代金券或立減優惠
通知地址 notify_url String(256) http://www.weixin.qq.com/wxpay/pay.php 異步接收微信支付結果通知的回調地址,通知url必須為外網可訪問的url,不能攜帶參數。
交易類型 trade_type String(16) JSAPI 取值如下:JSAPI,NATIVE,APP等,說明詳見參數規定
商品ID product_id String(32) 12235413214070356458058 trade_type=NATIVE時(即掃碼支付),此參數必傳。此參數為二維碼中包含的商品ID,商戶自行定義。
指定支付方式 limit_pay String(32) no_credit 上傳此參數no_credit--可限制用戶不能使用信用卡支付
用戶標識 openid String(128) oUpF8uMuAJO_M2pxb1Q9zNjWeS6o trade_type=JSAPI時(即公眾號支付),此參數必傳,此參數為微信用戶在商戶對應appid下的唯一標識。openid如何獲取,可參考【獲取openid】。企業號請使用【企業號OAuth2.0接口】獲取企業號內成員userid,再調用【企業號userid轉openid接口】進行轉換
+場景信息 scene_info String(256)

{"store_info" : {
"id": "SZTX001",
"name": "騰大餐廳",
"area_code": "440305",
"address": "科技園中一路騰訊大廈" }}

該字段用於上報場景信息,目前支持上報實際門店信息。該字段為JSON對象數據,對象格式為{"store_info":{"id": "門店ID","name": "名稱","area_code": "編碼","address": "地址" }} ,字段詳細說明請點擊行前的+展開

將參數構造成XML字符串寫入到請求微信接口的請求正文中(xml字符串示例)

<xml>
<appid>wx2421b1c4370ec43b</appid>
<attach>支付測試</attach>
<body>JSAPI支付測試</body>
<mch_id>10000100</mch_id>
<detail><![CDATA[{ "goods_detail":[ { "goods_id":"iphone6s_16G", "wxpay_goods_id":"1001", "goods_name":"iPhone6s 16G", "quantity":1, "price":528800, "goods_category":"123456", "body":"蘋果手機" }, { "goods_id":"iphone6s_32G", "wxpay_goods_id":"1002", "goods_name":"iPhone6s 32G", "quantity":1, "price":608800, "goods_category":"123789", "body":"蘋果手機" } ] }]]></detail>
<nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
<notify_url>http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php</notify_url>
<openid>oUpF8uMuAJO_M2pxb1Q9zNjWeS6o</openid>
<out_trade_no>1415659990</out_trade_no>
<spbill_create_ip>14.23.150.211</spbill_create_ip>
<total_fee>1</total_fee>
<trade_type>JSAPI</trade_type>
<sign>0CB01533B8C1EF103065174F50BCA001</sign>
</xml>

2.notify_url:回調(通知地址接口)

微信解析XML字符串信息之後會調用notify_url,回調接口,告訴客戶同意下下單是否成功。

3.統一下單成功之後,根據微信返回的信息中構造調用微信SDK需要用到的參數,返回給前端,前端利用SDK調用支付的接口。

代碼案例:

//微信支付需要的參數返回
public static boolean weChat(Map<String,Object> map,Map<String,Object> errorMap){
//微信統一下單
if(!PublicUtil.unifiedOrder(map,errorMap)){
errorMap.put("error",ResMessage.Server_Abnormal.code);
return true;
}
//下單成構造四個參數調用SDK
JSONObject jsonObject= weChatMaps(map);
map.clear();
jsonObjectConversionMap(jsonObject, map);
return false;
}

unifiedOrder:

/**
*
* @Title: unifiedOrder
* @Description: TODO
* @param @return
* @return boolean
* @throws
* 微信統一下單接口
*/
public static boolean unifiedOrder(Map<String,Object> map,Map<String,Object> errorMap){
Map<String,Object> mapstem = new HashMap<String,Object>();
map.put("body","******");
map.put("time_start",DateForamtUtil.to_YYYYMMddHHmmss_str(new Date())); //交易起始時間
map.put("time_expire",DateForamtUtil.to_YYYYMMddHHmmss_str(DateForamtUtil.addMinute(new Date(), 10))); //交易結束起始時間
//封裝微信端要求的參數
Map<String,Object> mapParameter =encapsulatedData(map);//封裝微信端要求的參數
//封裝成XML,轉成字符串
String spliceXml=spliceXml(mapParameter);
try{
//訪問微信接口
String strResult =getHttpRequest(Configure.UNIFIEDORDER_API, null, spliceXml);
//解析微信返回的XML
mapstem=XMLParser.getMapFromXML(spliceXml);
if(mapstem==null || mapstem.size()==0){
errorMap.put("error",ResMessage.comment_autograph_notnull.code);
return true;
}
map.put("prepay_id",String.valueOf(mapstem.get("mapstem")));
Map<String,Object> maps=weChatMaps(map);
return false;
} catch (MalformedURLException e){
e.printStackTrace();
return true;
} catch (IOException e){
e.printStackTrace();
return true;
} catch (ParserConfigurationException e){
// TODO Auto-generated catch block
e.printStackTrace();
return true;
} catch (SAXException e){
// TODO Auto-generated catch block
e.printStackTrace();
return true;
}

}

encapsulatedData:

/**
*
* @Title: encapsulatedData
* @Description: TODO
* @param
* @return void
* @throws
* 構造微信統一下單的數據參數
*/
public static Map<String,Object> encapsulatedData(Map<String,Object> map){
Map<String,Object> mapParameter = new HashMap<String,Object>();
mapParameter.put("appid", Configure.getAppid());//微信分配的公眾號ID
mapParameter.put("mch_id", Configure.getMchid());//微信支付分配的商戶號ID
mapParameter.put("nonce_str",UUID.randomUUID().toString().replace("-",""));//隨機字符串32位
mapParameter.put("body", String.valueOf(map.get("body")));//商品描述
mapParameter.put("out_trade_no", String.valueOf(map.get("strorder")));//微信支付分配的商戶號ID
String dbmoney=String.valueOf(Float.parseFloat(String.valueOf(map.get("dbmoney")))*100);
mapParameter.put("total_fee",dbmoney.substring(0,dbmoney.length()-2));//標價金額
mapParameter.put("time_start", String.valueOf(map.get("time_start")));
mapParameter.put("time_expire", String.valueOf(map.get("time_expire")));
mapParameter.put("openid", String.valueOf(map.get("struserid")));//用戶的openid
mapParameter.put("spbill_create_ip", String.valueOf(map.get("spbill_create_ip")));//終端IP request.getRemoteAddr()
mapParameter.put("notify_url", "http://autotest.xiaobaoche.net/weixin/paynt");//通知地址
mapParameter.put("trade_type", "JSAPI");//支付類型
String sign=sign(mapParameter);
mapParameter.put("sign",sign);//簽名(通過簽名算法計算得出的簽名值,詳見簽名生成算法)
return mapParameter;
}

構造XNL字符串 spliceXml:

/**
*
* @Title: spliceXml
* @Description: TODO
* @param @return
* @return String
* @throws
* 拼接微信要求的XML格式,憑借XML時,最好用轉義符號 <![CDATA[]]>
*/
public static String spliceXml(Map<String,Object> map){
StringBuffer spliceXml=new StringBuffer();
spliceXml.append("<xml>");
spliceXml.append("<appid><![CDATA[").append(String.valueOf(map.get("appid"))).append("]]</appid>");
spliceXml.append("<mch_id><![CDATA[").append(String.valueOf(map.get("mch_id"))).append("]]</mch_id>");
spliceXml.append("<nonce_str><![CDATA[").append(String.valueOf(map.get("nonce_str"))).append("]]</nonce_str>");
spliceXml.append("<body><![CDATA[").append(String.valueOf(map.get("body"))).append("]]</body>");
spliceXml.append("<out_trade_no><![CDATA[").append(String.valueOf(map.get("out_trade_no"))).append("]]</out_trade_no>");
spliceXml.append("<total_fee><![CDATA[").append(String.valueOf(map.get("total_fee"))).append("]]</total_fee>");
spliceXml.append("<time_start><![CDATA[").append(String.valueOf(map.get("time_start"))).append("]]</time_start>");
spliceXml.append("<time_expire><![CDATA[").append(String.valueOf(map.get("time_expire"))).append("]]</time_expire>");
spliceXml.append("<appid><![CDATA[").append(String.valueOf(map.get("spbill_create_ip"))).append("]]</appid>");
spliceXml.append("<spbill_create_ip><![CDATA[").append(String.valueOf(map.get("notify_url"))).append("]]</spbill_create_ip>");
spliceXml.append("<trade_type><![CDATA[").append(String.valueOf(map.get("trade_type"))).append("]]</trade_type>");
spliceXml.append("<sign><![CDATA[").append(String.valueOf(map.get("sign"))).append("]]</sign>");
spliceXml.append("</xml>");
return spliceXml.toString();
}

訪問微信接口的方法

/**
* 獲取http請求的數據
* @return
* @throws IOException
* @throws MalformedURLException
* 發起請求的方法
*/
public static String getHttpRequest(String url, String param, String contentType) throws MalformedURLException, IOException{
String result = "";
HttpURLConnection connection = null;
connection = (HttpURLConnection) new URL(url).openConnection();
connection.setDoOutput(true);// 設置連接輸出流為true,默認false (post
// 請求是以流的方式隱式的傳遞參數)
connection.setDoInput(true); // 設置連接輸入流為true
connection.setRequestProperty("Accept-Charset", "utf-8");
connection.setRequestProperty("contentType", "utf-8");
connection.setRequestProperty("contentType", "utf-8");
connection.setUseCaches(false); // post請求緩存設為false
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); // application/x-www-form-urlencoded->表單數據
connection.connect(); // 建立連接
// application/json;charset=UTF-8 application/x-www-form-urlencoded
if(contentType != null){
connection.setRequestProperty("contentType", contentType);
}
//參數字符串
if(param != null){
OutputStream out = connection.getOutputStream(); // 創建輸入輸出流
out.write(param.getBytes("UTF-8")); // 將參數輸出到連接
out.flush(); // 輸出完成後刷新並關閉流
out.close(); // 重要且易忽略步驟 (關閉流,切記!)
}
InputStream fin = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(fin, "utf-8"));
String str = reader.readLine();
while (str != null){
result += str;
str = reader.readLine();
}
return result;
}


getMapFromXML:解析微信端返回的XML

/**
*
* @Title: getMapFromXML
* @Description: TODO
* @param @param xmlString
* @param @return
* @param @throws ParserConfigurationException
* @param @throws IOException
* @param @throws SAXException
* @return Map<String,Object>
* @throws
* 解析XML,返回MAP
*/
public static Map<String,Object> getMapFromXML(String xmlString) throws ParserConfigurationException, IOException, SAXException {
//這裏用Dom的方式解析回包的最主要目的是防止API新增回包字段
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputStream is = Util.getStringStream(xmlString);
Document document = null;
try{
document = builder.parse(is);
}catch(Exception e){

}
//獲取到document裏面的全部結點
NodeList allNodes = document.getFirstChild().getChildNodes();
Node node;
Map<String, Object> map = new HashMap<String, Object>();
int i=0;
while (i < allNodes.getLength()) {
node = allNodes.item(i);
if(node instanceof Element){
map.put(node.getNodeName(),node.getTextContent());
}
i++;
}
return map;

}

構造微信調用SDK支付需要用到的參數

//------------------構造微信支付需要的參數開始--------------------------------------------------------------
public static JSONObject weChatMaps(Map<String,Object> maps){
//構造做微信支付時需要用到的參數
JSONObject jsonObject=new JSONObject();
jsonObject.put("appId",Configure.getAppid());//公眾號APPID
jsonObject.put("timeStamp",String.valueOf(System.currentTimeMillis() / 1000));
jsonObject.put("nonceStr",PublicTool.getRandomUUID(32));
jsonObject.put("package","prepay_id="+ String.valueOf(maps.get("prepay_id")));
jsonObject.put("signType","md5");
Map<String,Object> map = jsonObject;
String signs = sign(map);//簽名
jsonObject.put("paySign",signs);
return jsonObject;
}
//------------------構造微信支付需要的參數結束--------------------------------------------------------------

微信支付之前的統一下單