国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【轉(zhuǎn)】支付寶WAP支付接口開發(fā)

這篇具有很好參考價值的文章主要介紹了【轉(zhuǎn)】支付寶WAP支付接口開發(fā)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

支付寶WAP支付接口開發(fā)

因項目需要,要增加支付寶手機網(wǎng)站支付功能,找了支付寶的樣例代碼和接口說明,折騰兩天搞定,謹以此文作為這兩天摸索的總結(jié)。由于公司有自己的支付接口,并不直接使用這個接口,所以晚些時候打算把測試代碼整理好放到Github上。

1. 開發(fā)前準備

  1. 到官網(wǎng)了解此接口的信息,下載樣例代碼(只有ASP.NET和PHP)以便隨時參考。
  2. 一個通過實名認證的企業(yè)支付寶賬號,并申請開通手機WAP支付功能,我的測試賬號是拿公司的,申請流程不清楚,官網(wǎng)有說怎么申請,各位各顯神通吧。
  3. 公網(wǎng)域名和node.js環(huán)境。下面的代碼大多用coffee來表達,不過本文不會貼太多代碼,即使對coffee不熟悉也沒什么關系。關于coffee可以參考這里。

github上有兩個開源小項目(搜索?alipay?),但都沒有WAP支付功能,可以拿來當參考,可以認為是示例代碼的js移植版,結(jié)構(gòu)很相像。我原打算在其中一個項目基礎上繼續(xù)開發(fā),看了代碼和接口文檔后,還是決定從頭開發(fā)一個。因為原有代碼層次不夠清晰,有點過度設計的感覺,而且支付寶的接口很簡單,重寫工作量不大。

吐槽下: 官網(wǎng)的示例代碼真只是示例級(test)而已,跟產(chǎn)品級(production)還隔比較遠,感覺還談不上SDK。接口文檔相當?shù)目拥?,正因如此我才覺得有必要好好寫篇文章總結(jié)。

2. 流程

接口開發(fā)最重要的應該是理解數(shù)據(jù)交互流程了,流程弄清了,并理解為何這么設計,開發(fā)起來也是事半功倍

首先,要準備下面幾個參數(shù):

  1. 企業(yè)支付寶賬號的PID(也叫ParnerID)和KEY,如果使用RSA簽名而不是MD5的話,還要把RSA私鑰準備好
  2. 支付時用戶看到的東西:商品名稱(subject)、支付總額(total_fee)、購買數(shù)量(通常都是1吧)
  3. 交易后的跳轉(zhuǎn)地址,交易成功后用戶可以手工點擊,或頁面延遲自動跳轉(zhuǎn)到這個地址(return_url)
  4. 交易狀態(tài)異步通知地址,交易成功或交易關閉會把消息POST到這個地址(notify_url)

然后,看這幅流程圖(不錯吧,推薦下這個網(wǎng)站:)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Alipay WAP pay flow

Browzer->+Site: 1. HTTP GET                
note over Site: 2. create a new trade      
Site->+Alipay: 3. create redirect          
Alipay->-Site: 4. Token                    
note over Site: 5. build auth url          
Site->-Browzer: 6. redirect to auth url    
Browzer->Alipay: 7. redirect               
Alipay->Browzer: 8. trade info             
Browzer->Alipay: 9. auth and pay           
Alipay->+Site: 10. HTTP POST notify        
note over Site: 11. process trade          
Site->-Alipay: 12. reply "success"         
Alipay->Browzer: 13. pay success           
Browzer->Site: 14. goto return url

