您好,登錄后才能下訂單哦!
這篇文章主要介紹了django如何實現多種支付、并發訂單處理方法,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
django實現多種支付方式
''' #思路 我們希望,通過插拔的方式來實現多方式登錄,比如新增一種支付方式,那么只要在項目中新增一個py文件,導入里面的pay方法就可以了,這樣在支付業務中支付語句是不發生變化的。 所以就可以使用python的鴨子類型及面向對象的反射方法來實現功能 ''' ##新建一個Pay文件夾,里面放支付方式.py文件 #Alipay.py class Alipay: def pay(self): pass #Visapay.py class Visapay: def pay(self): pass #Wxpay.py(完全按照接口文檔來得) import time #記得導入商戶號和key哦! from app01.wx import settings class Wxpay: def pay(self,order_data): self.order_id = order_data["order_id"] self.open_id = order_data['open_id'] self.ip = order_data['ip'] data_body = self.get_body_data() import requests url = "https://api.mch.weixin.qq.com/pay/unifiedorder" response = requests.post(url, data_body.encode("utf-8"), headers={'content-type': "application/xml"}) res_dict = self.xml_to_dic(response.content) timeStamp = str(int(time.time())) paySign = self.get_pay_sign(res_dict, timeStamp) data_dic = { 'timeStamp': timeStamp, 'nonceStr': res_dict['nonce_str'], 'package': f"prepay_id={res_dict['prepay_id']}", 'signType': 'MD5', "paySign": paySign, } return data_dic def get_pay_sign(self, res_dict, timeStamp): print("res_dict", res_dict) data_dic = { 'appId': res_dict['appid'], 'timeStamp': timeStamp, 'nonceStr': res_dict['nonce_str'], 'package': f"prepay_id={res_dict['prepay_id']}", "signType": "MD5" } sign_str = "&".join([f"{k}={data_dic[k]}" for k in sorted(data_dic)]) sign_str = f"{sign_str}&key={settings.pay_apikey}" import hashlib md5 = hashlib.md5() md5.update(sign_str.encode("utf-8")) sign = md5.hexdigest() return sign.upper() def xml_to_dic(self, xml_data): import xml.etree.ElementTree as ET ''' xml to dict :param xml_data: :return: ''' xml_dict = {} root = ET.fromstring(xml_data) for child in root: xml_dict[child.tag] = child.text return xml_dict def get_random(self): import random data = "123456789zxcvbnmasdfghjklqwertyuiopZXCVBNMASDFGHJKLQWERTYUIOP" nonce_str = "".join(random.sample(data, 30)) return nonce_str def get_sign(self): data_dic = { "nonce_str": self.nonce_str, "out_trade_no": self.out_trade_no, "spbill_create_ip": self.spbill_create_ip, "notify_url": self.notify_url, "openid": self.open_id, "body": self.body, "trade_type": "JSAPI", "appid": self.appid, "total_fee": "1", "mch_id": self.mch_id } sign_str = "&".join([f"{k}={data_dic[k]}" for k in sorted(data_dic)]) sign_str = f"{sign_str}&key={settings.pay_apikey}" import hashlib md5 = hashlib.md5() md5.update(sign_str.encode("utf-8")) sign = md5.hexdigest() return sign.upper() def get_body_data(self): self.appid = settings.AppId # openid=self.open_id self.mch_id = str(settings.pay_mchid) self.nonce_str = self.get_random() self.out_trade_no = self.order_id self.spbill_create_ip = self.ip self.notify_url = "https://www.test.com" self.body = "老男孩學費" self.sign = self.get_sign() body_data = f""" <xml> <appid>{self.appid}</appid> <mch_id>{self.mch_id}</mch_id> <nonce_str>{self.nonce_str}</nonce_str> <sign>{self.sign}</sign> <body>{self.body}</body> <out_trade_no>{self.out_trade_no}</out_trade_no> <total_fee>1</total_fee> <spbill_create_ip>{ self.spbill_create_ip}</spbill_create_ip> <notify_url>{self.notify_url}</notify_url> <openid>{self.open_id}</openid> <trade_type>JSAPI</trade_type> </xml>""" return body_data ##調用支付方法的語句(一般支付都是發生在訂單創建好之后) import importlib from rest_framework.response import Response pay_method = "Wxpay" #這里是舉例子,所以把pay_method寫死了,正常情況下,應該是外面傳來的支付方式,然后用pay_method接收 try: #用字符串導入支付方式的py文件,例如這里的app01.Pay.{pay_method} pay_field = importlib.import_module(f"app01.Pay.{pay_method}") #用反射拿到該文件下面的類 pay_method_class = getattr(pay_field, pay_method) except: return Response({"code": 205, "msg": "錯誤的支付方式"}) order_data['ip'] = host_ip order_data['open_id'] = open_id #完成支付,并把支付數據返回 pay_data = pay_method_class().pay(order_data) ''' 這里直接用反射拿到的支付類,然后使用它的pay方法,完成支付 ''' return Response({"code": 200, "msg": "ok", "data": pay_data})
django實現訂單創建及支付
''' 幾個注意點: 1.a,b兩個用戶同時買一個庫存為1的商品,這樣為了保證數據安全(即a買了,庫存沒更新,b又買了,這樣就存在安全問題),需要在數據庫操作時加鎖,有兩個選擇:悲觀鎖和樂觀鎖。 悲觀鎖:沖突比較多的時候,使用悲觀鎖。悲觀鎖獲取數據時對數據行了鎖定,其他事務要想獲取鎖,必須等原事務結束。 樂觀鎖:沖突比較少的時候,使用樂觀鎖。查詢時不鎖數據,提交更改時進行判斷.使用樂觀鎖前,要先 設置mysql事務的隔離級別transaction-isolation = READ-COMMITTED 2.a用戶的訂單有兩種商品,這樣提交訂單后,假如第一種成功,第二種失敗,很顯然在訂單一個函數里面寫一個邏輯是行不通的,應該要么同時成功,要么同時失敗。于是自然而然就用到了事務。 ''' #urls.py from django.urls import path from app01.view import order urlpattern = [ path('order/create', order.Create.as_view()), ] #common文件夾下的func.py文件 import time,random def get_order_id(): str_all="1242356796734534" return time.strftime("%Y%m%d%H%M%S")+"".join(random.sample(str_all,5)) #view文件夾下的order.py文件 from rest_framework.views import APIView from rest_framework.response import Response from django.db import transaction from django import forms from django.core.cache import cache from common import func class OrderForm(): phone = forms.CharField( error_message = { 'required': "手機號不能為空" }, # 調用Form組件中的驗證器來校驗手機號 # validators=[RegexValidator(r'1[1-9][0-9]{9}', '手機號格式不正確')], ) token = forms.CharField( error_messages={ "required": "token不能為空" }) province=forms.CharField( error_messages={ "required": "省份不能為空" }) city = forms.CharField(error_messages={ "required": "城市不能為空" }) county = forms.CharField(error_messages={ "required": "縣/區不能為空" }) address = forms.CharField(error_messages={ "required": "詳細地址不能為空" }) name = forms.CharField(error_messages={ "required": "姓名不能為空" }) class Create(APIView): @transaction.atomic def post(self, requset): param = request.data #form表單檢驗訂單數據是否符合規范 for_obj = OrderForm(param) #校驗成功,并且買東西了 if for_obj.is_valid() and param.get('buy_list'): buy_list = param.get("buy_list") #固定方法拿到付款用戶的id if request.META.get("HTTP_X_FORWARDED_FOR"): host_ip = request.META["HTTP_X_FROWARDED_FOR"] else: host_ip = request.META["REMOTE_ADDR"] #校驗token,保證登入狀態 cache_data = cache.get(param["token"]) if not cache_data: return Response({"code": 202, "msg": "錯誤的token"}) openid = cache_data.split("&")[0] #通過openid查找用戶 user_data = models.Wxuser.objects.filter(openid=openid).first() #組織部分總訂單數據 order_data = {"consignee_mobile": param['phone'], 'consignee_name': param['name'], 'wxuser_id': user_data.id, "memo": param['remark'], "consignee_area": f"{param['province']},{param['city']},{param['county']}", "consignee_address": param['address'], } order_data['order_id']=func.get_order_id() order_data["order_total"]=0 #獲取用戶購買商品的id all_product_id=list(buy_list.keys()) #通過id來獲取商品的信息 product_data=models.Product.objects.filter(product_id__in=all_product_id).all() #開啟事務 sid = transaction.savepoint() for product in product_data: product.product_id=str(product.product_id) # num=buy_list[product.id] #獲取商品的庫存 for i in range(3): product_stock = product.stock.quantity new_product_stock = product_stock-buy_list[product.product_id] if new_product_stock<0: transaction.savepoint_rollback(sid) return Response({"code": 204, "msg": f"{product.name}庫存不足"}) #樂觀鎖 res=models.Stock.objects.filter(quantity=product_stock,stock_id=product.stock.stock_id).update(quantity=new_product_stock) if not res : #如果兩次都不成功,就讓用戶重新下單 if i==2: transaction.savepoint_rollback(sid) return Response({"code": 205, "msg": "下單失敗從新下單"}) else: continue else: break #組織子訂單數據 order_item_data = {'order_id': order_data['order_id'], 'product_id': product.product_id,"name": product.name, "image": product.image, "price": product.price, "nums": buy_list[product.product_id], "brief": product.brief} #創建數據 models.Order_items.objects.create(**order_item_data) #獲取訂單總金額 order_data["order_total"] += buy_list[product.product_id]*product.price #創建總訂單 models.Order.objects.create(**order_data) transaction.savepoint_commit(sid) #提交延時任務,判斷訂單再指定時間內,有沒有支付,如果沒有支付,回滾庫存,取消訂單 func.check_order(order_data['order_id']) #如果pay_methon是外面傳的 pay_methon= "Wxpay" try: #導入app01.Pay.{pay_methon} pay_filed=importlib.import_module(f"app01.Pay.{pay_methon}") #用反射獲取,這個文件中的類 pay_methon_class=getattr(pay_filed,pay_methon) except: return Response({"code": 205, "msg": "錯誤的支付方式"}) order_data['ip'] = host_ip order_data['open_id'] = openid #獲取小程序所需的支付數據 pay_data= pay_methon_class().pay(order_data) # pay_methon ="Alipay" # if pay_methon=="Wxpay": # Wxpay().pay # elif:... # pay_methon return Response({"code": 200, "msg": "ok","data":pay_data}) else: return Response({"code": 203, "msg": "缺少參數"}) class Notify(APIView): def post(self,request,paymethon): pay_filed = importlib.import_module(f"app01.Pay.{paymethon}") pay_methon_class = getattr(pay_filed, paymethon) data=pay_methon_class().notify(request.data) if data['stauts']=="suc": models.Order.objects.filter(order_id=data['order_id']).update(pay_status="")
celery實現庫存回滾
#proj_celery文件夾下的celery.py文件 import celery import time # broker='redis://127.0.0.1:6379/2' 不加密碼 backend = 'redis://127.0.0.1:6379/1' broker = 'redis://127.0.0.1:6379/2' cel = celery.Celery('test', backend=backend, broker=broker) import os, sys import django BASE_DIR = os.path.dirname(os.path.dirname(__file__)) # 定位到你的django根目錄 # sys.path.append(os.path.join(BASE_DIR, "app01")) sys.path.append(os.path.abspath(BASE_DIR)) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "shop.settings") django.setup() from django.db import transaction @cel.task @transaction.atomic def del_order(order_id): from app01 import models # 查看訂單數據 order_data = models.Order.objects.filter(order_id=order_id, pay_status=False).first() # 如果有數據表示沒有支付,要進行庫存回滾,和取消訂單 if order_data: # 獲取該訂單下的所有子訂單 order_items = models.Order_items.objects.filter(order_id=order_id).all() # 將子訂單中的數據轉變成 {商品id:購買數量,。。。}的格式 product_all_dic = {item.product_id: item.nums for item in order_items} # 獲取所有商品的id,成為list格式 product_all_id = list(product_all_dic.keys()) # 獲取所有的商品 all_product = models.Product.objects.filter(product_id__in=product_all_id).all() sid = transaction.savepoint() # 把對應的商品進行庫存回滾 for product in all_product: for i in range(3): stock = product.stock.quantity new_stock = stock + product_all_dic[product.product_id] #樂觀鎖 res = models.Stock.objects.filter(stock_id=product.stock.stock_id, quantity=stock).update( quantity=new_stock) if not res: if i == 2: transaction.savepoint_rollback(sid) # 如果這個執行失敗了,那我們要從新提交任務,不然庫存無法回滾 from app01.common import func func.check_order(order_id, 1) return else: continue else: break # 修改訂單狀態 res1 = models.Order.objects.filter(order_id=order_id, pay_status=False).update(status="dead") if res1: transaction.savepoint_commit(sid) else: transaction.savepoint_rollback(sid)
感謝你能夠認真閱讀完這篇文章,希望小編分享的“django如何實現多種支付、并發訂單處理方法”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。