python采用django框架實現(xiàn)支付寶即時到帳接口
因工作需要研究了支付寶即時到帳接口,并成功應用到網(wǎng)站上,把過程拿出來分享。
即時到帳只是支付寶眾多商家服務中的一個,表示客戶付款,客戶用支付寶付款,支付寶收到款項后,馬上通知你,并且此筆款項與交易脫離關系,商家可以馬上使用。
即時到帳只對企業(yè)客戶服務,注冊成功企業(yè)賬號以后,申請簽約即時到帳產(chǎn)品,大約3-5個工作日后,簽約成功,可以馬上進入集成產(chǎn)品階段。
這個是支付寶提供的接口,有asp,c#,java,php四種語言的,每種語言提供GBK和UTF-8兩種方案。另帶一份支付寶的文檔,這份文檔我感覺本來簡單的事情越說越麻煩了。
網(wǎng)上搜了一下,發(fā)現(xiàn)Python接口有幾個現(xiàn)成的方案。
https://github.com/fengli/alipay_python
這兩個是一個,代碼我還沒看,寫文檔的時候發(fā)現(xiàn)的。
https://github.com/lxneng/alipay
https://pypi.python.org/pypi/alipay/0.2.2
支付寶即時到帳交易過程。
商家:是指支付寶的企業(yè)客戶。也就是你集成服務單位。
終端消費者:是指在網(wǎng)上購物的消費者,你集成服務單位的客戶。
1、終端消費者在商家網(wǎng)站選擇商品,下訂單。
2、商家把支付信息,get到支付寶指定的鏈接。
3、終端消費者在支付寶的網(wǎng)站上操作付款。
4、付款成功后,支付寶post付款成功的信息到商家預先提供的地址。
5、支付寶在終端消費者付款成功后三秒后,通過get跳回商家指定的鏈接。
第4步跟終端消費者操作付款的跳轉(zhuǎn)無關,所以被稱為異步通知。這一步,支付寶期待你返回'success',如果你不返回'success',支付寶會于4分鐘后再次post付款成功的信息,此后每10分鐘post一次,至少30分鐘內(nèi)如此。如果終端消費者付款失敗,異步通知不會發(fā)生。
通過集成,我知道為什么終端消費者付款成功后要等3秒后跳轉(zhuǎn)回商家頁面了,因為它要等異步通知的信息先到達,先處理訂單,再帶終端消費者回到客戶的界面,這樣就可以看到支付成功的頁面了。當然付款失敗,異步通知不發(fā)生,訂單狀態(tài)沒有改變,終端消費者就只能看到付款失敗的信息。
了解了支付過程,開始設計程序。
1、生成商品訂單。終端消費者選擇商品生成商品訂單。ID號要唯一,這個唯一不是要你采用UUID,而是跟支付寶往來過程唯一即可,從1遞增也可以,只是終端消費者能看到這個id,所以最好采用固定長度的字符串,終端消費者如果知道自己是第一個客戶,會不會心里發(fā)怵?后面會談到,為了安全不僅不能用遞增,而且至少要6位隨機碼以上。
2、選擇支付方式。因為我們僅僅集成了即時到帳,所以只有支付寶付款一個選項。把支付方式加入訂單信息,同時把訂單id post到下一步地址,這一步post,最好采用新開頁面,本頁面彈出對話框,讓客戶自己選擇支付成功或者支付失敗。
3、發(fā)送支付信息。根據(jù)post過來的訂單號組合信息,get方式發(fā)送數(shù)據(jù)給支付寶,同時帶動終端消費者頁面跳轉(zhuǎn)到支付寶支付頁面。
4、接受異步通知。
5、支付成功后支付寶跳轉(zhuǎn)回來的程序。因為回來時get方式,為避免終端消費者看到更多敏感信息,這一步并沒有渲染頁面,而是處理信息,跳轉(zhuǎn)到另外一個頁面。
6、顯示支付結(jié)果。這個頁面和第2步讓客戶點擊支付成功跳轉(zhuǎn)的是同一個。支付失敗,就跳轉(zhuǎn)到一個說明吧。
alipay.py
import types
from urllib import urlencode, urlopen
from hashcompat import md5_constructor as md5<span style="white-space:pre"> </span>#見hashcompact.py
from config import settings<span style="white-space:pre"> </span>#見config.py
#字符串編解碼處理
def smart_str(s, encoding='utf-8', strings_only=False, errors='strict'):
if strings_only and isinstance(s, (types.NoneType, int)):
return s
if not isinstance(s, basestring):
try:
return str(s)
except UnicodeEncodeError:
if isinstance(s, Exception):
return ' '.join([smart_str(arg, encoding, strings_only,
errors) for arg in s])
return unicode(s).encode(encoding, errors)
elif isinstance(s, unicode):
return s.encode(encoding, errors)
elif s and encoding != 'utf-8':
return s.decode('utf-8', errors).encode(encoding, errors)
else:
return s
# 網(wǎng)關地址
_GATEWAY = 'https://mapi.alipay.com/gateway.do?'
# 對數(shù)組排序并除去數(shù)組中的空值和簽名參數(shù)
# 返回數(shù)組和鏈接串
def params_filter(params):
ks = params.keys()
ks.sort()
newparams = {}
prestr = ''
for k in ks:
v = params[k]
k = smart_str(k, settings.ALIPAY_INPUT_CHARSET)
if k not in ('sign','sign_type') and v != '':
newparams[k] = smart_str(v, settings.ALIPAY_INPUT_CHARSET)
prestr += '%s=%s&' % (k, newparams[k])
prestr = prestr[:-1]
return newparams, prestr
# 生成簽名結(jié)果
def build_mysign(prestr, key, sign_type = 'MD5'):
if sign_type == 'MD5':
return md5(prestr + key).hexdigest()
return ''
# 即時到賬交易接口
def create_direct_pay_by_user(tn, subject, body, bank, total_fee):
params = {}
params['service'] = 'create_direct_pay_by_user'
params['payment_type'] = '1' #商品購買,只能選這個
# 獲取配置文件
params['partner'] = settings.ALIPAY_PARTNER
params['seller_id'] = settings.ALIPAY_PARTNER
params['seller_email'] = settings.ALIPAY_SELLER_EMAIL
params['return_url'] = settings.ALIPAY_RETURN_URL
params['notify_url'] = settings.ALIPAY_NOTIFY_URL
params['_input_charset'] = settings.ALIPAY_INPUT_CHARSET
params['show_url'] = settings.ALIPAY_SHOW_URL
# 從訂單數(shù)據(jù)中動態(tài)獲取到的必填參數(shù)
params['out_trade_no'] = tn # 請與貴網(wǎng)站訂單系統(tǒng)中的唯一訂單號匹配
params['subject'] = subject # 訂單名稱,顯示在支付寶收銀臺里的“商品名稱”里,顯示在支付寶的交易管理的“商品名稱”的列表里。
params['body'] = body # 訂單描述、訂單詳細、訂單備注,顯示在支付寶收銀臺里的“商品描述”里,可以為空
params['total_fee'] = total_fee # 訂單總金額,顯示在支付寶收銀臺里的“應付總額”里,精確到小數(shù)點后兩位
# 擴展功能參數(shù)——網(wǎng)銀提前
if bank=='alipay' or bank=='':
params['paymethod'] = 'directPay' # 支付方式,四個值可選:bankPay(網(wǎng)銀); cartoon(卡通); directPay(余額); CASH(網(wǎng)點支付)
params['defaultbank'] = '' # 支付寶支付,這個為空
else:
params['paymethod'] = 'bankPay' # 默認支付方式,四個值可選:bankPay(網(wǎng)銀); cartoon(卡通); directPay(余額); CASH(網(wǎng)點支付)
params['defaultbank'] = bank # 默認網(wǎng)銀代號,代號列表見http://club.alipay.com/read.php?tid=8681379
params,prestr = params_filter(params)
params['sign'] = build_mysign(prestr, settings.ALIPAY_KEY, settings.ALIPAY_SIGN_TYPE)
params['sign_type'] = settings.ALIPAY_SIGN_TYPE
return _GATEWAY + urlencode(params)
def notify_verify(post):
# 初級驗證--簽名
_,prestr = params_filter(post)
mysign = build_mysign(prestr, settings.ALIPAY_KEY, settings.ALIPAY_SIGN_TYPE)
if mysign != post.get('sign'):
return False
# 二級驗證--查詢支付寶服務器此條信息是否有效
params = {}
params['partner'] = settings.ALIPAY_PARTNER
params['notify_id'] = post.get('notify_id')
gateway = 'https://mapi.alipay.com/gateway.do?service=notify_verify&'
verify_result = urlopen(gateway, urlencode(params)).read()
if verify_result.lower().strip() == 'true':
return True
return False
hashcompact.py
""" The md5 and sha modules are deprecated since Python 2.5, replaced by the hashlib module containing both hash algorithms. Here, we provide a common interface to the md5 and sha constructors, preferring the hashlib module when available. """ try: import hashlib md5_constructor = hashlib.md5 md5_hmac = md5_constructor sha_constructor = hashlib.sha1 sha_hmac = sha_constructor except ImportError: import md5 md5_constructor = md5.new md5_hmac = md5 import sha sha_constructor = sha.new sha_hmac = sha
config.py
#-*- coding:utf-8 -*- class settings: # 安全檢驗碼,以數(shù)字和字母組成的32位字符 ALIPAY_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' ALIPAY_INPUT_CHARSET = 'utf-8' # 合作身份者ID,以2088開頭的16位純數(shù)字 ALIPAY_PARTNER = 'xxxxxxxxxxxxxxxx' # 簽約支付寶賬號或賣家支付寶帳戶 ALIPAY_SELLER_EMAIL = 'ls@abc.com' ALIPAY_SIGN_TYPE = 'MD5' # 付完款后跳轉(zhuǎn)的頁面(同步通知) 要用 http://格式的完整路徑,不允許加?id=123這類自定義參數(shù) ALIPAY_RETURN_URL='http://www.xxx.com/alipay/return/' # 交易過程中服務器異步通知的頁面 要用 http://格式的完整路徑,不允許加?id=123這類自定義參數(shù) ALIPAY_NOTIFY_URL='http://www.xxx.com/alipay/notify/'
view.py
#確認支付
def pay(request):
cbid=request.POST.get('id')
try:
cb=cBill.objects.get(id=cbid)
except ObjectDoesNotExist:
return HttpResponseRedirect("/err/no_object")
#如果網(wǎng)關是支付寶
if cb.cbank.gateway=='alipay':
tn=cb.id
subject=''
body=''
bank=cb.cbank.id
tf='%.2f' % cb.amount
url=create_direct_pay_by_user (tn,subject,body,bank,tf)
#如果網(wǎng)關是財付通
elif cb.cbank.gateway=='tenpay':
pass
#去支付頁面
return HttpResponseRedirect (url)
#alipay異步通知
@csrf_exempt
def alipay_notify_url (request):
if request.method == 'POST':
if notify_verify (request.POST):
#商戶網(wǎng)站訂單號
tn = request.POST.get('out_trade_no')
#支付寶單號
trade_no=request.POST.get('trade_no')
#返回支付狀態(tài)
trade_status = request.POST.get('trade_status')
cb = cBill.objects.get(pk=tn)
if trade_status == 'TRADE_SUCCESS':
cb.exe()
log=Log(operation='notify1_'+trade_status+'_'+trade_no)
log.save()
return HttpResponse("success")
else:
#寫入日志
log=Log(operation='notify2_'+trade_status+'_'+trade_no)
log.save()
return HttpResponse ("success")
else:
#黑客攻擊
log=Log(operation='hack_notify_'+trade_status+'_'+trade_no+'_'+'out_trade_no')
log.save()
return HttpResponse ("fail")
#同步通知
def alipay_return_url (request):
if notify_verify (request.GET):
tn = request.GET.get('out_trade_no')
trade_no = request.GET.get('trade_no')
trade_status = request.GET.get('trade_status')
cb = cBill.objects.get(pk=tn)
log=Log(operation='return_'+trade_status+'_'+trade_no)
log.save()
return HttpResponseRedirect ("/public/verify/"+tn)
else:
#錯誤或者黑客攻擊
log=Log(operation='err_return_'+trade_status+'_'+trade_no)
log.save()
return HttpResponseRedirect ("/")
#外部跳轉(zhuǎn)回來的鏈接session可能丟失,無法再進入系統(tǒng)。
#客戶可能通過xxx.com操作,但是支付寶只能返回www.xxx.com,域名不同,session丟失。
def verify(request,cbid):
try:
cb=cBill.objects.get(id=cbid)
#如果訂單時間距現(xiàn)在超過1天,跳轉(zhuǎn)到錯誤頁面!
#避免網(wǎng)站信息流失
return render_to_response('public_verify.html',{'cb':cb},RequestContext(request))
except ObjectDoesNotExist:
return HttpResponseRedirect("/err/no_object")
view.py中的代碼僅供參考!
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助。
相關文章
Flask框架運用Ajax實現(xiàn)數(shù)據(jù)交互的示例代碼
使用Ajax技術網(wǎng)頁應用能夠快速地將增量更新呈現(xiàn)在用戶界面上,而不需要重載刷新整個頁面,這使得程序能夠更快地回應用戶的操作,本文將簡單介紹使用AJAX如何實現(xiàn)前后端數(shù)據(jù)通信2022-11-11
python3模擬實現(xiàn)xshell遠程執(zhí)行l(wèi)inux命令的方法
今天小編就為大家分享一篇python3模擬實現(xiàn)xshell遠程執(zhí)行l(wèi)inux命令的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-07-07

