支付寶手機網站支付流程(Node實現)


公司M站要接入支付寶,借機研究了一下支付寶的支付流程。畢竟,只有公司才能拿到支付接口權限。

主要參考文檔:
https://doc.open.alipay.com/doc2/detail?treeId=60&articleId=103564&docType=1
https://b.alipay.com/order/productDetail.htm?productId=2015110218008816

手機網站支付接口

產品簡介

手機網站支付主要應用於手機、掌上電腦等無線設備的網頁上,通過網頁跳轉或瀏覽器自帶的支付寶快捷支付實現買家付款的功能,資金即時到賬。

申請條件

1、您申請前必須擁有企業支付寶賬號(不含個體工商戶賬號),且通過支付寶實名認證審核。
2、如果您有已經建設完成的無線網站(非淘寶、天貓、誠信通網店),網站經營的商品或服務內容明確、完整(古玩、珠寶等奢侈品、投資類行業無法申請本產品)。
3、網站必須已通過ICP備案,備案信息與簽約商戶信息一致。

配置

假設我們已經成功申請到手機網站支付接口,在進行開發之前,需要使用公司賬號登錄支付寶開放平台。

查看PID

1、開發者登錄開放平台,點擊右上角的“賬戶及密鑰管理”。

2、選擇“合作伙伴密鑰”,即可查詢到合作伙伴身份(PID),以2088開頭的16位純數字。

配置密鑰

支付寶提供了DSA、RSA、MD5三種簽名方式,本次開發中,我們使用RSA簽名和加密,那就只配置RSA密鑰就好了。

關於RSA加密的詳解,參見《支付寶簽名與驗簽》。

應用環境

本節可以忽略,本節可以忽略,本節可以忽略!因為官方文檔並沒有提及應用環境配置的問題。

進入管理中心,對應用進行設置。


上圖是我的應用配置選項,公司賬號也許會有所不同。具體哪些參數需要配置?請參照接口參數說明,需要什么就配置什么。

交互

Node端發起支付請求


我們公司采用的,就是這種方式,步驟3中Node端獲取到的支付寶參數,包括sign。其實,sign的計算工作也可以放在Node端,只不過支付寶沒有給出Node的demo,實現起來需要耗費多一點時間。

后端發起支付請求


這種方式也很好,而且,步驟4中后端獲取到支付頁面,也可以不傳給Node端,自己顯示出來。這樣,整個流程就更加簡單。

return_url和notify_url

return_url,支付完成后的回調url;notify_url,支付完成后通知的url。支付寶發送給兩個url的參數是一樣的,只不過一個是get,一個是post。

以上兩種發起請求的方式中,return_url在Node端,notify_url在后端。我們也可以根據需要,把兩個url都放在后端,或者都放在Node端,改變相應業務邏輯即可。

Node端詳解

Node端發起支付請求有兩種選擇,一種是獲取到后端給的參數后,通過request模塊發起get請求,獲取到支付寶返回的支付頁面,然后顯示到 頁面上;另一種是獲取到后端給的參數后,把參數全部輸出到頁面中的form表單,然后通過js自動提交表單,獲取到支付寶返回的支付頁面(同時顯示出 來)。

request發起請求

// 通過orderId向后端請求獲取支付寶支付參數alidata var alipayUrl = 'https://mapi.alipay.com/gateway.do?'+ alidata; request.get({url: alipayUrl},function(error, response, body){ res.send(response.body); });

理論上完全正確的請求,然而,獲取到的支付頁面,輸出到頁面上,卻是亂碼。沒錯,還是一個錯誤提示頁面。

神奇的地方在於,在刷新頁面多次后,正常了!!!啊嘞,這是什么鬼?

先解決亂碼問題,看看報什么錯!

request.get({url: alipayUrl},function(error, response, body){ var str = response2.body; str = str.replace(/gb2312/, "utf-8"); res.setHeader('content-type', 'text/html;charset=utf-8'); res.send(str); });

很遺憾,無效!亂碼依然是亂碼。。。和沈晨帥哥討論很久,最終決定換一種方案——利用表單提交。

表單提交請求

Node端