這個流程圖基本囊括了整個交互過程,下面是說明:

  1. 用戶點擊購買按鈕(或其他形式),向網(wǎng)站發(fā)起購買請求
  2. 網(wǎng)站創(chuàng)建訂單,指派一個唯一訂單號
  3. 網(wǎng)站把訂單號、企業(yè)支付寶賬號、交易金額、數(shù)量等信息,用私鑰簽名發(fā)送給支付寶
  4. 支付寶創(chuàng)建一個交易訂單,返回一個交易令牌(token)
  5. 網(wǎng)站按照指定要求,用token和自己的私鑰,構(gòu)造一個重定向得到支付地址
  6. 網(wǎng)站把重定向地址返回給瀏覽器
  7. 瀏覽器自動重定向到該地址,即包含了token、網(wǎng)站簽名的支付寶交易頁面
  8. 支付寶顯示當前交易金額、數(shù)量、賣家等信息
  9. 用戶用自己的支付寶賬號支付這筆金額
  10. 支付寶把用戶支付成功(或失敗)這個消息和訂單號加上支付寶的簽名,使用HTTP POST的方式通知網(wǎng)站(失敗的話,會隔段時間重新發(fā)送)
  11. 網(wǎng)站處理交易后續(xù)邏輯(發(fā)貨、訂單狀態(tài)存儲之類的)
  12. 網(wǎng)站返回"success"字符串給支付寶,表示該通知已經(jīng)處理,不用再重發(fā)
  13. 支付寶顯示支付成功頁面給用戶(這一步和第10步是不分先后發(fā)生的)
  14. 支付成功頁面延遲自動跳轉(zhuǎn),或用戶點擊“返回商戶頁面”,跳轉(zhuǎn)到網(wǎng)站的支付結(jié)束頁面(此時不一定成功處理支付寶發(fā)來的通知),但會在URL帶上當前的訂單號和狀態(tài)。

