美文网首页第三方支付开发支付功能Python 运维
支付宝移动支付服务器异步通知 python 实现

支付宝移动支付服务器异步通知 python 实现

作者: 闪了腰的企鹅 | 来源:发表于2016-02-29 16:05 被阅读2955次

    支付宝移动支付成功后,支付宝会给商户端预先设置的 notifyURL 发送异步通知,商户端收到支付状态的异步通知后,需要给支付宝返回商户端的处理状态。

    简单逻辑如下(服务器异步通知方面逻辑):

    1. 移动App通过支付宝发起支付
    2. 支付宝服务端向商户端发送支付状态
    3. 商户端验证支付宝发来的请求的签名
    4. 商户端验证请求是否来源支付宝
    5. 商户端反馈支付宝处理结果

    涉及 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>

    相关文章

      网友评论

      • 30dfe321c6a6:我前段时间也集成了alipay,服务端,写了一个公用的代码,放到了github上,如果有朋友有需要,也可以看一下:
        https://github.com/albertmenglongli/ToolsLibrary/blob/master/payment/alipay_server_sdk.py
        30dfe321c6a6:@日光倾城也未必温暖_7079 可以用啊,都上生产了,抱歉,我不怎么上简书,所以才看到消息
        748f4a44e611:能用?
      • ca63366e0798:使用 prcrupto 签名 支付宝返回 ILLEGAL_SIGN,请问你遇到过吗
        闪了腰的企鹅:@nimda_ 没有哟,sign 不合法?
      • 一曲广陵散:你好,我将支付宝传回的sign参数, base64.decodestring(sign)后,为什么得到的是乱码,不知道是不是这个原因导致了,验证签名失败(Verification failed)
        闪了腰的企鹅:@一曲广陵散 nice.
        一曲广陵散:@闪了腰的企鹅 谢谢提醒,sign解码后出现乱码不是验证失败的原因,是我把支付宝公钥写错了,现在已经在你的帮助下解决了 :dolphin:
        闪了腰的企鹅:@一曲广陵散 print base64.decodestring(sign) 的确是乱码,但是 调用 rsa.verify(content, signatureString, pubkey) 可以通过认证。所以问题不在乱码与非乱码。如果你 Verification failed,你可以从下面这三个地方再确认一下:
        1. content 是否序列正确(包括 key 是否多了,少了),并且是否经过 content.encode("utf-8")
        2. sign 是否经过 base64.decodestring (按你的意思,排除这一步)
        3. pubkey 是否正确
        就上面这 3 种情况,逐一排查一下吧。

      本文标题:支付宝移动支付服务器异步通知 python 实现

      本文链接:https://www.haomeiwen.com/subject/pnegkttx.html