// node端 // 通過orderId向后端請求獲取支付寶支付參數alidata function getArg(str,arg) { var reg = new RegExp('(^|&)' + arg + '=([^&]*)(&|$)', 'i'); var r = str.match(reg); if (r != null) { return unescape(r[2]); } return null; } var alipayParam = { _input_charset: getArg(alidata,'_input_charset'), body: getArg(alidata,'body'), it_b_pay: getArg(alidata, 'it_b_pay'), notify_url: getArg(alidata, 'notify_url'), out_trade_no: getArg(alidata, 'out_trade_no'), partner: getArg(alidata, 'partner'), payment_type: getArg(alidata, 'payment_type'), return_url: getArg(alidata, 'return_url'), seller_id: getArg(alidata, 'seller_id'), service: getArg(alidata, 'service'), show_url: getArg(alidata, 'show_url'), subject: getArg(alidata, 'subject'), total_fee: getArg(alidata, 'total_fee'), sign_type: getArg(alidata, 'sign_type'), sign: getArg(alidata, 'sign'), app_pay: getArg(alidata, 'app_pay') }; res.render('artist/alipay',{ // 其他參數 alipayParam: alipayParam }); 

html

<!--alipay.html--> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>支付寶支付</title> </head> <body> <form id="ali-form" action="https://mapi.alipay.com/gateway.do" method="get"> <input type="hidden" name="_input_charset" value="<%= alipayParam._input_charset%>"> <input type="hidden" name="body" value="<%= alipayParam.body%>"> <input type="hidden" name="it_b_pay" value="<%= alipayParam.it_b_pay%>"> <input type="hidden" name="notify_url" value="<%= alipayParam.notify_url%>"> <input type="hidden" name="out_trade_no" value="<%= alipayParam.out_trade_no%>"> <input type="hidden" name="partner" value="<%= alipayParam.partner%>"> <input type="hidden" name="payment_type" value="<%= alipayParam.payment_type%>"> <input type="hidden" name="return_url" value="<%= alipayParam.return_url%>"> <input type="hidden" name="seller_id" value="<%= alipayParam.seller_id%>"> <input type="hidden" name="service" value="<%= alipayParam.service%>"> <input type="hidden" name="show_url" value="<%= alipayParam.show_url%>"> <input type="hidden" name="subject" value="<%= alipayParam.subject%>"> <input type="hidden" name="total_fee" value="<%= alipayParam.total_fee%>"> <input type="hidden" name="sign_type" value="<%= alipayParam.sign_type%>"> <input type="hidden" name="sign" value="<%= alipayParam.sign%>"> <input type="hidden" name="app_pay" value="<%= alipayParam.app_pay%>"> </form> <% include ../bootstrap.html %> <script type="text/javascript" src="<%= dist %>/js/business-modules/artist/alipay.js?v=2016052401"></script> </body> </html>

js

// alipay.js seajs.use(['zepto'],function($){ var index = { init:function(){ var self = this; this.bindEvent(); }, bindEvent:function(){ var self = this; $('#ali-form').submit(); } } index.init(); });

小結


選擇支付寶支付后,成功跳轉到了支付寶支付頁面,nice!看來這種方案很靠譜。

開始時,打算把alidata直接輸出到form表單的action中接口的后面,因為這樣似乎最簡便。但是,提交表單時,后面的參數全部被丟棄 了。所以,不得不得把所有參數放在form表單中。Node端拆分了一下參數,組裝成了一個alipayParam對象,這個工作也可以交給后端來做。

顯然,request模擬表單提交和真實表單提交結果的不同,得出的結論是,request並不能完全模擬表單提交。或者,request可以模擬,而我不會-_-|||。

錯誤排查


值得點贊的是,支付寶給的錯誤代碼很明確,一看就懂。上面這個錯誤,簽名不對。因為我給alipayParam私自加了個app_pay屬性,沒有經過簽名。

微信屏蔽支付寶

問題描述

以上,大功告成?不!還有一個坑要填,因為微信屏蔽了支付寶!
在電腦上,跳轉支付寶支付頁面正常,很完美!然而,在微信瀏覽器中測試時,卻沒有跳轉,而是出現如下信息。

完美解決辦法

微信端支付寶支付,iframe改造
http://www.cnblogs.com/jiqing9006/p/5584268.html

該辦法的核心在於:把微信屏蔽的鏈接,賦值給iframe的src屬性。

Node端

res.render('artist/alipay',{  alipayParam: alipayParam,  param: urlencode(alidata) });

html

<input type="hidden" id="param" value="<%= param%>"> <iframe id="payFrame" name="mainIframe" src="" frameborder="0" scrolling="auto" ></iframe>

js

var iframe = document.getElementById('payFrame'); var param = $('#param').val(); iframe.src='https://mapi.alipay.com/gateway.do?'+param;

報錯