可以發(fā)現(xiàn),整個流程有點像OAuth(哎呀,之前那篇文章還沒寫完呢?。?,主要分三步:

一是申請支付寶交易號(獲取token),這一步可以理解為,讓支付寶驗證網(wǎng)站的有效性、讓網(wǎng)站指定該交易要支付多少錢 二是用戶到支付寶頁面付款,這一步可以理解為,讓支付寶驗證用戶有效性,讓用戶在一個不受網(wǎng)站監(jiān)視的環(huán)境下進行支付 三是用戶付款后,處理結(jié)果頁面告訴用戶支付成功(同步通知),另外異步通知網(wǎng)站服務器該訂單已支付。

支付寶的接口文檔說只有兩個步驟,感覺不是很好理解,三步還是比較準確的(收錢肯定要辦事的嘛)。

好困,細節(jié)問題下期繼續(xù)。。。


2013-07-24

3. 細節(jié)

3.1 網(wǎng)站向支付寶申請新訂單

網(wǎng)站的訂單系統(tǒng)先產(chǎn)生一個新訂單,然后請求支付寶創(chuàng)建一個支付寶訂單.

申請新訂單的service是?alipay.wap.trade.create.direct?需要提交的關鍵參數(shù)包括:

3.1.1 用戶在支付寶看到的訂單信息:

subject: 商品名稱

total_fee: 總金額

seller_account_name: 賣家支付寶賬號(估計跟私鑰綁定的)

merchant_url: 商品展示URL(似乎這個并非必要)

3.1.2 支付寶通知網(wǎng)站時將附帶的信息:

out_trade_no: 該次交易對應網(wǎng)站的訂單號(要求唯一)

call_back_url: 交易成功后,支付寶頁面上“返回到商家頁面”的地址(同步回調(diào))

notify_url: 交易狀態(tài)變更后,支付寶通知網(wǎng)站的回調(diào)地址(異步通知)

支付寶驗證通過后,將返回新創(chuàng)建的支付寶訂單號,網(wǎng)站可將該訂單號與自己訂單系統(tǒng)的的訂單號綁定在一起。支付寶同時返回的還有該次交易的token,用于(3.2)用戶支付。

3.2 用戶在支付寶網(wǎng)站,查看訂單消息,通過驗證并支付

網(wǎng)站返回跳轉(zhuǎn)到支付寶的地址,service是alipay.wap.auth.authAndExecute,包含(3.1)返回的token和網(wǎng)站對跳轉(zhuǎn)地址的簽名

這是個HTTPS頁面,基本認為是安全的。當然前提是瀏覽器沒被動手腳,安卓不少應用被捆綁廣告那是常有的事,手機瀏覽器對HTTPS也不像PC那樣有明顯提示,這些也是我不怎么信任手機支付的原因。

用戶跳轉(zhuǎn)到支付寶頁面后,可以在該頁面里看到當前支付的訂單的名稱和金額,這些是3.1申請時由網(wǎng)站指定的,讓用戶在支付寶的頁面確認一次再付款是合理的。

3.3 支付寶通知網(wǎng)站支付成功,網(wǎng)站收錢做事

這個過程是支付寶通知網(wǎng)站,網(wǎng)站處理后通知用戶已到賬,共包括兩個并行部分:

3.3.1 異步通知

用戶支付后,支付寶通過HTTP協(xié)議通知網(wǎng)站該訂單交易結(jié)果。說白了就是支付寶悄悄地告訴網(wǎng)站“這個訂單已經(jīng)已經(jīng)付款啦”

值得注意的是,異步通知有重發(fā)機制,支付寶需要得到響應為"success"才認為該通知成功被接收,否則會間隔一段時間重發(fā),依次間隔2m,10m,10m,1h,2h,6h,15h,最多8次通知,由notify_id說明是同一個通知。8次通知都接收失敗怎么辦?額orz...文檔沒說,用那個支付寶訂單號登錄支付寶去查賬吧。

3.3.2 同步通知

用戶支付后,支付寶頁面提示“支付成功”,可點擊返回商家頁面,也可等待一段時間自動跳回

個人認為,網(wǎng)站跳到這個頁面后,如果仍未收到(3.3.1)異步通知,并且使用的是MD5簽名,應該把狀態(tài)從“待付款”調(diào)整為“等待對賬”,而不應該貿(mào)然相信該通知的結(jié)果。原因是這個回調(diào)地址用戶是可以知道的,MD5簽名還是有被偽造的可能(4.3)。當然額外再做個token之類的理論上也行(需要放在urlpath而不是querystring)。

假如接口調(diào)用出錯,通知是不會簽名的。不簽名的原因我懷疑是防止有人惡意收集請求-簽名樣本,見(4.3)。

4. 簽名與加密

簡單的說,簽名防篡改,加密防竊聽。上面的兩種請求(3.1和3.2)和兩個通知(3.3.1和3.3.2)都被要加簽名,支付寶支持下面兩類簽名:

4.1 MD5: 業(yè)務數(shù)據(jù)不加密,防篡改

  • 優(yōu)點: 相對較簡單(當然是相對DSA/RSA來說),計算速度快,明文更直觀

  • 缺點: 可抵賴,可能被竊聽、安全性不如非對稱加密

4.2 DSA/RSA: 業(yè)務數(shù)據(jù)加密,也防篡改

  • 優(yōu)點:不可抵賴,安全性較高

  • 缺點:相對較復雜,解密速度慢

一開始我想不懂,支付寶既然支持RSA為何還要支持MD5,后來有人說RSA太慢,想想支付寶的業(yè)務量就釋然了。由于每個商家的私鑰都不同,并且跟商家的支付寶賬號綁定,即使商家的私鑰被破解了,用戶支付時HTTPS協(xié)議基本可以保證用戶支付的目標還是商家的賬號。

4.3 使用MD5簽名可能存在的風險

以下情景僅是我的推斷,沒有嘗試過,所謂道高一尺魔高一丈,希望讀者也別以身犯險。

在用戶支付的步驟(3.2)和支付成功響應頁面(3.3),用戶可以得到一個明文請求內(nèi)容和對應簽名。由于網(wǎng)站和支付寶直接通信共用同一個密鑰,一般長期不變,雙方都可以對同一段數(shù)據(jù)產(chǎn)生簽名,這就有可能抵賴的風險:

網(wǎng)站:“這個數(shù)據(jù)是你發(fā)過來的,上面有你的簽名?!?/p>

支付寶:“不是我發(fā)的。這個數(shù)據(jù)是你偽造的,簽名是你簽的?!?/p>

另外,當攻擊者收集到足夠多的樣本,是有可能破解出密鑰的,繼而可偽造網(wǎng)站或支付寶任意一方。

4.3.1 惡意消耗商家的訂單號

攻擊者偽造大量未使用的訂單號(不少網(wǎng)站的訂單號都是遞增的純數(shù)字,并公開給用戶,且很容易推測到后面的數(shù)字),向支付寶請求訂單,直到超時。由于商家對此并不知情(回調(diào)地址和通知地址均篡改掉),其他用戶下單時假如商家用了被偽造過的訂單號,就可能被支付寶認為提交了重復訂單,結(jié)果支付失敗。

4.3.2 欺騙商家已支付訂單

由同步通知(3.3.2)返回的參數(shù)可以看到,網(wǎng)站訂單標識和交易token和都是可以得到的。這樣的話,關于步驟(3.1),用戶不知道的參數(shù)包括notify_url和out_user_no,假如網(wǎng)站的用戶id本身就是公開的,通知回調(diào)地址(3.3.1)被得知或同步通知(3.3.2)實現(xiàn)的不好,就可以通過偽造支付通知,欺騙商家訂單已支付。

待續(xù),下期補充實現(xiàn)代碼


2013-07-28

5. 代碼

5.1 簽名

我只做了MD5簽名,項目里沒用到RSA簽名,就沒做那方面。按照文檔說明和demo源代碼,很容易就可以寫出下面的簽名代碼:

1
2
3
4
5
6
7
getSign = (obj,key) ->
	return null unless obj
	arr = ([k,v] for k,v of obj when k isnt 'sign' and v? and v isnt '')
	arr.sort()
	src = ("#{i[0]}=#{i[1]}" for i in arr).join '&'
	src = "#{src}#{key}"
	crypto.createHash('md5').update(src,'utf8').digest 'hex'

支付寶發(fā)到網(wǎng)站的通知(3.3)的簽名算法跟上面有點不一樣,文檔有這么段說明:

這里說要按通知的參數(shù)的原本順序計算簽名。所以我就把上面的arr.sort()去掉然后計算簽名,結(jié)果發(fā)通知發(fā)來的簽名和我自己計算的不一致,糾結(jié)半天后仔細看文檔的樣例說明,看到下面段:

仔細跟實際接收的數(shù)據(jù)比較之后發(fā)現(xiàn),文檔和樣例都說發(fā)來的參數(shù)順序是(service,v,sec_id,notify_data),但我實際收到的并不是按這個順序,只要按照文檔的參數(shù)順序重新排列再計算簽名就正確了,最終通知的簽名算法如下( 真是個蛋疼的大坑orz):

1
2
3
4
5
getNotitySign = (obj,key) ->
	return null unless obj
	src = ("#{k}=#{obj[k]}" for k in ["service","v","sec_id","notify_data"]).join '&'
	src = "#{src}#{key}"
	crypto.createHash('md5').update(src,'utf8').digest 'hex'
  • 文檔里有說到字符編碼參數(shù)_input_charset,我用的是utf8編碼,發(fā)現(xiàn)不用傳這個參數(shù)也可以,看來支付寶默認的字符編碼就是utf8了

  • 如果使用RSA簽名,需要先解密再計算簽名

5.2 輔助方法

為了代碼層次更清晰,我把簽名、url拼接等方法抽出到一個單獨模塊(alipay_api.coffee):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
api_url = "http://wappaygw.alipay.com/service/rest.htm"
regexTokenXml = /<request_token>(.*)<\/request_token>/

module.exports = api = 

	services:
		create: "alipay.wap.trade.create.direct"
		auth: "alipay.wap.auth.authAndExecute"

	toReqData: (name,obj) ->
		arr = ["<#{name}>"]
		arr.push "<#{k}>#{v}</#{k}>" for k,v of obj
		arr.push "</#{name}>"
		arr.join ''

	createReq: (service,partner,req_data) -> 
		service : service
		format  : 'xml'
		v       : '2.0'
		partner : partner
		sec_id  : 'MD5'
		sign    : null
		req_data: req_data

	parseTokenFromXml: (xml) ->
		return null unless xml
		m = regexTokenXml.exec xml
		m?[1]?.trim()

	getSign: (obj,key='') ->
		return null unless obj
		arr = ([k,v] for k,v of obj when k isnt 'sign' and v? and v isnt '')
		arr.sort()
		src = ("#{i[0]}=#{i[1]}" for i in arr).join '&'
		src = "#{src}#{key}"
		crypto.createHash('md5').update(src,'utf8').digest 'hex'

	getNotitySign: (obj,key='') ->
		return null unless obj
		src = ("#{k}=#{obj[k]}" for k in ["service","v","sec_id","notify_data"]).join '&'
		src = "#{src}#{key}"
		crypto.createHash('md5').update(src,'utf8').digest 'hex'

	sendCreate: (req,done) ->
		opt =
			url: createCreateUrl req
		request.get opt, (err,res,body) ->
			return done err if err
			body = "" unless body
			ret = querystring.parse body
			body = null
			done null,ret

	createAuthUrl: (token='',key='') ->
		req = api.createReq api.services.auth
		req.req_data = "<auth_and_execute_req><request_token>#{token}</request_token></auth_and_execute_req>"
		req.sign = api.getSign req, yes
		createAuthUrl req

createCreateUrl = (req) ->
	url = "#{api_url}?"
	url += "req_data=#{encodeURIComponent req.req_data}"
	url += "&service=#{encodeURIComponent req.service}"
	url += "&sec_id=#{encodeURIComponent req.sec_id}"
	url += "&partner=#{encodeURIComponent req.partner}"
	url += "&req_id=#{encodeURIComponent req.req_id}"
	url += "&sign=#{encodeURIComponent req.sign}"
	url += "&format=#{encodeURIComponent req.format}"
	url += "&v=#{encodeURIComponent req.v}"
	url

createAuthUrl = (req) ->
	url = "#{api_url}?"
	url += "req_data=#{encodeURIComponent req.req_data}"
	url += "&service=#{encodeURIComponent req.service}"
	url += "&sec_id=#{encodeURIComponent req.sec_id}"
	url += "&partner=#{encodeURIComponent req.partner}"
	url += "&sign=#{encodeURIComponent req.sign}"
	url += "&format=#{encodeURIComponent req.format}"
	url += "&v=#{encodeURIComponent req.v}"
	url

5.3 業(yè)務部分

5.3.1 購買(buy)

購買的邏輯對應于(2)流程圖的(2,3,4,5),創(chuàng)建唯一請求ID,填充本次交易信息,發(fā)送到支付寶并獲取token,然后拼接支付url并簽名,然后重定向。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
demo.buy = (info,done) ->
	return done 'bad user' unless info?.user_id?.length>10
	req = api.createReq api.services.create, info.partner
	ret = 
		redirect: ''
		token: null
	async.series [
		(cb) ->
			getRequestId req.service,(err,req_id) ->
				req.req_id = req_id
				cb err
		(cb) ->
			createTrade info,req.req_id,(err, tradeId) ->
				return cb err if err 
				req.req_data = 
					subject            : info.subject  # 商品名稱
					out_trade_no       : tradeId.toString() # 網(wǎng)站訂單號
					total_fee          : info.total_fee  # 價錢(number),單位元,例如 0.01 代表1分錢
					seller_account_name: info.seller_account_name # 支付寶賬號
					call_back_url      : info.call_back_url # 支付成功后瀏覽器跳轉(zhuǎn)地址
					notify_url         : info.notify_url # 支付成功支付寶的通知將異步發(fā)送到此地址
					out_user           : info.user_id # 網(wǎng)站的用戶標識
					merchant_url       : info.merchant_url # 商品展示頁面, 只是實際測試時(ios)發(fā)現(xiàn)支付時沒地方可以跳到這個頁面
				req.pay_expire = info.pay_expire if info.pay_expire? # 支付過期時間
				req.req_data = api.toReqData 'direct_trade_create_req',req.req_data
				req.sign = api.getSign req, info.key
				cb null
		(cb) ->
			api.sendCreate req, (err,res) ->
				return cb err if err 
				return cb 'bad sign from alipay server' unless req.sign is api.getSign req
				ret.token = api.parseTokenFromXml res.res_data
				ret.redirect = api.createAuthUrl ret.token
				storeTradeInfo req.out_trade_no, req.total_fee, ret.token, (err,success) ->
					return cb err if err
					cb if success then null else 'store trade info fail'
	],(err) ->
		done err, ret.redirect

其中用到的幾個方法跟存儲相關,我用的是MySQL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
getRequestId = (service, done) ->
	sql = "insert into alipay_requests(service,create_time,state) values(?,now(),'CREATE')" 
	db.queryAll sql,[service],(err,result) ->
		done err, result?.insertId

createTrade = (info,req_id,callback) ->
	sql = "insert into alipay_trades(user,req_id,create_time) values(?,?,now())" 
	db.queryAll sql,[info.user_id,req_id],(err,result) ->
		callback err, result?.insertId
		
storeTradeInfo = (tradeId,rmb,token,callback) ->
	sql = "update alipay_trades set rmb=?,token=?,state='WAIT_PAY' where id = ?"
	args = [rmb,token,tradeId]
	db.queryAll sql,args,(err,updateResult) ->
		callback err,updateResult?.affectedRows is 1

5.3.2 通知(notify)

用戶支付后就等著通知了,按道理應該在TRADE_SUCCESS時處理用戶支付成功的邏輯,但我實際測試發(fā)現(xiàn)至發(fā)送了TRADE_FINISHED事件來,所以干脆兩個一并處理了,反正只會有一次成功。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
xmlreader = require 'xmlreader'rr,updateResult?.affectedRows is 1
demo.onNotify = (req,callback) ->
	xmlreader.read req.notify_data,(err,xdoc) ->
		return done err if err
		notify = xdoc.notify
		notify_id = notify?.notify_id?.text()
		return done 'bad notify_data' unless notify_id
		done = (response) ->
			unless 'string' is typeof response
				console.error "response notify error: " + (response?.stack ? response ? '')
				response = 'server error'
			console.error "response notify: #{response}" unless response is 'success'
			storeNotifyResponse notify_id,response, (err) ->
				callback if response is 'success' then err else response
		storeNotifyDetails notify_id, notify, req, (err,success) ->
			return done err if err
			unless success
				return done "store detail error"
			trade_status = notify.trade_status.text()
			if trade_status is 'TRADE_FINISHED' or trade_status is 'TRADE_SUCCESS'
				unless req.sign is api.getNotitySign req
					return done 'bad sign'
				out_trade_no = notify.out_trade_no.text()
				getTradeUser out_trade_no,(err,user) ->
					return done err if err
					user_id = user?.user
					return done 'unknown user' unless user_id
					onPayed user_id,(err,success) ->
						return done err if err
						return done "onPayed error" unless success
						storeTradeFinalState out_trade_no, no, console.error
						done 'success'
			else # TRADE_PENDING, TRADE_CLOSED, WAIT_BUYER_PAY, etc 
				return done 'unknown trade status'

跟存儲相關的幾個方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
storeTradeFinalState = (tradeId,isError,callback) ->
	state = if isError then 'FAILURE' else 'SUCCESS'
	sql = "update alipay_trades set state=?,close_time=now() where id = ?"
	args = [state,tradeId]
	db.queryAll sql,args,(err,updateResult) ->
		callback err,updateResult?.affectedRows is 1

getTradeUser = (out_trade_no,callback) ->
	sql = "select id,user from alipay_trades where id=?"
	db.queryOne sql,[out_trade_no],callback

onPayed = (user_id,callback) ->
	sql = "update users set vip=1 where id=? and vip=0 limit 1"
	db.queryAll sql,[user_id],(err,updateResult) ->
		callback err, updateResult?.affectedRows is 1

storeNotifyDetails = (notify_id,notify,raw,callback) ->
	sql = "insert ignore into alipay_notifies(id,recv_time,subject,trade_no,gmt_create,
 		quantity,out_trade_no,notify_time,total_fee,buyer_email,trade_status, 
  		gmt_payment,gmt_close,raw) values(?,now(),?,?,?,?,?,?,?,?,?,?,?,? )"
	args = [
		notify_id,
		notify.subject?.text()
		notify.trade_no?.text()
		notify.gmt_create?.text()
		notify.quantity?.text()
		notify.out_trade_no?.text()
		notify.notify_time?.text()
		notify.total_fee?.text()
		notify.buyer_email?.text()
		notify.trade_status?.text()
		notify.gmt_payment?.text()
		notify.gmt_close?.text()
		JSON.stringify raw
	]
	db.queryAll sql,args,(err,updateResult) ->
		callback err,updateResult?.affectedRows is 1 

