1. 程式人生 > 程式設計 >django中使用事務及接入支付寶支付功能

django中使用事務及接入支付寶支付功能

之前一直想記錄一下在專案中使用到的事務以及支付寶支付功能,自己一直犯懶沒有完,趁今天有點興致,在這記錄一下。

商城專案必備的就是支付訂單的功能,所以就會涉及到訂單的儲存以及支付介面的引入。先來看看訂單的儲存,在資料庫模型涉及之初,將訂單分成了兩個表,一個為訂單表,記錄訂單的基本資訊,如訂單號,使用者資訊,運費之類,一個為訂單商品表,記錄該訂單中的商品資訊。在儲存訂單時,肯定會涉及到兩個表的新建和儲存,其實還有一張表也需要進行一些修改,那就是商品表,當一個訂單儲存成功,意味著本次交易成功,商品售出,商品的庫存應該進行修改。所以,在儲存訂單這一操作中,涉及到的表有三張。所以在儲存訂單時,多表資料的修改,要嘛同時成功,要嘛同時失敗,這就跟資料庫中的事務很像,因此,在這裡引入事務,來完成訂單儲存的功能。

在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中使用事務及接入支付寶支付功能,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回覆大家的!