然而,在改造時,先是報錯ILLEGAL_SIGN,於是利用urlencode處理了字符串。接着,又報錯ILLEGAL_EXTERFACE,沒有找到解決辦法。

暫時放棄,以后如果有了解決辦法再補上。

官方解決辦法

關於微信公眾平台無法使用支付寶收付款的解決方案說明
https://cshall.alipay.com/enterprise/help_detail.htm?help_id=524702

該方法的核心在於:確認支付時,提示用戶打開外部系統瀏覽器,在系統瀏覽器中支付。

html

<!--alipay.html--> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>支付寶支付</title> </head> <body> <form id="ali-form" action="https://mapi.alipay.com/gateway.do" method="get"> <input type="hidden" name="_input_charset" value="<%= alipayParam._input_charset%>"> <input type="hidden" name="body" value="<%= alipayParam.body%>"> <input type="hidden" name="it_b_pay" value="<%= alipayParam.it_b_pay%>"> <input type="hidden" name="notify_url" value="<%= alipayParam.notify_url%>"> <input type="hidden" name="out_trade_no" value="<%= alipayParam.out_trade_no%>"> <input type="hidden" name="partner" value="<%= alipayParam.partner%>"> <input type="hidden" name="payment_type" value="<%= alipayParam.payment_type%>"> <input type="hidden" name="return_url" value="<%= alipayParam.return_url%>"> <input type="hidden" name="seller_id" value="<%= alipayParam.seller_id%>"> <input type="hidden" name="service" value="<%= alipayParam.service%>"> <input type="hidden" name="show_url" value="<%= alipayParam.show_url%>"> <input type="hidden" name="subject" value="<%= alipayParam.subject%>"> <input type="hidden" name="total_fee" value="<%= alipayParam.total_fee%>"> <input type="hidden" name="sign_type" value="<%= alipayParam.sign_type%>"> <input type="hidden" name="sign" value="<%= alipayParam.sign%>"> <div class="wrapper buy-wrapper" style="display: none;"> <a href="javascript:void(0);" class="J-btn-submit btn mj-submit btn-strong btn-larger btn-block">確認支付</a> </div> </form> <input type="hidden" id="param" value="<%= param%>"> <% include ../bootstrap.html %> <script type="text/javascript" src="<%= dist %>/js/business-modules/artist/ap.js"></script> <script> var btn = document.querySelector(".J-btn-submit"); btn.addEventListener("click", function (e) { e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); var queryParam = ''; Array.prototype.slice.call(document.querySelectorAll("input[type=hidden]")).forEach(function (ele) { queryParam += ele.name + "=" + encodeURIComponent(ele.value) + '&'; }); var gotoUrl = document.querySelector("#ali-form").getAttribute('action') + '?' + queryParam; _AP.pay(gotoUrl); return false; }, false); btn.click(); </script> </body> </html>

該頁面會自動跳轉到同一文件夾下的pay.htm,該文件官方已經提供,把其中的引入ap.js的路徑修改一下即可。最終效果如下:

后記

支付寶的支付流程和微信的支付流程,有很多相似之處。沈晨指出一點不同:支付寶支付完成后有return_url和notify_url;微信支付完成后只有notify_url。

研讀了一下微信支付的開發文檔,確實如此。微信支付完成后的通知也分成兩路,一路通知到notify_url,另一路返回給調用支付接口的JS,同時發微信消息提示。也就是說,跳轉return_url的工作我們需要自己做。

最后,感謝沈晨帥哥提供的思路和幫助,感謝體超帥哥辛苦改后端。

書簽

支付寶開放平台
https://openhome.alipay.com/platform/home.htm

支付寶開放平台-手機網站支付-文檔中心
https://doc.open.alipay.com/doc2/detail?treeId=60&articleId=103564&docType=1

支付寶WAP支付接口開發
http://blog.csdn.net/tspangle/article/details/39932963

wap h5手機網站支付接口喚起支付寶錢包付款

商家服務 - 支付寶 知托付!
https://b.alipay.com/order/techService.htm

微信支付-開發文檔
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1

微信端支付寶支付,iframe改造
http://www.cnblogs.com/jiqing9006/p/5584268.html

微信如何突破支付寶的封鎖
http://blog.csdn.net/lee_sire/article/details/49530875

JavaScript專題(二):深入理解iframe
http://www.cnblogs.com/fangjins/archive/2012/08/19/2645631.html

關於微信公眾平台無法使用支付寶收付款的解決方案說明
https://cshall.alipay.com/enterprise/help_detail.htm?help_id=524702


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM