支付寶移動支付成功后,支付寶會給商戶端預(yù)先設(shè)置的 notifyURL 發(fā)送異步通知死陆,商戶端收到支付狀態(tài)的異步通知后,需要給支付寶返回商戶端的處理狀態(tài)唧瘾。
簡單邏輯如下(服務(wù)器異步通知方面邏輯):
- 移動App通過支付寶發(fā)起支付
- 支付寶服務(wù)端向商戶端發(fā)送支付狀態(tài)
- 商戶端驗證支付寶發(fā)來的請求的簽名
- 商戶端驗證請求是否來源支付寶
- 商戶端反饋支付寶處理結(jié)果
涉及 Python 庫 rsa 和 requests
rsa: <code>pip install rsa</code>
requests: <code>pip install requests</code>
主要代碼如下(Django環(huán)境):
view.py:
# ...
import AlipayHelper as AliPay
# ...
@csrf_exempt
def paycallbackAliPay(request):
if request.method == 'POST':
if not len(request.POST) > 0:
return HttpResponse(status=400)
alipay = AliPay.AliPay()
# ?驗證簽名 sign
if alipay.verifySignString(request.POST):
notify_id = request.POST['notify_id']
parter = request.POST['seller_id']
# 驗證是否是支付寶發(fā)來的通知
if alipay.verifyURL(parter,notify_id):
# 處理服務(wù)器端邏輯措译,更新數(shù)據(jù)庫等
# ...
# ...
# ...
print '-------- pay success ------------'
# 向支付寶返回?成功接收并處理異步通知狀態(tài)
return HttpResponse("SUCCESS")
return HttpResponse(status=400)
AlipayHelper.py
# -*- coding: utf-8 -*-
import rsa
import base64
import requests
# 支付寶 RSA 公鑰
ALIPAY_RSA_PUBLIC_KEY_PATH = 'alipay_rsa_public_key.pem'
# 驗證是否是支付寶發(fā)來的通知鏈接地址
ALIPAY_REMOTE_ORIGIN_URL = 'https://mapi.alipay.com/gateway.do'
class AliPay():
# 驗證簽名
# params:request.POST
def verifySignString(self,params):
if not len(params) > 0:
return False
key_sorted = sorted(params.keys())
content = ''
sign_type = params["sign_type"]
signOrigin = params["sign"]
for key in key_sorted:
if key not in ["sign","sign_type"]:
if len(params[key]) > 0:
content = content + key + "=" + params[key] + "&"
content = content[:-1]
content = content.encode("utf-8")
# print "content -> " + content
if sign_type.upper() == "RSA":
try:
with open(ALIPAY_RSA_PUBLIC_KEY_PATH) as publickfile:
pub = publickfile.read()
pubkey = rsa.PublicKey.load_pkcs1_openssl_pem(pub)
# ?支付寶返回的 sign 經(jīng)過 base64 encode,先 decode 一下
signatureString = base64.decodestring(signOrigin)
if rsa.verify(content, signatureString, pubkey):
# print "----------verify sign success----------"
return True
except Exception,e:
# print "----------verify sign failed----------"
return False
else:
# ?支付寶當前僅支持 RSA 加密饰序,未來也許會有其他類型
return False
return False
# 驗證是否是支付寶發(fā)來的通知
# partner:request.POST["seller_id"]领虹,也可以 hardcode
# notify_id:request.POST["notify_id"]
def verifyURL(self, partner, notify_id):
payload = {'service':'notify_verify','partner':partner,'notify_id':notify_id}
urlString = ALIPAY_REMOTE_ORIGIN_URL
r = requests.get(urlString,params=payload)
result = r.text
# print result
if result.upper() == "TRUE":
# print "----------verify ?url success----------"
return True
return False
注意點就是,支付寶返回的 sign 是 base64 encode 過的求豫,需要先 decode塌衰,再調(diào)用方法 <code>rsa.verify()</code>