支付宝移动支付成功后,支付宝会给商户端预先设置的 notifyURL 发送异步通知,商户端收到支付状态的异步通知后,需要给支付宝返回商户端的处理状态。
简单逻辑如下(服务器异步通知方面逻辑):
- 移动App通过支付宝发起支付
- 支付宝服务端向商户端发送支付状态
- 商户端验证支付宝发来的请求的签名
- 商户端验证请求是否来源支付宝
- 商户端反馈支付宝处理结果
涉及 Python 库 rsa 和 requests
rsa: <code>pip install rsa</code>
requests: <code>pip install requests</code>
主要代码如下(Django环境):
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']
# 验证是否是支付宝发来的通知
if alipay.verifyURL(parter,notify_id):
# 处理服务器端逻辑,更新数据库等
# ...
# ...
# ...
print '-------- pay success ------------'
# 向支付宝返回�成功接收并处理异步通知状态
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'
# 验证是否是支付宝发来的通知链接地址
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 经过 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
# 验证是否是支付宝发来的通知
# 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,再调用方法 <code>rsa.verify()</code>
网友评论
https://github.com/albertmenglongli/ToolsLibrary/blob/master/payment/alipay_server_sdk.py
1. content 是否序列正确(包括 key 是否多了,少了),并且是否经过 content.encode("utf-8")
2. sign 是否经过 base64.decodestring (按你的意思,排除这一步)
3. pubkey 是否正确
就上面这 3 种情况,逐一排查一下吧。