(项目)生鲜超市(十)

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

posted @ 2018-11-28 18:36  Sweltering  阅读(443)  评论(0编辑  收藏  举报