您好,登錄后才能下訂單哦!
之前一直想記錄一下在項目中使用到的事務以及支付寶支付功能,自己一直犯懶沒有完,趁今天有點興致,在這記錄一下。
商城項目必備的就是支付訂單的功能,所以就會涉及到訂單的保存以及支付接口的引入。先來看看訂單的保存,在數據庫模型涉及之初,將訂單分成了兩個表,一個為訂單表,記錄訂單的基本信息,如訂單號,用戶信息,運費之類,一個為訂單商品表,記錄該訂單中的商品信息。在保存訂單時,肯定會涉及到兩個表的新建和保存,其實還有一張表也需要進行一些修改,那就是商品表,當一個訂單保存成功,意味著本次交易成功,商品售出,商品的庫存應該進行修改。所以,在保存訂單這一操作中,涉及到的表有三張。所以在保存訂單時,多表數據的修改,要嘛同時成功,要嘛同時失敗,這就跟數據庫中的事務很像,因此,在這里引入事務,來完成訂單保存的功能。
在Django中可以通過 django.db.transaction
模塊提供的 atomic 來定義一個事務, atomic 提供兩種用法,一種是裝飾器,一種是with語句。
from django.db import transaction @transaction.atomic def viewfunc(request): # 這些代碼會在一個事務中執行 ... from django.db import transaction def viewfunc(request): # 這部分代碼不在事務中,會被Django自動提交 ... with transaction.atomic(): # 這部分代碼會在事務中執行 ...
在Django中,還提供了保存點的支持,可以在事務中創建保存點來記錄數據的特定狀態,數據庫出現錯誤時,可以恢復到數據保存點的狀態
from django.db import transaction # 創建保存點 save_id = transaction.savepoint() # 回滾到保存點 transaction.savepoint_rollback(save_id) # 提交從保存點到當前狀態的所有數據庫事務操作 transaction.savepoint_commit(save_id)
所以,可以在序列化器的create方法中,創建一個事務,還進行數據的修改保存還有新建,若有地方出錯,則直接回滾,若沒有問題則提交事務。代碼如下
def create(self, validated_data): """ 保存訂單 """ # 獲取當前下單用戶 user = self.context['request'].user # 組織訂單編號 20170903153611+user.id # timezone.now() -> datetime order_id = timezone.now().strftime('%Y%m%d%H%M%S') + ('%09d' % user.id) address = validated_data['address'] pay_method = validated_data['pay_method'] # 生成訂單 with transaction.atomic(): # 創建一個保存點 save_id = transaction.savepoint() try: # 創建訂單信息 order = OrderInfo.objects.create( order_id=order_id, user=user, address=address, total_count=0, total_amount=Decimal(0), freight=Decimal(10), pay_method=pay_method, status=OrderInfo.ORDER_STATUS_ENUM['UNSEND'] if pay_method == OrderInfo.PAY_METHODS_ENUM['CASH'] else OrderInfo.ORDER_STATUS_ENUM['UNPAID'] ) # 獲取購物車信息 redis_conn = get_redis_connection("cart") redis_cart = redis_conn.hgetall("cart_%s" % user.id) cart_selected = redis_conn.smembers('cart_selected_%s' % user.id) # 將bytes類型轉換為int類型 cart = {} for sku_id in cart_selected: cart[int(sku_id)] = int(redis_cart[sku_id]) # # 一次查詢出所有商品數據 # skus = SKU.objects.filter(id__in=cart.keys()) # 處理訂單商品 sku_id_list = cart.keys() for sku_id in sku_id_list: while True: sku = SKU.objects.get(id=sku_id) sku_count = cart[sku.id] # 判斷庫存 origin_stock = sku.stock # 原始庫存 origin_sales = sku.sales # 原始銷量 if sku_count > origin_stock: transaction.savepoint_rollback(save_id) raise serializers.ValidationError('商品庫存不足') # 用于演示并發下單 # import time # time.sleep(5) # 減少庫存 # sku.stock -= sku_count # sku.sales += sku_count # sku.save() new_stock = origin_stock - sku_count new_sales = origin_sales + sku_count # 根據原始庫存條件更新,返回更新的條目數,樂觀鎖 ret = SKU.objects.filter(id=sku.id, stock=origin_stock).update(stock=new_stock, sales=new_sales) if ret == 0: continue # 累計商品的SPU 銷量信息 sku.goods.sales += sku_count sku.goods.save() # 累計訂單基本信息的數據 order.total_count += sku_count # 累計總金額 order.total_amount += (sku.price * sku_count) # 累計總額 # 保存訂單商品 OrderGoods.objects.create( order=order, sku=sku, count=sku_count, price=sku.price, ) # 更新成功 break # 更新訂單的金額數量信息 order.total_amount += order.freight order.save() except serializers.ValidationError: raise except Exception as e: logger.error(e) transaction.savepoint_rollback(save_id) raise # 提交事務 transaction.savepoint_commit(save_id) # 更新redis中保存的購物車數據 pl = redis_conn.pipeline() pl.hdel('cart_%s' % user.id, *cart_selected) pl.srem('cart_selected_%s' % user.id, *cart_selected) pl.execute() return order
還有一點需要注意的是,當訂單提交,購物車中相應的商品應該進行刪除。好了,以上就是django中的事務。
再來說說支付寶支付功能的引入,現在基本上所有的項目涉及到支付功能時都會引入第三方支付,其中使用最廣泛的應該就是支付寶和微信了,這里我使用的是支付寶支付。當訂單創建完成,接下來就是支付了。
先來縷一下流程,用戶點擊按鈕請求支付寶支付界面,先進行登錄,登錄成功后進行支付操作,支付成功會進行回調。
首先第一步,用戶點擊按鈕,后端會進行url的拼接,將拼接好的url返給前端,前端進行跳轉,跳轉到支付寶相關界面,用戶進行登錄和支付等操作。
查看pythonsdk,首先我們可以通過openssl命令生成一個密鑰(公鑰和私鑰),私鑰自己留存,公鑰用戶校驗。命令如下:
openssl
OpenSSL> genrsa -out app_private_key.pem 2048 # 私鑰 OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # 導出公鑰 OpenSSL> exit
同時,你要從支付寶獲得一個公鑰字符串,格式可參考:https://github.com/fzlee/alipay/blob/master/tests/certs/ali/ali_public_key.pem
由于上面我們用的是RSA生成的密鑰,所以在支付寶中我們也需要RSA的公鑰
設置好了密鑰,我們就可以開始寫視圖,代碼如下:
class PaymentView(APIView): """ 支付 """ permission_classes = (IsAuthenticated,) def get(self, request, order_id): """ 獲取支付鏈接 """ # 判斷訂單信息是否正確 try: order = OrderInfo.objects.get(order_id=order_id, user=request.user, pay_method=OrderInfo.PAY_METHODS_ENUM["ALIPAY"], status=OrderInfo.ORDER_STATUS_ENUM["UNPAID"]) except OrderInfo.DoesNotExist: return Response({'message': '訂單信息有誤'}, status=status.HTTP_400_BAD_REQUEST) # 構造支付寶支付鏈接地址 alipay = AliPay( appid=settings.ALIPAY_APPID, app_notify_url=None, # 默認回調url app_private_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/app_private_key.pem"), alipay_public_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "keys/alipay_public_key.pem"), # 支付寶的公鑰,驗證支付寶回傳消息使用,不是你自己的公鑰, sign_type="RSA2", # RSA 或者 RSA2 debug=settings.ALIPAY_DEBUG # 默認False ) order_string = alipay.api_alipay_trade_page_pay( out_trade_no=order_id, total_amount=str(order.total_amount), subject="美多商城%s" % order_id, return_url="http://www.meiduo.site:8080/pay_success.html", ) # 需要跳轉到https://openapi.alipay.com/gateway.do? + order_string # 拼接鏈接返回前端 alipay_url = settings.ALIPAY_URL + "?" + order_string return Response({'alipay_url': alipay_url})
相關的參數可以提前在配置文件中配置好(ALPAY_APPID,ALPAY_URL,ALPAY_DEBUG)注意ALPAY為True時才啟用沙箱環境。當用戶支付成功,會對你填寫的回調網址進行回調,返回的參數如下圖。
前端頁面將此數據發送給后端,后端檢驗并保存支付結果。以上就是全部過程。具體的過程可以參考pythonsdk。
總結
以上所述是小編給大家介紹的django中使用事務及接入支付寶支付功能,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回復大家的!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。