• (项目)生鲜超市(十)


    十一、pycharm远程代码调试

      第三方登录和支付,都需要有线上服务器才行(需要回调url),我们可以用pycharm去远程调试服务器代码。

      首先需要一台云服务器,我用的是腾讯云的服务器,pycharm远程连接服务器及解释器的方法这里不细讲,如果有不懂的童靴可以私聊我,我会发视频给你。

    十二、支付宝沙箱环境配置

      订单结算是通过支付宝进行支付的,这里测试使用蚂蚁金服支付宝的沙箱环境测试支付流程,沙箱环境的配置也不细讲,如有需要请联系我发视频。

      将解释器的环境暂时改成本地进行调试,然后在utils下新建alipay.py文件,编写生成支付宝支付接口的url脚本,在编写之前需要pip install pycryptodome,这个库主要用来对密钥进行签名,编写代码:

      1 import json
      2 from datetime import datetime
      3 from Crypto.PublicKey import RSA
      4 from Crypto.Signature import PKCS1_v1_5
      5 from Crypto.Hash import SHA256
      6 from base64 import b64encode, b64decode
      7 from urllib.parse import quote_plus
      8 from urllib.parse import urlparse, parse_qs
      9 from urllib.request import urlopen
     10 from base64 import decodebytes, encodebytes
     11 
     12 
     13 class AliPay(object):
     14     """
     15     支付宝支付接口
     16     """
     17 
     18     def __init__(self, appid, app_notify_url, app_private_key_path,
     19                  alipay_public_key_path, return_url, debug=False):
     20         self.appid = appid
     21         self.app_notify_url = app_notify_url
     22         # 私钥
     23         self.app_private_key_path = app_private_key_path
     24         self.app_private_key = None
     25         self.return_url = return_url
     26         with open(self.app_private_key_path) as fp:
     27             self.app_private_key = RSA.importKey(fp.read())
     28         # 公钥
     29         self.alipay_public_key_path = alipay_public_key_path
     30         with open(self.alipay_public_key_path) as fp:
     31             self.alipay_public_key = RSA.import_key(fp.read())
     32 
     33         if debug is True:
     34             self.__gateway = "https://openapi.alipaydev.com/gateway.do"
     35         else:
     36             self.__gateway = "https://openapi.alipay.com/gateway.do"
     37 
     38     def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):
     39         # 请求参数
     40         biz_content = {
     41             "subject": subject,
     42             "out_trade_no": out_trade_no,
     43             "total_amount": total_amount,
     44             "product_code": "FAST_INSTANT_TRADE_PAY",
     45             # "qr_pay_mode":4
     46         }
     47         # 允许传递更多参数,放到biz_content
     48         biz_content.update(kwargs)
     49         data = self.build_body("alipay.trade.page.pay", biz_content, self.return_url)
     50         return self.sign_data(data)
     51 
     52     def build_body(self, method, biz_content, return_url=None):
     53         # build_body主要生产消息的格式
     54         # 公共请求参数
     55         data = {
     56             "app_id": self.appid,
     57             "method": method,
     58             "charset": "utf-8",
     59             "sign_type": "RSA2",
     60             "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
     61             "version": "1.0",
     62             "biz_content": biz_content
     63         }
     64 
     65         if return_url is not None:
     66             data["notify_url"] = self.app_notify_url
     67             data["return_url"] = self.return_url
     68 
     69         return data
     70 
     71     def sign_data(self, data):
     72         # 签名
     73         data.pop("sign", None)
     74         # 排序后的字符串
     75         unsigned_items = self.ordered_data(data)
     76         # 排完序后拼接起来
     77         unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)
     78         # 这里得到签名的字符串
     79         sign = self.sign(unsigned_string.encode("utf-8"))
     80         # 对url进行处理
     81         quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in unsigned_items)
     82 
     83         # 获得最终的订单信息字符串
     84         signed_string = quoted_string + "&sign=" + quote_plus(sign)
     85         return signed_string
     86 
     87     # 参数传进来一定要排序
     88     def ordered_data(self, data):
     89         complex_keys = []
     90         for key, value in data.items():
     91             if isinstance(value, dict):
     92                 complex_keys.append(key)
     93 
     94         # 将字典类型的数据dump出来
     95         for key in complex_keys:
     96             data[key] = json.dumps(data[key], separators=(',', ':'))
     97 
     98         return sorted([(k, v) for k, v in data.items()])
     99 
    100     def sign(self, unsigned_string):
    101         # 开始计算签名
    102         key = self.app_private_key
    103         # 签名的对象
    104         signer = PKCS1_v1_5.new(key)
    105         # 生成签名
    106         signature = signer.sign(SHA256.new(unsigned_string))
    107         # base64 编码,转换为unicode表示并移除回车
    108         sign = encodebytes(signature).decode("utf8").replace("
    ", "")
    109         return sign
    110 
    111     def _verify(self, raw_content, signature):
    112         # 开始计算签名
    113         key = self.alipay_public_key
    114         signer = PKCS1_v1_5.new(key)
    115         digest = SHA256.new()
    116         digest.update(raw_content.encode("utf8"))
    117         if signer.verify(digest, decodebytes(signature.encode("utf8"))):
    118             return True
    119         return False
    120 
    121     def verify(self, data, signature):
    122         if "sign_type" in data:
    123             sign_type = data.pop("sign_type")
    124         # 排序后的字符串
    125         unsigned_items = self.ordered_data(data)
    126         message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)
    127         return self._verify(message, signature)
    128 
    129 
    130 if __name__ == "__main__":
    131     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'
    132     o = urlparse(return_url)
    133     query = parse_qs(o.query)
    134     processed_query = {}
    135     ali_sign = query.pop("sign")[0]
    136 
    137     # 测试用例
    138     alipay = AliPay(
    139         # 沙箱里面的appid值
    140         appid="2016092000557473",
    141         # notify_url是异步的url
    142         app_notify_url="http://127.0.0.1:8000/",
    143         # 我们自己商户的密钥
    144         app_private_key_path="../trade/keys/private_2048.txt",
    145         # 支付宝的公钥
    146         alipay_public_key_path="../trade/keys/alipay_key_2048.txt",  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
    147         # debug为true时使用沙箱的url。如果不是用正式环境的url
    148         debug=True,  # 默认False,
    149         return_url="http://127.0.0.1:8000/alipay/return/"
    150     )
    151 
    152     for key, value in query.items():
    153         processed_query[key] = value[0]
    154     # print (alipay.verify(processed_query, ali_sign))
    155 
    156     # 直接支付:生成请求的字符串。
    157     url = alipay.direct_pay(
    158         # 订单标题
    159         subject="测试订单wj",
    160         # 我们商户自行生成的订单号
    161         out_trade_no="2018041721312",
    162         # 订单金额
    163         total_amount=100,
    164         # 成功付款后跳转到的页面,return_url同步的url
    165         # return_url="http://127.0.0.1:8000/"
    166     )
    167     # 将生成的请求字符串拿到我们的url中进行拼接
    168     re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
    169 
    170     print(re_url)

      直接运行该文件,会生成一段支付宝支付接口的url,点击这个url,会跳转到支付宝支付接口,将支付宝提供给你的沙箱环境中的买家账户进行付款测试:

      现在由django去集成支付宝的notify_url和return_url,首先配置支付宝接口的url:

    1 path('alipay/return/', AlipayView.as_view()),  # 支付宝接口

      在alipay.py中,将return_url和notify_url都改成远程服务器的地址:

    1 app_notify_url="http://148.70.2.75:8000/alipay/return/"
    2 return_url="http://148.70.2.75:8000/alipay/return/"

      在settings中配置公钥私钥路径:

    1 # 支付宝相关的key
    2 private_key_path = os.path.join(BASE_DIR, 'apps/trade/keys/private_2048.txt')
    3 ali_pub_key_path = os.path.join(BASE_DIR, 'apps/trade/keys/alipay_key_2048.txt')

      在trade/views.py中编写支付宝的接口:

     1 class AlipayView(APIView):
     2     """支付宝接口"""
     3 
     4     # 处理支付宝的return_url返回
     5     def get(self, request):
     6         processed_dict = {}
     7 
     8         # 获取GET中的参数
     9         for key, value in request.GET.items():
    10             processed_dict[key] = value
    11 
    12         # 从processed_dict中取出sign
    13         sign = processed_dict.pop("sign", None)
    14 
    15         # 生成AliPay对象
    16         alipay = AliPay(
    17             appid="2016092000557473",
    18             app_notify_url="http://148.70.2.75:8000/alipay/return/",
    19             app_private_key_path=private_key_path,
    20             alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
    21             debug=True,  # 默认False,
    22             return_url="http://148.70.2.75:8000/alipay/return/"
    23         )
    24 
    25         # 验证签名
    26         verify_re = alipay.verify(processed_dict, sign)
    27 
    28         # 这里可以不做操作。因为不管发不发return url。notify url都会修改订单状态。
    29         if verify_re is True:
    30             order_sn = processed_dict.get('out_trade_no', None)
    31             trade_no = processed_dict.get('trade_no', None)
    32             trade_status = processed_dict.get('trade_status', None)
    33 
    34             existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
    35             for existed_order in existed_orders:
    36                 existed_order.pay_status = trade_status
    37                 existed_order.trade_no = trade_no
    38                 existed_order.pay_time = datetime.now()
    39                 existed_order.save()
    40 
    41     # 处理支付宝的notify_url
    42     def post(self, request):
    43         processed_dict = {}
    44 
    45         # 取出post里面的数据
    46         for key, value in request.POST.items():
    47             processed_dict[key] = value
    48 
    49         # 去掉sign
    50         sign = processed_dict.pop("sign", None)
    51 
    52         # 生成一个Alipay对象
    53         alipay = AliPay(
    54             appid="2016092000557473",
    55             app_notify_url="http://148.70.2.75:8000/alipay/return/",
    56             app_private_key_path=private_key_path,
    57             alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
    58             debug=True,  # 默认False,
    59             return_url="http://148.70.2.75:8000/alipay/return/"
    60         )
    61 
    62         # 进行验证
    63         verify_re = alipay.verify(processed_dict, sign)
    64 
    65         if verify_re is True:
    66             # 商户网站唯一订单号
    67             order_sn = processed_dict.get('out_trade_no', None)
    68             # 支付宝系统交易流水号
    69             trade_no = processed_dict.get('trade_no', None)
    70             # 交易状态
    71             trade_status = processed_dict.get('trade_status', None)
    72 
    73             # 查询数据库中订单记录
    74             existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
    75             for existed_order in existed_orders:
    76                 # 订单商品项
    77                 order_goods = existed_order.goods.all()
    78                 # 商品销量增加订单中数值
    79                 for order_good in order_goods:
    80                     goods = order_good.goods
    81                     goods.sold_num += order_good.goods_num
    82                     goods.save()
    83 
    84                 # 更新订单状态
    85                 existed_order.pay_status = trade_status
    86                 existed_order.trade_no = trade_no
    87                 existed_order.pay_time = datetime.now()
    88                 existed_order.save()
    89             # 需要返回一个'success'给支付宝,如果不返回,支付宝会一直发送订单支付成功的消息
    90             return Response("success")

      创建订单的时候生成一个支付的url,这个逻辑OderSerializer和OrderDetailSerializer中都添加:

     1 class OrderDetailSerializer(serializers.ModelSerializer):
     2     # goods字段需要嵌套一个OrderGoodsSerializer
     3     goods = OrderGoodsSerializer(many=True)
     4     # 支付订单的url
     5     alipay_url = serializers.SerializerMethodField(read_only=True)
     6 
     7     def get_alipay_url(self, obj):
     8         alipay = AliPay(
     9             appid="2016092000557473",
    10             app_notify_url="http://148.70.2.75:8000/alipay/return/",
    11             app_private_key_path=private_key_path,
    12             alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
    13             debug=True,  # 默认False,
    14             return_url="http://148.70.2.75:8000/alipay/return/"
    15         )
    16 
    17         url = alipay.direct_pay(
    18             subject=obj.order_sn,
    19             out_trade_no=obj.order_sn,
    20             total_amount=obj.order_mount,
    21         )
    22         re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
    23 
    24         return re_url
    25 
    26     class Meta:
    27         model = OrderInfo
    28         fields = "__all__"
    29 
    30 
    31 class OrderSerializer(serializers.ModelSerializer):
    32     user = serializers.HiddenField(
    33         default=serializers.CurrentUserDefault()
    34     )
    35     # 生成订单的时候这些不用post
    36     pay_status = serializers.CharField(read_only=True)
    37     trade_no = serializers.CharField(read_only=True)
    38     order_sn = serializers.CharField(read_only=True)
    39     pay_time = serializers.DateTimeField(read_only=True)
    40     nonce_str = serializers.CharField(read_only=True)
    41     pay_type = serializers.CharField(read_only=True)
    42     # 支付订单的url
    43     alipay_url = serializers.SerializerMethodField(read_only=True)
    44 
    45     def get_alipay_url(self, obj):
    46         alipay = AliPay(
    47             appid="2016092000557473",
    48             app_notify_url="http://148.70.2.75:8000/alipay/return/",
    49             app_private_key_path=private_key_path,
    50             alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
    51             debug=True,  # 默认False,
    52             return_url="http://148.70.2.75:8000/alipay/return/"
    53         )
    54 
    55         url = alipay.direct_pay(
    56             subject=obj.order_sn,
    57             out_trade_no=obj.order_sn,
    58             total_amount=obj.order_mount,
    59         )
    60         re_url = "https://openapi.alipaydev.com/gateway.do?{data}".format(data=url)
    61 
    62         return re_url
    63 
    64     # 生成订单号
    65     def generate_order_sn(self):
    66         # 格式:当前时间+userid+随机数
    67         random_ins = Random()
    68         order_sn = '{time_str}{userid}{ranstr}'.format(time_str=time.strftime("%Y%m%d%H%M%S"),
    69                                                        userid=self.context["request"].user.id,
    70                                                        ranstr=random_ins.randint(10, 99))
    71         return order_sn
    72 
    73     def validate(self, attrs):
    74         # validate中添加order_sn,然后在view中就可以save
    75         attrs['order_sn'] = self.generate_order_sn()
    76         return attrs
    77 
    78     class Meta:
    79         model = OrderInfo
    80         fields = "__all__"

      然后将本地修改的地方一定要上传到服务器上,在服务器上调试代码,将Vue中de的api.js中的host改成线上的地址:

    1 let host = 'http://148.70.2.75:8000';

      然后在pycharm中运行项目,点击订单的接口创建一个订单:

    十三、将Vue的静态文件放到django中

      vue有两种开发模式:

    • build(用来生成静态文件)
    • dev(开发模式)

      在前端Vue项目目录下,运行:cnpm run build

      运行之后,会生成项目的静态文件:

      把index.html文件拷贝到项目templates目录下,在项目根目录下新建static文件,将下面的文件拷贝过来:

      在settings中配置静态文件的路径:

    1 STATIC_URL = '/static/'
    2 STATICFILES_DIRS = (
    3     os.path.join(BASE_DIR, "static"),
    4 )

      修改index.html中静态文件路径:

      配置index的url:

    1 path('index/', TemplateView.as_view(template_name='index.html'), name='index')  # 首页

      在trade/views.py中配置支付成功return的地址:

     1 class AlipayView(APIView):
     2     """支付宝接口"""
     3 
     4     # 处理支付宝的return_url返回
     5     def get(self, request):
     6         processed_dict = {}
     7 
     8         # 获取GET中的参数
     9         for key, value in request.GET.items():
    10             processed_dict[key] = value
    11 
    12         # 从processed_dict中取出sign
    13         sign = processed_dict.pop("sign", None)
    14 
    15         # 生成AliPay对象
    16         alipay = AliPay(
    17             appid="2016092000557473",
    18             app_notify_url="http://148.70.2.75:8000/alipay/return/",
    19             app_private_key_path=private_key_path,
    20             alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
    21             debug=True,  # 默认False,
    22             return_url="http://148.70.2.75:8000/alipay/return/"
    23         )
    24 
    25         # 验证签名
    26         verify_re = alipay.verify(processed_dict, sign)
    27 
    28         # 这里可以不做操作。因为不管发不发return url。notify url都会修改订单状态。
    29         if verify_re is True:
    30             order_sn = processed_dict.get('out_trade_no', None)
    31             trade_no = processed_dict.get('trade_no', None)
    32             trade_status = processed_dict.get('trade_status', None)
    33 
    34             existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
    35             for existed_order in existed_orders:
    36                 existed_order.pay_status = trade_status
    37                 existed_order.trade_no = trade_no
    38                 existed_order.pay_time = datetime.now()
    39                 existed_order.save()
    40 
    41             # 支付完成跳转到首页
    42             response = redirect("index")
    43             response.set_cookie("nextPath", "pay", max_age=2)
    44             return response
    45         else:
    46             response = redirect("index")
    47             return response
    48 
    49     # 处理支付宝的notify_url
    50     def post(self, request):
    51         processed_dict = {}
    52 
    53         # 取出post里面的数据
    54         for key, value in request.POST.items():
    55             processed_dict[key] = value
    56 
    57         # 去掉sign
    58         sign = processed_dict.pop("sign", None)
    59 
    60         # 生成一个Alipay对象
    61         alipay = AliPay(
    62             appid="2016092000557473",
    63             app_notify_url="http://148.70.2.75:8000/alipay/return/",
    64             app_private_key_path=private_key_path,
    65             alipay_public_key_path=ali_pub_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
    66             debug=True,  # 默认False,
    67             return_url="http://148.70.2.75:8000/alipay/return/"
    68         )
    69 
    70         # 进行验证
    71         verify_re = alipay.verify(processed_dict, sign)
    72 
    73         if verify_re is True:
    74             # 商户网站唯一订单号
    75             order_sn = processed_dict.get('out_trade_no', None)
    76             # 支付宝系统交易流水号
    77             trade_no = processed_dict.get('trade_no', None)
    78             # 交易状态
    79             trade_status = processed_dict.get('trade_status', None)
    80 
    81             # 查询数据库中订单记录
    82             existed_orders = OrderInfo.objects.filter(order_sn=order_sn)
    83             for existed_order in existed_orders:
    84                 # 订单商品项
    85                 order_goods = existed_order.goods.all()
    86                 # 商品销量增加订单中数值
    87                 for order_good in order_goods:
    88                     goods = order_good.goods
    89                     goods.sold_num += order_good.goods_num
    90                     goods.save()
    91 
    92                 # 更新订单状态
    93                 existed_order.pay_status = trade_status
    94                 existed_order.trade_no = trade_no
    95                 existed_order.pay_time = datetime.now()
    96                 existed_order.save()
    97             # 需要返回一个'success'给支付宝,如果不返回,支付宝会一直发送订单支付成功的消息
    98             return Response("success")

      现在可以通过index直接访问了:http://148.70.2.75:8000/index,然后登陆添加商品到购物车进行结算,跳转到支付宝支付页面,支付成功跳转到首页。

  • 相关阅读:
    1. Redis是属于多线程还是单线程?不同版本之间有什么区别?
    揭开操作系统之内存管理的面纱
    《Cython系列》3. 深入Cython(内含Python解释器相关知识以及源码分析)
    《Cython系列》2. 编译并运行Cython代码
    《Cython系列》1. Cython概要
    python执行lua代码
    lua语言(2):闭包、模式匹配、日期、编译、模块
    100个网络基础知识
    str list tuple dict
    基础算法
  • 原文地址:https://www.cnblogs.com/Sweltering/p/10029851.html
Copyright © 2020-2023  润新知