storeNotifyResponse = (notify_id,response,done) ->
	sql = "update alipay_notifies set response=? where id=?"
	db.queryAll sql,[response,notify_id],done

6. 后記

有些公司有自己的支付平臺,封裝了一層,結(jié)果調(diào)用流程變成下面這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Alipay WAP using platform pay flow

Browzer->+Site: 1. HTTP GET
note over Site: 2. create a new trade
Site->-Browzer: 3. trade info
Browzer->+Platform: 4. redirect
note over Platform: 5. create a new trade
Platform->+Alipay: 6. create redirect
Alipay->-Platform: 7. Token
note over Platform: 8. build auth url
Platform->-Browzer: 9. redirect to auth url
Browzer->Alipay: 10. redirect
Alipay->Browzer: 11. trade info
Browzer->Alipay: 12. auth and pay
Alipay->+Platform: 13. HTTP POST notify
Platform->Site: 14. proxy
note over Site: 15. process trade
Site->Platform: 16. reply "success"
Platform->-Alipay: 17. reply "success"
Alipay->Browzer: 18. pay success
Browzer->Site: 19. goto return url

咋一看,挺方便的,Site只需要做個跳轉(zhuǎn)即可,細節(jié)都被Platform隱藏起來了,切換不同的支付方式也變得很方便。

