微信支付開發流程_清晰_易懂_有原始碼
轉自 https://blog.csdn.net/weixin_41497737/article/details/80547243
最近因為公司需求開始開始做微信支付的開發,在網上參考來了很多文章,大多都說微信支付的開發文件真坑怎樣怎樣,做完之後感覺沒那麼坑,都是自己看的不仔細才回導致出了問題。這裡我以JSAPI公眾號支付為例。測試環境使用花生殼的內網穿透來搭建的,還開始擔心殼域名沒有備案,後來發現也是可以的,用花生殼的可以放心使用..
主要分一下幾個大步驟:
一。下載sdk 。其實這個sdk就已經把微信支付的功能封裝在裡面了,已經算是一個成形的程式碼級應用了.sdk已經把要做的工作都做了,剩下的就是你去呼叫了,就這麼簡單。
先看一下這幾個介面和類大概是幹嘛的(不瞭解也絲毫不影響開發):
可以對照自己的SDK來看。
1.IWXPayDomain.java
實現域名管理的,不需要我們做工作。
這個抽象介面,測試包中已經幫助我們實現了,我們可以拿過來直接用。複製貼上改個名字。
2.WXPayConstants
微信支付常量類,這其中的常量很多,但是也都很好理解,內部類列舉,限定了簽名方式只能是MD5或者HMACSHA256。這裡要注意的是,簽名也是有SDK內部實現的,只有在使用到沙箱環境時,才用MD5簽名。最後兩
部分主要是請求字尾,只不過前者是生產環境,後邊的帶有的SANbox的是沙箱測試環境。為了直觀,我把他們都標註出來了。主要分六部分:
你可以把你們公司的名字MD5加密一下做加密或者直接加密的方式,MD5加密。這麼做的好處是,加密源好記,可以防止丟失。
3.WXPayConfig.java
這是一個抽象類,裡邊是一些微信支付的基本配置。是需要你自己繼承並完善的。但是這個實現在SDK自帶的測試包中已經實現了,直接把他複製過來。把自己的配置搞進去。
這一步主要的就是下載證書,在商戶平臺下載證書後,生產環境或者測試的電腦主機才可以呼叫微信支付下載證書後,放到指定位置,在配置一下路徑,很簡單,例如我的:
這樣配置的實現類就完成了。
4.WXPay.java
看名字你就應該知道,這是最重要的類。就是這個類中已經封裝好了所有方法,我們只需要在建立一個類,來呼叫其中的方法就可以了。
5.WXPayUtil.java
工具類,裡邊包含了要用到的方法,很全面。
微信支付介面傳輸資料是通過XML字串來傳輸的,然後再兩端再分別解析成對映結合。這是封裝在內部的我們瞭解一下就可以了。還包括符號的生成,你看,簽名都給你寫好了。
當然,你也可以根據自己的需求在放一些其他的工具方法。
6.request和report我沒怎麼看。看到這裡就足夠了。
二。建立工具類WXPayTool.java
這裡我就直接複製了。排版有點亂,但方便大家。這是測試用,大家可以在改一下。在這裡我還新建立了一個類,也就是OrderData類。在傳參的時候還要多寫一些程式碼,為什麼我要建立這個類呢?我看了微信簽名的生成規則,如果字串為空或者空字串。那麼簽名時會自動過濾掉,不參與簽名。這麼做的好處就是,易於擴充套件,如果以後公司在需要什麼其他的支付方法,也比較方便,在呼叫一個wxpay中的方法,並傳進對應的引數就可以了。我把WXPayTool和OrderData.java的實體類粘貼出來。
公共類WXPayTool {
私人WXPay wxpay;
私人WXPayConfigImpl配置;
公共WXPayTool()丟擲異常{
config = WXPayConfigImpl.getInstance();
wxpay =新的WXPay(config,false,false);
}
/ **
*公眾號支付
*發起支付後--- >>通訊程式碼,必然返回。請求是否成功程式碼。如果成功則不返回或者。
*得到預付單編號
* /
public Map <String,String> doUnifiedOrder(OrderData orderData){
Map <String,String> data = new HashMap <String,String>();
data.put(“body”,orderData.getBody());
data.put(“out_trade_no”,orderData.getout_trade_no());
data.put(“total_fee”,orderData.getTotal_fee());
data.put(“spbill_create_ip”,orderData.getSpbill_create_ip());
data.put(“time_start”,orderData.getTime_start());
data.put(“time_expire”,orderData.getTime_expire());
data.put(“notify_url”,orderData.getNotify_url());
data.put(“trade_type”,orderData.getTrade_type());
data.put(“product_id”,orderData.getProduct_id());
data.put(“openid”,orderData.getOpenid());
嘗試{
Map <String,String> r = wxpay.unifiedOrder(data,1000,1000);
的System.out.println(R);
返回r;
catch(Exception e){
e.printStackTrace();
返回null;
}
}
/ **
*關閉訂單
* @param out_trade_no預付單
* @返回
* /
public Map <String,String> doOrderClose(String out_trade_no){
的System.out.println( “關閉訂單”);
HashMap <String,String> data = new HashMap <String,String>();
data.put(“out_trade_no”,out_trade_no);
嘗試{
Map <String,String> r = wxpay.closeOrder(data);
的System.out.println(R);
返回r;
catch(Exception e){
e.printStackTrace();
返回null;
}
}
public Map <String,String> doOrderQuery(String out_trade_no){
的System.out.println( “查詢訂單”);
HashMap <String,String> data = new HashMap <String,String>();
data.put(“out_trade_no”,out_trade_no);
// data.put(“transaction_id”,“4008852001201608221962061594”);
嘗試{
Map <String,String> r = wxpay.orderQuery(data,1000,1000);
的System.out.println(R);
返回r;
catch(Exception e){
e.printStackTrace();
返回null;
}
}
/ **
*退款
*已測試
* @返回
* /
public Map <String,String> doRefund(String out_trade_no,String total_fee){
HashMap <String,String> data = new HashMap <String,String>();
data.put(“out_trade_no”,out_trade_no);
data.put(“out_refund_no”,out_trade_no);
data.put(“total_fee”,total_fee);
data.put(“refund_fee”,total_fee);
data.put(“refund_fee_type”,“CNY”);
//data.put(“op_user_id”,config.getMchID());
嘗試{
Map <String,String> r = wxpay.refund(data);
的System.out.println(R);
返回r;
catch(Exception e){
e.printStackTrace();
返回null;
}
}
/ **
*查詢退款
*已經測試
* @返回
* /
public Map <String,String> doRefundQuery(String out_trade_no){
HashMap <String,String> data = new HashMap <String,String>();
data.put(“out_refund_no”,out_trade_no);
嘗試{
Map <String,String> r = wxpay.refundQuery(data);
的System.out.println(R);
返回r;
catch(Exception e){
e.printStackTrace();
返回null;
}
}
/ **
*對賬單下載
*已測試
* @返回
* /
public Map <String,String> doDownloadBill(long time){
HashMap <String,String> data = new HashMap <String,String>();
data.put(“bill_date”,WXPayUtil.generateBillDateStrByLong(time));
data.put(“bill_type”,“ALL”);
嘗試{
Map <String,String> r = wxpay.downloadBill(data);
的System.out.println(R);
返回r;
catch(Exception e){
e.printStackTrace();
返回null;
}
}
}
/ **
*微信支付統一下單,所用到的引數,全部為String
* /
公共類OrderData擴充套件BaseEntity實現Serializable {
/ *引數說明---->
* 1。這裡包含的引數,是出去的配置中的其他引數,配置中包含了
* APPID,mchid,標誌,signtype,nonce_str
* 2。餘下的是下面這些,***標記的為必填項
* 3。引數的初始值都是“”空字串
* 因為在微信支付官方的SDK中,實現了引數為空不參與簽名
* 4。把所有引數都實現的目的是,易於擴充套件
* 5。由於我們的使用場景,是JSAPI微信公眾號支付,我們把要選擇引數標記一下用###來標識
* /
private String device_info =“”; //裝置號
private String body =“”; //商品簡單描述維康動力 - 醫療###
private String detail =“”; //商品詳情
私人字串附加=“”; //附加資料,在查詢時,原樣返回待定
private String out_trade_no =“”; //訂單號***
private String fee_type =“”; //型別,不傳,預設為CNY
private String total_fee =“”; //總計多少錢***
private String spbill_create_ip =“”; //使用者端ip,必傳***
private String time_start =“”; // ###訂單生成時間--->這裡指的是預付單生成時間###
private String time_expire =“”; // ###訂單失效時間--->預付單失效後,如果使用者還要提交支付,需要發起重新請求訂單介面,獲得新的預付單ID ###
private String goods_tag =“”; //訂單優惠說明待定
私人字串notify_url =“”; //回撥地址***
private String trade_type =“”; // JSAPI ***
私人字串product_id =“”; //商品ID,###
private String limit_pay =“”; //非信用卡支付
private String openid =“”; //使用者的OpenID ***
private String scene_info =“”; //場景資訊
三。具體流程
上一張圖,這是我測試用的前端頁面,這樣看起來也比較清楚。
第一個介面,也就是統一下單介面。這一步相當於使用者選中了一個商品,並生成了訂單,而這個訂單就是預付單。這裡有幾個需要注意的地方
1)spbill_create_ip的填寫文件中指的是客戶端的IP,使用了SDK提供的方法WXPayUtil.getCustomerIp(請求)。但是貌似沒有什麼效果,直接用的127.0.0.1也沒問題。微信支付系統雖然要求傳這個引數,但貌似對這個引數沒有多大的處理。
下面的開始時間和結束時間,微信支付是有要求的,格式為YYYYMMDDHHMMSS,下面的兩個方法是我封裝的方法。
2)在統一下單提交的資料中要新增notify_url。這是在使用者支付成功之後,微信支付系統返回給系統的資料。以XML字串資料流在HTTP實體中傳過來。這是很重要的,在這個過程中,我們需要把返回的資料與平臺內資料做對比,檢視資訊是否準確。並且校驗之後要把資訊返回給微信支付系統的。如果配置了過濾器過濾器,別忘了放了這個方法。
這個介面主要獲得的就是prepay_id預付單ID,有了預付單號之後就可以進行下一步,在JS中調起微信支付控制元件了。
大概是這樣的:
3)在呼叫返回的資料是Map集合,這樣傳到前端解析時有問題的,所以使用JSON.fromObject(map)解析成net.json,再傳到前端就不會有問題了,後續的介面這些也是需要注意的。
第二步,已經有了訂單,使用者決定要不要支付。在JS中調起控制元件,發起支付。點擊發起支付,輸入密碼付款成功。這裡需要注意的是
1)package引數,packge引數的內部是prepay_id ='prepay_id'。在這一步測試時,最好用iphone測試,我在使用安卓時,出錯了但是沒有任何提示。在使用蘋果測試時,彈出了錯誤資訊,total_fee引數錯誤,但是在我們傳的簽名引數中根本就沒有total_fee這個引數。這就比較奇怪了,再次檢視文件,發現還是自己看的不仔細。
主要檢查了下prepay_id,我傳引數的時候格式不對,pacakge是js中的關鍵字,我把prepay_id傳到了後臺,拼接出鍵值對:package:'prepay_id = xxxxxx',在前端取值時就出了問題。所以我直接把prepay_id設定為js的全域性變數。在js傳參時直接把這個引數傳進去。
2)在這裡我引入了一個獲取JS呼叫的引數的方法,這個方法也很簡單,直接把prepay_id傳入後端,用SDK生成簽名。這裡要注意的是signType不是MD5.MD5是沙箱測試才會使用簽名方式。
第三步查詢訂單。,查詢訂單是可以根據微信生成的訂單號或者平臺自己的訂單號來查詢當前訂單的狀態的這裡要注意的是:
1)要先分清幾個單號的概念。預付單ID,商戶訂單ID,微信系統訂單ID。商戶訂單,這裡商戶訂單是平臺自己的自己生成的,在平臺中標識,並且也傳到微信系統中。預付單,在把商品資訊提交後產生的預付款的單號,只是在微信系統中生成了訂單,但是還沒有支付。而微信系統訂單則是已經支付完成之後的單號。
開始的時候,我是用預付單查詢訂單,但是報錯為引數長度有誤。開發文件中寫的是微信系統訂單和商戶訂單二選一,並且長度都是32位的。後來我做了一個騷操作,數了一下預付單ID的長度,的確的確,它是31位的!後來嘗試用支付成功後返回的微信系統訂單來查詢訂單,發現還是不行的.