zoukankan      html  css  js  c++  java
  • (项目)生鲜超市(十)

    十一、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,然后登陆添加商品到购物车进行结算,跳转到支付宝支付页面,支付成功跳转到首页。

  • 相关阅读:
    day 66 crm(3) 自创组件stark界面展示数据
    day 65 crm(2) admin源码解析,以及简单的仿造admin组件
    用 Python+nginx+django 打造在线家庭影院
    django -admin 源码解析
    day 64 crm项目(1) admin组件的初识别以及应用
    云链接 接口不允许 情况 解决方法 mysql Host is not allowed to connect to this MySQL server解决方法
    day 56 linux的安装python3 ,虚拟环境,mysql ,redis
    day55 linux 基础以及系统优化
    Codeforces 989 P循环节01构造 ABCD连通块构造 思维对云遮月参考系坐标轴转换
    Codeforces 990 调和级数路灯贪心暴力 DFS生成树两子树差调水 GCD树连通块暴力
  • 原文地址:https://www.cnblogs.com/Sweltering/p/10029851.html
Copyright © 2011-2022 走看看