第三方登陸和支付,都須要有線上服務器才行(須要回調url),咱們能夠用pycharm去遠程調試服務器代碼。html
首先須要一臺雲服務器,我用的是騰訊雲的服務器,pycharm遠程鏈接服務器及解釋器的方法這裏不細講,若是有不懂的童靴能夠私聊我,我會發視頻給你。前端
訂單結算是經過支付寶進行支付的,這裏測試使用螞蟻金服支付寶的沙箱環境測試支付流程,沙箱環境的配置也不細講,若有須要請聯繫我發視頻。vue
將解釋器的環境暫時改爲本地進行調試,而後在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×tamp=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,會跳轉到支付寶支付接口,將支付寶提供給你的沙箱環境中的買家帳戶進行付款測試:npm
如今由django去集成支付寶的notify_url和return_url,首先配置支付寶接口的url:django
1 path('alipay/return/', AlipayView.as_view()), # 支付寶接口
在alipay.py中,將return_url和notify_url都改爲遠程服務器的地址:json
1 app_notify_url="http://148.70.2.75:8000/alipay/return/" 2 return_url="http://148.70.2.75:8000/alipay/return/"
在settings中配置公鑰私鑰路徑:api
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中都添加:cookie
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有兩種開發模式:
在前端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,而後登錄添加商品到購物車進行結算,跳轉到支付寶支付頁面,支付成功跳轉到首頁。