美文网首页
11-Flask之支付宝集成

11-Flask之支付宝集成

作者: EndEvent | 来源:发表于2018-10-18 00:18 被阅读728次

    一、登录蚂蚁金服网站

    https://open.alipay.com/developmentAccess/developmentAccess.htm
    

    二、配置沙箱应用环境

    沙箱应用: 支付宝给开发者提供的调试环境应用

    • 沙盒应用界面


      信息配置
    • 生成RSA密钥
      支付宝提供一键生成工具便于开发者生成一对RSA密钥.

      下载该工具后,解压打开文件夹,运行“RSA签名验签工具.bat”(WINDOWS)或“RSA签名验签工具.command”(MAC_OSX)

    生成的私钥需妥善保管,避免遗失,不要泄露。应用私钥需填写到代码中供签名时使用。应用公钥需提供给支付宝账号管理者上传到支付宝开放平台。

    • 上传自己的公钥
      生成公钥后,将公钥填写到信息配置


      上传自己的公钥

    上述中生成了生成RSA密钥操作中,对应的应用公钥2048.txt即是公钥!

    • 保存支付宝公钥
      将生成的支付宝公钥,复制到一个txt文件中


      支付公钥
    • 秘钥个数确定
      这样我们就有了3个秘钥,看下图!


      秘钥文件
    • 秘钥文件的修改
      在秘钥txt文件里,秘钥内容的头部和结尾加上两行字符串,注意所有秘钥都要加。

    -----BEGIN PRIVATE KEY-----
    # 秘钥内容....
    -----END PRIVATE KEY-----
    

    后续方便操作,直接读取文件即可

    三、支付宝集成到项目

    • 基本流程
    1. 接收商品名称信息、订单金额、订单号
    2. 请求支付宝预付订单创建接口
    3. 根据返回的URL返回给客户端
    4. 用户支付
    5. 用户支付完成之后,客户端会跳转到一个页面(服务器预置页面)
    6. 用户支付完成之后,支付宝会调用回调(通知服务器)
    
    • 安装
    # 需要模块加密方面的模块
    pip install pycryptodome   
    
    • 支付宝封装及初始化配置 alipay/init.py
    # __init__.py文件(直接拷贝)
    from datetime import datetime
    from Crypto.PublicKey import RSA
    from Crypto.Signature import PKCS1_v1_5
    from Crypto.Hash import SHA256
    from base64 import b64encode, b64decode
    from urllib.parse import quote_plus
    from urllib.parse import urlparse, parse_qs
    from urllib.request import urlopen
    from base64 import decodebytes, encodebytes
    import json
    
    # 支付宝封装(不需要做任何修改)
    class AliPay(object):
        """
        支付宝支付接口
        """
        def __init__(self, appid, app_notify_url, app_private_key_path,
                     alipay_public_key_path, return_url, debug=False):
            self.appid = appid
            self.app_notify_url = app_notify_url
            self.app_private_key_path = app_private_key_path
            self.app_private_key = None
            self.return_url = return_url
            with open(self.app_private_key_path) as fp:
                self.app_private_key = RSA.importKey(fp.read())
    
            self.alipay_public_key_path = alipay_public_key_path
            with open(self.alipay_public_key_path) as fp:
                self.alipay_public_key = RSA.import_key(fp.read())
    
            if debug is True:
                self.__gateway = "https://openapi.alipaydev.com/gateway.do"
            else:
                self.__gateway = "https://openapi.alipay.com/gateway.do"
    
        def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):
            biz_content = {
                "subject": subject,
                "out_trade_no": out_trade_no,
                "total_amount": total_amount,
                "product_code": "FAST_INSTANT_TRADE_PAY",
                # "qr_pay_mode":4
            }
    
            biz_content.update(kwargs)
            data = self.build_body("alipay.trade.page.pay", biz_content, self.return_url)
            return self.sign_data(data)
    
        def build_body(self, method, biz_content, return_url=None):
            data = {
                "app_id": self.appid,
                "method": method,
                "charset": "utf-8",
                "sign_type": "RSA2",
                "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                "version": "1.0",
                "biz_content": biz_content
            }
    
            if return_url is not None:
                data["notify_url"] = self.app_notify_url
                data["return_url"] = self.return_url
    
            return data
    
        def sign_data(self, data):
            data.pop("sign", None)
            # 排序后的字符串
            unsigned_items = self.ordered_data(data)
            unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)
            sign = self.sign(unsigned_string.encode("utf-8"))
            ordered_items = self.ordered_data(data)
            quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in ordered_items)
    
            # 获得最终的订单信息字符串
            signed_string = quoted_string + "&sign=" + quote_plus(sign)
            return signed_string
    
        def ordered_data(self, data):
            complex_keys = []
            for key, value in data.items():
                if isinstance(value, dict):
                    complex_keys.append(key)
    
            # 将字典类型的数据dump出来
            for key in complex_keys:
                data[key] = json.dumps(data[key], separators=(',', ':'))
    
            return sorted([(k, v) for k, v in data.items()])
    
        def sign(self, unsigned_string):
            # 开始计算签名
            key = self.app_private_key
            signer = PKCS1_v1_5.new(key)
            signature = signer.sign(SHA256.new(unsigned_string))
            # base64 编码,转换为unicode表示并移除回车
            sign = encodebytes(signature).decode("utf8").replace("\n", "")
            return sign
    
        def _verify(self, raw_content, signature):
            # 开始计算签名
            key = self.alipay_public_key
            signer = PKCS1_v1_5.new(key)
            digest = SHA256.new()
            digest.update(raw_content.encode("utf8"))
            if signer.verify(digest, decodebytes(signature.encode("utf8"))):
                return True
            return False
    
        def verify(self, data, signature):
            if "sign_type" in data:
                sign_type = data.pop("sign_type")
            # 排序后的字符串
            unsigned_items = self.ordered_data(data)
            message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)
            return self._verify(message, signature)
            
    
    # 初始化操作
    # 设置秘钥公钥的存放路径
    app_private_key_path = os.path.join(BASE_DIR, 'alipay/ying_yong_si_yao.txt')
    alipay_public_key_path = os.path.join(BASE_DIR, 'alipay/zhi_fu_bao_gong_yao.txt')
    # 根据自己申请的进行设置 
    alipay = AliPay(
        appid="2016091800542542",  # 设置签约的appid
        app_notify_url="http://112.74.55.3/notify/",   # 异步支付通知url
        app_private_key_path=app_private_key_path,  # 设置应用私钥
        alipay_public_key_path=alipay_public_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
        debug=True,  # 默认False,            # 设置是否是沙箱环境,True是沙箱环境
        return_url="http://112.74.55.3/result/",   # 同步支付通知url,在这个页面可以展示给用户看,只有付款成功后才会跳转
    )
    
    备注: return_url 和 notify_url的区别  
        return_url: 
            如果设置了return_url,买家付款成功后会跳到return_url所在页面;
            在这个页面可以展示给用户看,只有付款成功后才会跳转;
            
        notify_url:
            服务器后台通知,这个页面是程序后台运行的(买家和卖家都看不到);
            买家付完款后,支付宝会调用notify_url这个页面所在的页面并把相应的参数传递到这个页面;
            这个页面根据支付宝传递过来的参数修改网站订单的状态,更新完订单后需要在页面上;
            打印出一个success给支付宝,如果反馈给支付宝的不是success,支付宝会继续调用这个页面;
    
    • 应用公钥(设置自己应用的公钥) alipay/ying_yong_gong_yao.txt
    -----BEGIN PRIVATE KEY-----
    MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5X3ezQUT2Cg5ujqoz+dfEBTGzavJRxG8CsXMzs5ualReWI0i3Itn15rG9mqYQUqIsfte2gAHc3+rNgMR8oEjcVrgY0qcPl/D3rj+/h0vYHl7lT5g93+n7/CVlk9KOhCm217kFV3gAJYusIXk6jWtpFXl2nXq1BlNhDgrBli4dozJ2k99OSUfNndrpuGfAVIOGIG4dsff598k+gb92eXwT151DMrqoV++387vRRnNnnBqbnYldBodBJXNyEsUCaf+CtacJy72tL6IwRVErD2MVuGjMWQ25Y1qWa/kNXI6ElIDljZylpWFDfMySMFkyOQra3RePlda7pDeKLmPwbwHpQIDAQAB
    -----END PRIVATE KEY-----
    
    • 应用私有(设置自己应用的私有) alipay/ying_yong_si_yao.txt
    -----BEGIN PRIVATE KEY-----
    MIIEpQIBAAKCAQEA5X3ezQUT2Cg5ujqoz+dfEBTGzavJRxG8CsXMzs5ualReWI0i3Itn15rG9mqYQUqIsfte2gAHc3+rNgMR8oEjcVrgY0qcPl/D3rj+/h0vYHl7lT5g93+n7/CVlk9KOhCm217kFV3gAJYusIXk6jWtpFXl2nXq1BlNhDgrBli4dozJ2k99OSUfNndrpuGfAVIOGIG4dsff598k+gb92eXwT151DMrqoV++387vRRnNnnBqbnYldBodBJXNyEsUCaf+CtacJy72tL6IwRVErD2MVuGjMWQ25Y1qWa/kNXI6ElIDljZylpWFDfMySMFkyOQra3RePlda7pDeKLmPwbwHpQIDAQABAoIBAQDIh24SE+e9L5kRw4QS9we4E4+L1BnywepgN2q5IuSUgNhX5CucLywz14kXdRlJpAlCDHZbYz8fi2Vdst0R3AnOWSxNVrqkPiX3DguynGnzkPON+43bdm6WWNE9IFep2TcT5RzEFt1PZeuyT60xgmoSeQR6GiNx5hI4AKSHzSqSWteL9xd/IUbHrA4YBXhX/oqJdrQr7Ri+380qoHHLiz5gNkXzVGFKrqVwsvVOBGdQ75S+wSTmJJ6hYm9vF4T7FQk8Ar22/22ZFuNLa1IMDXEe8Oh5NVCoXQqMFyhAYf7oN4AzZrBSvLmd85wfQSmvbthmL2JdOynEytYVa7Q2F3zZAoGBAPp6RDO16jEZySrX4T/eKukCD1Cq2g1LAPsq8dxjfdSgNxtz9qt24vuflZu2RbW/VoLZxDZJEIGhY7x0ClhioEwixBCHuPcXLFRd4mvxawzpJBRg9z0Mquf1cret6CBqsML+hgqlu2WwAbgX6hJ3ZJ8Sz3zICoG3K1NgzQkAbsybAoGBAOqNJ/r56jtB42XeGrNRE9bmpOnNDMN56oKiGDIMMe/FDjwYJw/dX7Ih3k0AAGgQWgdq03s+6ji3sI65mcqUrwBf6gSufDWrOvolbKIIfiC1dn/h81HsfkC5y02hTjfmfYg/nCXF5WUsVahrYRgTZL6iHrav2EighyxZCHDlWiC/AoGBAKdkiqvcSTSjPPOq7lgWi6rIu593bt4vX+p7WsOkofU1VV69cv5IJ41tqzgy6gd6ZcBw9FYrIiLfzp2lFzzWPLZlDY7WhWBsVi3TyHvHDXnFXV1ZKCHek2ENgasLR/RSGOtX2hDHjaN5qsxy/vV0YQSGN6/2qD7kQMoqCRyY83M9AoGBAJrvUViVG2e3V1nkBCktRnivdIJIg8TgVMuj/W2Z5qamuYdywOLW7a7CdpDQjnQvUPkTVM1VuuxqUoOFEJTj8FHECda716qwCE5SyFakW4SJudQx0kPsf12MHUKTahK8+3Ez65Z8CdLyHUcaX1HDBcpzVuyAbdNTzta5nTzOE+ILAoGAF0dMOdqnprsr82MudHqjIZMSanfd+QHgARNT0IQVxIFBIsV4PkGmHKU2EZdcB604mZYqAKaCJy+LZIE6VGpgGydRZ98RSK/jaNprge5cm9KPy9nGdfVKBhPkrU3Lkcix0gcvRWC3HVVaYMiZlS1i1tpwgI8z0xtoA9EoLJGxuYc=
    -----END PRIVATE KEY-----
    
    • 支付宝公钥(设置自己申请支付宝公钥) alipay/zhi_fu_bao_gong_yao.txt
    -----BEGIN PRIVATE KEY-----
    MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtaQiWU7tUcDzqbTT5cWF6PS9jn6sMlpG4wR1YFhUo8TDqW1FI5wi/dfylOqJSS2lV8OOuzMk80Ajq9wbyQ0oB+dmrgcZIwF8bLkHvGyO83SAmmNmZ8bcnlD91EnS2c+vIF02ADpE/NKaX+5gjXj9FMTr5z6cqrpfjxWYB7FCp77fd1hd6TbzkMaW70RzBkWGTVMG91p7uZexVKkURPl5Sb8KRnXSmTkjTTWBFZOsARFVu7nsbMjd+q3RYni3BvhfKyZIW/PtQ9HUrX3tHbGThdj2D4qgO3mWB51IdH0gM9OOTyFYvm9TMJGeJ6KdeELlGLNVGhRAbK4NJjPlnuVd2wIDAQAB
    -----END PRIVATE KEY-----
    
    • 支付接口
    # 定义资源
    class Pay(Resource):
        def post(self):
            # 获取订单号,根据订单生成 支付订单
            # 支付订单包括: 订单号、支付金额、订单名称
    
            # 传递参数执行支付类里的direct_pay方法,返回签名后的支付参数,
            url = alipay.direct_pay(
                subject="测试订单",  # 订单名称
                # 订单号生成,一般是当前时间(精确到秒)+用户ID+随机数
                out_trade_no="201810021221",  # 订单号
                total_amount=100,  # 支付金额
                return_url="http://112.74.55.3/result/"  # 支付成功后,跳转url 【客户端显示】
            )
    
            # 将前面后的支付参数,拼接到支付网关
            # 注意:下面支付网关是沙箱环境,最终进行签名后组合成支付宝的url请求
            re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
           
            # 返回的是支付宝的支付地址
            return {'re_url': re_url}
            
    # 添加资源
    api.add_resource(Pay, '/paytest/')  # 支付接口
    

    支付账号密码在沙盒账号中,买家账号: gpeggv3096@sandbox.com, 登录密码: 111111, 支付密码: 111111

    • 支付成功跳转的路由(客户端显示)
    @blue.route('/result/')
    def result():
        # 订单支付完成,跳转到对应的页面
        
        return render_template('cart.html')
    
    # 支付宝返回的数据
    data = {
        'seller_id': '2088102176233911',
        'trade_no': '2018101822001438720500654194', # 订单号
        'timestamp': '2018-10-18 00:12:54',
        'app_id': '2016091800542542',
        'version': '1.0',
        'sign_type': 'RSA2',
        'out_trade_no': 'Thu Oct 18 00:09:45 20187265',
        'auth_app_id': '2016091800542542',
        'total_amount': '123.00',
        'charset': 'utf-8',
        'method': 'alipay.trade.page.pay.return'
        'sign': 'cjok55ELAfbGc9RfwsS93pplaFNUIv0hAsfzbklDU+5sdnq0lc9K1a+IRO8MJZpwCX9OjBLXqY64LEa2cM5hv0bly2HnEmbNTta8e15VdvXFH//zMWT05HeNmXu2+H3Kq2RzQbFs2LIdFD2HmYDnbkwhTyAxnhQWEn7yZdAeD/7vNjGoREW2Xbh3aV5ukSFBihzwTGJNMNxyKei6KxQwJRJKDyV0fdZyH6i39xoyp8aMfQGZqRC9HbKKBW6BsFg/At1hVsMJQ4Ib/6jK2NMNMckUIf1gXLrY4Gv/LaqVSYuZpwNQoEwgHYHiC9KUa0BhsgAlJDEpARliLnvyUUOJKA=='
    }
    
    • 支付成功通知接口(支付宝通知服务器)
    # 定义资源
    class Paynotify(Resource):  # 异步支付通知
        def post(self):
            # 获取 支付成功的 订单号
            # 修改订单状态以及其他操作
            
            # 返回支付宝success,否则会不间断的调用该回调
            return {'msg':'success'}
            
    # 添加资源
    api.add_resource(Paynotify, '/notify/') # 支付成功后的回调
    
    # 支付宝返回的数据
    data = {
         "subject": "测试订单",
         "gmt_payment": "2016-11-16 11:42:19",
         "charset": "utf-8",
         "seller_id": "xxxx",
         "trade_status": "TRADE_SUCCESS",
         "buyer_id": "xxxx",
         "auth_app_id": "xxxx",
         "buyer_pay_amount": "0.01",
         "version": "1.0",
         "gmt_create": "2016-11-16 11:42:18",
         "trade_no": "xxxx",    # 订单号
         "fund_bill_list": "[{\"amount\":\"0.01\",\"fundChannel\":\"ALIPAYACCOUNT\"}]",
         "app_id": "xxxx",
         "notify_time": "2016-11-16 11:42:19",
         "point_amount": "0.00",
         "total_amount": "0.01",
         "notify_type": "trade_status_sync",
         "out_trade_no": "xxxx",
         "buyer_logon_id": "xxxx",
         "notify_id": "xxxx",
         "seller_email": "xxxx",
         "receipt_amount": "0.01",
         "invoice_amount": "0.01",
         "sign": "xxx"
    }
    

    支付宝API文档(通知参数说明): https://docs.open.alipay.com/204/105301/

    相关文章

      网友评论

          本文标题:11-Flask之支付宝集成

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