zoukankan      html  css  js  c++  java
  • 支付功能

    Django rest framework之支付功能

    一.支付宝支付

      1.进入蚂蚁金服开放平台(查看api):

        1.1在正式生产环境中需要创建应用(需审核):

        1.2沙箱环境(测试):

    可以在文档中查看对应的api接口

    对应接口参数,sign_type加密算法推荐RSA2更安全

    sign签名中生成RSA密钥,下载生成密钥后会生成两个文件(公钥和私钥文件)

    将公钥填写在在RSA

    在生成的公钥和私钥文件头和尾加上(-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY-----)

    取下支付宝公钥放在文件中(用于查询·订单状态)

    共有三个文件(公钥,私钥,支付宝公钥)

    支付接口部分参数

    拼接参数并签名赋值给sign

      2.付款接口代码(依赖pycryptodome包):

          2.1会生成支付宝返回的url,并解读,可支付(测试)

      1 # -*- coding: utf-8 -*-
      2 __author__ = 'LYQ'
      3 # pip install pycryptodome,需要安装
      4 
      5 from datetime import datetime
      6 from Crypto.PublicKey import RSA
      7 from Crypto.Signature import PKCS1_v1_5
      8 from Crypto.Hash import SHA256
      9 from base64 import b64encode, b64decode
     10 from urllib.parse import quote_plus
     11 from urllib.parse import urlparse, parse_qs
     12 from urllib.request import urlopen
     13 from base64 import decodebytes, encodebytes
     14 
     15 import json
     16 
     17 
     18 class AliPay(object):
     19     """
     20     支付宝支付接口
     21     """
     22 
     23     def __init__(self, appid, app_notify_url, app_private_key_path,
     24                  alipay_public_key_path, return_url, debug=False):
     25         '''
     26         初始化参数appid,app_notify_url,app_private_key_path(这是私钥文件的路径),
     27         app_private_key初始化为None,打开私钥文件用Crypto.PublicKey中的RSA.importKey()对私钥加密
     28         alipay_public_key用同样方法加密
     29         '''
     30         self.appid = appid
     31         self.app_notify_url = app_notify_url
     32         self.app_private_key_path = app_private_key_path
     33         self.app_private_key = None
     34         self.return_url = return_url
     35         with open(self.app_private_key_path) as fp:
     36             self.app_private_key = RSA.importKey(fp.read())
     37 
     38         self.alipay_public_key_path = alipay_public_key_path
     39         with open(self.alipay_public_key_path) as fp:
     40             self.alipay_public_key = RSA.import_key(fp.read())
     41 
     42         if debug is True:
     43             self.__gateway = "https://openapi.alipaydev.com/gateway.do"
     44         else:
     45             self.__gateway = "https://openapi.alipay.com/gateway.do"
     46 
     47     def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):
     48        #应用请求参数,对应文档字段(有四个参数必填)
     49         biz_content = {
     50             "subject": subject,
     51             "out_trade_no": out_trade_no,
     52             "total_amount": total_amount,
     53             "product_code": "FAST_INSTANT_TRADE_PAY",
     54             # "qr_pay_mode":4
     55         }
     56         #对传入的参数更新biz_content
     57         biz_content.update(kwargs)
     58         data = self.build_body("alipay.trade.page.pay", biz_content, self.return_url)
     59         return self.sign_data(data)
     60 
     61     def build_body(self, method, biz_content, return_url=None):
     62         #生成主要参数,与biz_content同级,嵌套
     63         data = {
     64             "app_id": self.appid,
     65             "method": method,
     66             "charset": "utf-8",
     67             "sign_type": "RSA2",
     68             "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
     69             "version": "1.0",
     70             "biz_content": biz_content
     71         }
     72 
     73         if return_url is not None:
     74             #还是公共请求参数
     75             data["notify_url"] = self.app_notify_url
     76             data["return_url"] = self.return_url
     77 
     78         return data
     79 
     80     def sign_data(self, data):
     81         #签名时不能有sign字段,去掉sign字段
     82         data.pop("sign", None)
     83         # 参数需要排序,排序后的字符串,数组里面嵌套元组"[(),()...]"
     84         unsigned_items = self.ordered_data(data)
     85         unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)
     86         sign = self.sign(unsigned_string.encode("utf-8"))
     87         # ordered_items = self.ordered_data(data)
     88         #quote_plus()对url处理
     89         quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in unsigned_items)
     90 
     91         # 获得最终的订单信息字符串
     92         signed_string = quoted_string + "&sign=" + quote_plus(sign)
     93         return signed_string
     94 
     95     def ordered_data(self, data):
     96         complex_keys = []
     97         for key, value in data.items():
     98             if isinstance(value, dict):
     99                 complex_keys.append(key)
    100 
    101         # 将字典类型的数据dump出来
    102         for key in complex_keys:
    103             data[key] = json.dumps(data[key], separators=(',', ':'))
    104 
    105         return sorted([(k, v) for k, v in data.items()])
    106 
    107     def sign(self, unsigned_string):
    108         # 开始计算签名
    109         key = self.app_private_key
    110         signer = PKCS1_v1_5.new(key)
    111         #调用算法生成签名
    112         signature = signer.sign(SHA256.new(unsigned_string))
    113         # 进行base64 编码,转换为unicode表示并移除回车
    114         sign = encodebytes(signature).decode("utf8").replace("
    ", "")
    115         return sign
    116 
    117     def _verify(self, raw_content, signature):
    118         # 开始计算签名
    119         key = self.alipay_public_key
    120         signer = PKCS1_v1_5.new(key)
    121         digest = SHA256.new()
    122         digest.update(raw_content.encode("utf8"))
    123         if signer.verify(digest, decodebytes(signature.encode("utf8"))):
    124             return True
    125         return False
    126 
    127     def verify(self, data, signature):
    128         if "sign_type" in data:
    129             sign_type = data.pop("sign_type")
    130         # 排序后的字符串
    131         unsigned_items = self.ordered_data(data)
    132         message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)
    133         return self._verify(message, signature)
    134 
    135 
    136 if __name__ == "__main__":
    137     return_url = 'http://127.0.0.1:8000/?total_amount=100.00&timestamp=2017-08-15+23%3A53%3A34&sign=e9E9UE0AxR84NK8TP1CicX6aZL8VQj68ylugWGHnM79zA7BKTIuxxkf%2FvhdDYz4XOLzNf9pTJxTDt8tTAAx%2FfUAJln4WAeZbacf1Gp4IzodcqU%2FsIc4z93xlfIZ7OLBoWW0kpKQ8AdOxrWBMXZck%2F1cffy4Ya2dWOYM6Pcdpd94CLNRPlH6kFsMCJCbhqvyJTflxdpVQ9kpH%2B%2Fhpqrqvm678vLwM%2B29LgqsLq0lojFWLe5ZGS1iFBdKiQI6wZiisBff%2BdAKT9Wcao3XeBUGigzUmVyEoVIcWJBH0Q8KTwz6IRC0S74FtfDWTafplUHlL%2Fnf6j%2FQd1y6Wcr2A5Kl6BQ%3D%3D&trade_no=2017081521001004340200204115&sign_type=RSA2&auth_app_id=2016080600180695&charset=utf-8&seller_id=2088102170208070&method=alipay.trade.page.pay.return&app_id=2016080600180695&out_trade_no=20170202185&version=1.0'
    138     o = urlparse(return_url)
    139     query = parse_qs(o.query)
    140     processed_query = {}
    141     ali_sign = query.pop("sign")[0]
    142 
    143     alipay = AliPay(
    144         # 沙箱环境appid
    145         appid="2016091800536621",
    146         app_notify_url="http://47.106.211.59:8008/alipay/return/",
    147         # 私钥路径
    148         app_private_key_path="../trade/keys/private_2048.txt",
    149         alipay_public_key_path="../trade/keys/alipay_key_2048.txt",  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
    150         debug=True,  # 默认False,
    151         # 支付成功后跳转的url
    152         return_url="http://47.106.211.59:8008/alipay/return/"
    153     )
    154 
    155     # for key, value in query.items():
    156     #     processed_query[key] = value[0]
    157     # print(alipay.verify(processed_query, ali_sign))
    158 
    159     url = alipay.direct_pay(
    160         subject="测试订单4",
    161         out_trade_no="201667201",
    162         total_amount=100,
    163         return_url="http://47.106.211.59:8008/alipay/return/"
    164     )
    165     re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
    166 
    167     print(re_url)
    View Code

           2.2访问url效果如下:可用沙箱支付,支付成功会跳转到如图的return_url(同步),notify_url会发送异步请求

      3.支付宝通知接口验证: 

        3.1页面回跳参数:

        3.2接收查看:

          3.2.1返回参数查看

    大概内容如上,是一个字典

          3.2.2验证代码如下,verify()函数如上,返回False:
     1 if __name__ == "__main__":
     2     #返回的url
     3     return_url = 'http://127.0.0.1:8000/?total_amount=100.00&timestamp=2017-08-15+23%3A53%3A34&sign=e9E9UE0AxR84NK8TP1CicX6aZL8VQj68ylugWGHnM79zA7BKTIuxxkf%2FvhdDYz4XOLzNf9pTJxTDt8tTAAx%2FfUAJln4WAeZbacf1Gp4IzodcqU%2FsIc4z93xlfIZ7OLBoWW0kpKQ8AdOxrWBMXZck%2F1cffy4Ya2dWOYM6Pcdpd94CLNRPlH6kFsMCJCbhqvyJTflxdpVQ9kpH%2B%2Fhpqrqvm678vLwM%2B29LgqsLq0lojFWLe5ZGS1iFBdKiQI6wZiisBff%2BdAKT9Wcao3XeBUGigzUmVyEoVIcWJBH0Q8KTwz6IRC0S74FtfDWTafplUHlL%2Fnf6j%2FQd1y6Wcr2A5Kl6BQ%3D%3D&trade_no=2017081521001004340200204115&sign_type=RSA2&auth_app_id=2016080600180695&charset=utf-8&seller_id=2088102170208070&method=alipay.trade.page.pay.return&app_id=2016080600180695&out_trade_no=20170202185&version=1.0'
     4     o = urlparse(return_url)
     5     query = parse_qs(o.query)
     6     processed_query = {}
     7     #把sign给pop出
     8     ali_sign = query.pop("sign")[0]
     9 
    10     alipay = AliPay(
    11         # 沙箱环境appid
    12         appid="2016091800536621",
    13         app_notify_url="http://47.106.211.59:8008/alipay/return/",
    14         # 私钥路径
    15         app_private_key_path="../trade/keys/private_2048.txt",
    16         alipay_public_key_path="../trade/keys/alipay_key_2048.txt",  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
    17         debug=True,  # 默认False,
    18         # 支付成功后跳转的url
    19         return_url="http://47.106.211.59:8008/alipay/return/"
    20     )
    21 
    22     for key, value in query.items():
    23         processed_query[key] = value[0]
    24     #调用verify对信息对比,一样就返回True,否则返回False
    25     print(alipay.verify(processed_query, ali_sign))
    View Code

      4.django集成支付宝notify_url和return_url接口:

          

    return_url为同步

    notify_url为异步

    共有四种状态

          数据库model对应代码部分:
     1 class OrderInfo(models.Model):
     2     """
     3     订单
     4     """
     5 #对应四种状态
     6     ORDER_STATUS = (
     7         ("TRADE_SUCCESS", "成功"),
     8         ("TRADE_CLOSED", "超时关闭"),
     9         ("WAIT_BUYER_PAY", "交易创建"),
    10         ("TRADE_FINISHED", "交易结束"),
    11         ("paying", "待支付"),
    12     )
    13 ......
    View Code

    notify_url返回的业务参数

          处理return_url和notify_url的接口:
     1 class AlipayView(APIView):
     2     '''
     3     支付宝接口
     4     '''
     5 
     6     def get(self, request):
     7         '''
     8         处理return_url返回
     9         :param request: 
    10         :return: 
    11         '''
    12         process_dic = {}
    13         #把POST中的数据全取出来
    14         for key, value in request.GET.items():
    15             process_dic[key] = value
    16         #把sign去掉
    17         sign = process_dic.pop('sign', None)
    18         alipay = AliPay(
    19             # 沙箱环境appid
    20             appid="2016091800536621",
    21             app_notify_url="http://47.106.211.59:8008/alipay/return/",
    22             # 私钥路径
    23             app_private_key_path=private_key_path,
    24             alipay_public_key_path=ali_pub_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
    25             debug=True,  # 默认False,
    26             return_url="http://47.106.211.59:8008/alipay/return/"
    27         )
    28         #验证
    29         verify_re = alipay.verify(process_dic, sign)
    30         if verify_re is True:
    31             order_sn = process_dic.get('order_sn', None)
    32             trade_no = process_dic.get('trade_no', None)
    33             trade_status = process_dic.get('trade_status', None)
    34             exsisted_orders = OrderInfo.objects.filter(order_sn=order_sn)
    35             for exsisted_order in exsisted_orders:
    36                 #销量数目修改
    37                 order_goods=exsisted_order.objects.all()
    38                 for order_good in order_goods:
    39                     goods=order_good.goods()
    40                     goods.sold_num+=order_good.goods_num
    41                     goods.save()
    42                 exsisted_order.pay_status = trade_status
    43                 exsisted_order.trade_no = trade_no
    44                 exsisted_order.pay_time = datetime.now()
    45                 exsisted_order.save()
    46             response = redirect("index")
    47             response.set_cookie("nextPath","pay", max_age=3)
    48             return response
    49         else:
    50             response = redirect("index")
    51             return response
    52 
    53     def post(self, request):
    54         '''
    55         处理notify_url,会把返回的所有参数放在post中
    56         :param request: 
    57         :return: 
    58         '''
    59         process_dic = {}
    60         for key, value in request.POST.items():
    61             process_dic[key] = value
    62         sign = process_dic.pop('sign', None)
    63         alipay = AliPay(
    64             # 沙箱环境appid
    65             appid="2016091800536621",
    66             app_notify_url="http://47.106.211.59:8008/alipay/return/",
    67             # 私钥路径
    68             app_private_key_path=private_key_path,
    69             alipay_public_key_path=ali_pub_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
    70             debug=True,  # 默认False,
    71             return_url="http://47.106.211.59:8008/alipay/return/"
    72         )
    73 
    74         verify_re = alipay.verify(process_dic, sign)
    75         if verify_re is True:
    76             #获取业务参数
    77             order_sn = process_dic.get('order_sn', None)
    78             trade_no = process_dic.get('trade_no', None)
    79             trade_status = process_dic.get('trade_status', None)
    80             exsisted_orders = OrderInfo.objects.filter(order_sn=order_sn)
    81             #保存到数据库
    82             for exsisted_order in exsisted_orders:
    83                 exsisted_order.pay_status = trade_status
    84                 exsisted_order.trade_no = trade_no
    85                 exsisted_order.pay_time = datetime.now()
    86                 exsisted_order.save()
    87             return Response("success")
    View Code 
          订单序列化:
     1 class OrderSerializer(serializers.ModelSerializer):
     2     '''
     3     订单序列化
     4     '''
     5     user = serializers.HiddenField(default=serializers.CurrentUserDefault())
     6     pay_status = serializers.CharField(read_only=True)
     7     order_sn = serializers.CharField(read_only=True)
     8     trade_no = serializers.CharField(read_only=True)
     9     pay_time = serializers.DateTimeField(read_only=True)
    10     add_time = serializers.DateTimeField(read_only=True)
    11     alipay_url = serializers.SerializerMethodField(read_only=True)
    12 
    13     def get_alipay_url(self, obj):
    14         '''
    15         生成url
    16         :return: 
    17         '''
    18         alipay = AliPay(
    19             # 沙箱环境appid
    20             appid="2016091800536621",
    21             app_notify_url="http://47.106.211.59:8008/alipay/return/",
    22             # 私钥路径
    23             app_private_key_path=private_key_path,
    24             alipay_public_key_path=ali_pub_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
    25             debug=True,  # 默认False,
    26             return_url="http://47.106.211.59:8008/alipay/return/"
    27         )
    28         url = alipay.direct_pay(
    29             subject=obj.order_sn,
    30             out_trade_no=obj.order_sn,
    31             total_amount=obj.order_mount,
    32             # return_url="http://47.106.211.59:8008/alipay/return/"
    33         )
    34         re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
    35         return re_url
    36 
    37     class Meta:
    38         model = OrderInfo
    39         fields = "__all__"
    40 
    41     def generate_order_sn(self):
    42         # 生成订单号(当前时间加用户id加随机数)
    43         random_ins = random.Random()
    44         order_sn = "{timestr}{userid}{ranstr}".format(timestr=time.strftime("%Y%m%d%H%M%S"),
    45                                                       userid=self.context['request'].user.id,
    46                                                       ranstr=random_ins.randint(10, 99))
    47         return order_sn
    48 
    49     def validate(self, attrs):
    50         attrs['order_sn'] = self.generate_order_sn()
    51         return attrs
    View Code
          订单详情序列化:
    class OrderDetaiSerializer(serializers.ModelSerializer):
        goods = OrderGoodsSerializer(many=True)
        alipay_url = serializers.SerializerMethodField(read_only=True)
    
        def get_alipay_url(self, obj):
            '''
            生成url
            :return: 
            '''
            alipay = AliPay(
                # 沙箱环境appid
                appid="2016091800536621",
                app_notify_url="http://47.106.211.59:8008/alipay/return/",
                # 私钥路径
                app_private_key_path=private_key_path,
                alipay_public_key_path=ali_pub_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                debug=True,  # 默认False,
                return_url="http://47.106.211.59:8008/alipay/return/"
            )
            url = alipay.direct_pay(
                subject=obj.order_sn,
                out_trade_no=obj.order_sn,
                total_amount=obj.order_mount,
                # return_url="http://47.106.211.59:8008/alipay/return/"
            )
            re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
            return re_url
    
        class Meta:
            model = OrderInfo
            fields = "__all__"
    View Code  

      5.注意:一般支付都是图片放在网页中,需要前端多做一个页面,也可以用django做代理页面,使用cnpm run build生成相应文件,放入django项目中,相应配置。

    二.微信支付

      待写

  • 相关阅读:
    POST请求
    怎样在ios开发中设置tableview的cell颜色
    error LNK2005 已经在***.obj中定义
    IOS开发之UIView的基本使用
    [置顶] 浅谈Android的资源编译过程
    IOS开发之UIView总结
    鉴别不使用的索引
    浅谈Jquery的使用下篇
    Go如何使用实现继承的组合
    做一个小淘气轮廓--文章和论文专辑
  • 原文地址:https://www.cnblogs.com/lyq-biu/p/9625980.html
Copyright © 2011-2022 走看看