可是,這個平臺的接口并不完善,這些數(shù)據(jù)都是明文并且沒有要求Site做簽名,于是就有一個風險。跟(2)的流程圖對比,可以發(fā)現(xiàn)這個流程多了(3,4,14,16)四個步驟。后面兩步只是個代理包裝,沒什么問題,問題在于步驟3和4。

可以看到,交易細節(jié)被放到了重定向url中了,即用戶可以得知交易內(nèi)容并篡改里面的數(shù)據(jù)。舉個例子,假設重定向地址類似這樣

http://payment.mysite.com/api/pay?tradeno=10000&rmb=100.00&siteid=1

由于該地址沒有驗簽機制,所以攻擊者很容易就可以發(fā)現(xiàn)這里面可以隨意篡改數(shù)據(jù)。

  • 方法1: 將rmb=100.00改成0.01,即將100元的支付變成1分錢,然后支付。結(jié)果Site得到通知的時候,就需要額外處理這種支付款項和要求款項不一致的情況。當然最簡單就是金額不足就不退款并且支付失敗了。假如沒做這種判斷,那就相當把100元的商品以1分錢賣出去了。

  • 方法2: 偽造大量tradeno,然后請求,并且不支付款項。由于tradeno要求唯一,并只能使用一次,這樣就相當于消耗掉了site的交易ID,并且site對此毫不知情。結(jié)果正常用戶要購買時,創(chuàng)建的訂單號對于site來說是未使用的,但對platform來說已經(jīng)是使用過了,會返回支付失敗。

要避免這個問題,需要在重定向的地址增加簽名,簽名異常的請求都拋棄掉。文章來源地址http://www.zghlxwxcb.cn/news/detail-711199.html

到了這里,關于【轉(zhuǎn)】支付寶WAP支付接口開發(fā)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • Servlet+JDBC實戰(zhàn)開發(fā)書店項目講解第七篇:模擬支付

    在本篇文章中,我們將使用Servlet和JDBC來完成書店項目中的模擬支付功能。我們將介紹實現(xiàn)思路、后端代碼設計以及前端代碼設計,并通過代碼實現(xiàn)訂單支付成功,并實現(xiàn)查看未付款訂單和完成訂單支付等操作。 為了模擬支付功能,我們需要在書店項目中添加支付的相關邏輯

    2024年02月12日
    瀏覽(20)
  • FineBI實戰(zhàn)項目一(20):不同支付方式訂單筆數(shù)分析開發(fā)

    FineBI實戰(zhàn)項目一(20):不同支付方式訂單筆數(shù)分析開發(fā)

    點擊新建組件,創(chuàng)建不同支付方式訂單筆數(shù)組件。 選擇線圖,拖拽total_cnt到縱軸,拖拽pay_type到橫軸,修改連線樣式為雷達圖。 修改橫軸和縱軸的顯示文字 添加組件到儀表板。 效果如下:

    2024年01月15日
    瀏覽(24)
  • uniapp前端+python后端=微信小程序支付到底怎么開發(fā)???國內(nèi)的資料為什么沒一篇能講清楚,簡簡單單的只需要3步就可以了-V2版本

    uniapp前端+python后端=微信小程序支付到底怎么開發(fā)???國內(nèi)的資料為什么沒一篇能講清楚,簡簡單單的只需要3步就可以了-V2版本

    真的,在接到這個任務的時候,本以為很簡單,不就是普通的瀏覽器復制粘貼,最不濟找下gpt給生成一下,但是到實際開發(fā)就不同了,不是后端出問題就是前端,搜資料,上百度上google,基本每一個人講的都不一樣,不是這問題就是那問題,特別是微信官方,自己接口的邏輯

    2024年01月22日
    瀏覽(30)
  • 網(wǎng)址打包微信小程序源碼 wap轉(zhuǎn)微信小程序 網(wǎng)站轉(zhuǎn)小程序源碼 網(wǎng)址轉(zhuǎn)小程序開發(fā)

    網(wǎng)址打包微信小程序源碼 wap轉(zhuǎn)微信小程序 網(wǎng)站轉(zhuǎn)小程序源碼 網(wǎng)址轉(zhuǎn)小程序開發(fā)

    我們都知道微信小程序是無法直接打開網(wǎng)址的。 這個小程序源碼提供了一種將網(wǎng)址直接打包成微信小程序的方法, 使得用戶可以在微信小程序中直接訪問這些網(wǎng)址內(nèi)容。 這個源碼沒有進行加密,可以直接查看和修改。 將下面代碼中的網(wǎng)站改成你的就行了,簡單易用 藍奏云

    2024年04月10日
    瀏覽(24)
  • 微信支付, 小程序,公眾號, 商戶號 需要進行的配置

    微信支付, 小程序,公眾號, 商戶號 需要進行的配置

    要想綁定商戶號對接支付功能的必須是服務號,不能是訂閱號. 服務號的申請創(chuàng)建這里不多說.按照微信提示步驟走即可 1.1.1點擊左側(cè)菜單 基本配置 1.1.2點擊啟用開發(fā)者密碼(AppSecret) 根據(jù)提示驗證 1.1.3保存記錄AppID、AppSecret。 白名單內(nèi)只填寫ip地址即可 點擊左側(cè)菜單 開發(fā)者工

    2024年02月16日
    瀏覽(19)
  • 支付-支付寶接口全流程

    支付-支付寶接口全流程

    支付寶支付產(chǎn)品如下:文檔: https://b.alipay.com/signing/productSetV2.htm 如何開通支付寶手機網(wǎng)站支付接口? 進入網(wǎng)址: https://b.alipay.com/signing/productDetailV2.htm?productId=I1011000290000001001 點擊:立即開通 上傳營業(yè)執(zhí)照等資料,提交審核,根據(jù)提示進行開通。 第三方支付接口流程大同小

    2024年02月06日
    瀏覽(19)
  • 西米支付:如何選擇自己需求的接口(傳奇游戲支付接口)

    西米支付:如何選擇自己需求的接口(傳奇游戲支付接口)

    傳奇游戲是中國網(wǎng)游無法繞過的一座碑,也是千萬初代網(wǎng)游玩家的游戲啟蒙,2001年一款游戲橫空出世 靠著超爽的打擊感,和多人同屏戰(zhàn)斗迅速在網(wǎng)游火了起來,它就是傳奇。隨著《傳奇》盛大的成長、興盛與衰弱,一路走來,已經(jīng)在14年。游戲的充值模式也由以前的點卡充值

    2024年02月15日
    瀏覽(28)
  • AI問答:前端需要掌握的設計模式/vue項目使用了哪些設計模式/vue項目開發(fā)可以使用哪些設計模式

    AI問答:前端需要掌握的設計模式/vue項目使用了哪些設計模式/vue項目開發(fā)可以使用哪些設計模式

    一、理解什么是設計模式 設計模式是對軟件設計開發(fā)過程中反復出現(xiàn)的某類問題的通用解決方案。 設計模式是一個在軟件設計領域中被廣泛應用的概念,它指的是一套被公認為有效的解決特定問題的設計思路和方法。 設計模式更多的是指導思想和方法論,而不是現(xiàn)成的代碼

    2024年02月09日
    瀏覽(25)
  • 關于支付通道,支付接口,支付對接的100個名詞解釋

    一份簡明易懂的支付術語解釋清單,幫助你更好地理解支付通道、支付接口和支付對接等相關概念。 100個名詞的簡要解釋: 在線支付:通過互聯(lián)網(wǎng)實現(xiàn)的支付方式,包括網(wǎng)銀支付、第三方支付等。 支付網(wǎng)關:連接商戶和支付機構(gòu)的中間件,實現(xiàn)支付流程的安全處理和支付數(shù)

    2024年02月04日
    瀏覽(57)
  • 蒼穹外賣項目開發(fā)指南:項目概述、環(huán)境搭建、Swagger接口文檔生成

    蒼穹外賣項目開發(fā)指南:項目概述、環(huán)境搭建、Swagger接口文檔生成

    詳細介紹蒼穹外賣項目的開發(fā)流程,包括軟件開發(fā)整體介紹、項目概述、環(huán)境搭建、Swagger接口文檔生成、Nginx反向代理和負載均衡配置等內(nèi)容,幫助開發(fā)人員快速上手項目開發(fā)。

    2024年02月10日
    瀏覽(79)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包