支付寶支付功能
-
閱讀支付寶開放平台的電腦網站支付文檔
- 支付寶服務器同步回調一次結果給前端
- 支付寶服務器最多異步回調8次結果給后端, 如果后端返回success, 則支付寶服務器結束異步回調
-
在github上搜索alipay, 選擇星最多的sdk, 然后安裝: pip install python-alipay-sdk --upgrade
-
下載支付寶官方提供的一鍵生成 RSA 密鑰工具, 生成應用公鑰和應用私鑰, 將應用公鑰添加到支付寶開放平台, 然后獲取支付寶公鑰
-
二次封裝網頁支付sdk
''' # ...\luffyapi\luffyapi\libs\alipay\web_pay.py from alipay import AliPay from .settings import * alipay = AliPay( # 真實appid則debug為False, 沙箱appid則debug為True appid=APP_ID, debug=DEBUG, app_notify_url=None, app_private_key_string=APP_PRIVATE_KEY_STRING, alipay_public_key_string=ALIPAY_PUBLIC_KEY_STRING, sign_type=SIGN, ) # ...\luffyapi\luffyapi\libs\alipay\__init__.py from .web_pay import alipay from .settings import GATEWAY as alipay_gateway # 支付寶網關接口 '''
創建訂單並生成支付鏈接的接口
'''
# ...\luffyapi\luffyapi\apps\order\views.py
...
from rest_framework.generics import CreateAPIView
from rest_framework.permissions import IsAuthenticated
class OrderCreateAPIView(CreateAPIView):
permission_classes = [IsAuthenticated] # 設置登錄后才能購買課程
serializer_class = order_serializers.OrderModelSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data, context={'request': request}) # 將request對象傳入OrderModelSerializer類中
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
return Response(serializer.pay_url)
# ...\luffyapi\luffyapi\apps\order\order_serializers.py
from rest_framework import serializers
from . import models
from ..course.models import Course
class OrderModelSerializer(serializers.ModelSerializer):
courses = serializers.PrimaryKeyRelatedField(required=True, queryset=Course.objects.all(), many=True) # 自定義反序列化字段
class Meta:
model = models.Order
fields = ['subject', 'total_amount', 'pay_type', 'courses']
...
def _check_total_amount(self, attrs):
total_amount = attrs.get('total_amount') # 獲取前端傳過來的訂單總價
# 根據訂單中的課程信息統計出實際的訂單總價
total_amount_temp = 0
courses = attrs.get('courses')
for course in courses:
total_amount_temp += course.price
# 將前端傳過來的訂單總價與實際的訂單總價進行比對
if total_amount != total_amount_temp:
raise serializers.ValidationError({'total_amount': '價格異常'})
return total_amount
def _get_out_trade_no(self):
import time
temp_no = '%.7f' % time.time()
out_trade_no = temp_no.replace('.', '')
return out_trade_no[-13: -1]
# 從傳入的request對象中獲取用戶對象
def _get_request_user(self):
return self.context.get('request').user
def _get_pay_url(self, out_trade_no, total_amount, subject):
from luffyapi.libs.alipay import alipay, alipay_gateway
from django.conf import settings
order_string = alipay.api_alipay_trade_page_pay(
out_trade_no=out_trade_no,
total_amount=str(total_amount),
subject=subject,
return_url=settings.RETURN_URL, # 同步回調的前端接口
notify_url=settings.NOTIFY_URL # 異步回調的后端接口
)
return alipay_gateway + order_string
def validate(self, attrs):
total_amount = self._check_total_amount(attrs) # 校驗訂單總價
out_trade_no = self._get_out_trade_no() # 生成訂單號
user = self._get_request_user() # 獲取下單用戶
pay_url = self._get_pay_url(out_trade_no, total_amount, attrs.get('subject')) # 生成支付鏈接
self.pay_url = pay_url # 將支付鏈接綁定給OrderModelSerializer類的pay_url屬性
# 在訂單表中創建新的訂單記錄時所需要的額外字段數據
attrs['out_trade_no'] = out_trade_no
attrs['user'] = user
return attrs
重寫create方法: 1. 在訂單表中創建新的訂單記錄, 2. 在訂單詳情表中創建新的訂單詳情記錄
def create(self, validated_data):
courses = validated_data.pop('courses') # 將訂單中的課程信息取出額外記錄到訂單詳情表中
order_obj = models.Order.objects.create(**validated_data) # 在訂單表中創建新的訂單記錄
# 在訂單詳情表中創建新訂單詳情記錄
for course in courses:
models.OrderDetail.objects.create(order=order_obj, course=course, price=course.price, real_price=course.price)
return order_obj
'''
后端支付寶異步回調接口
'''
# ...\luffyapi\luffyapi\apps\order\views.py
...
from luffyapi.libs.alipay import alipay
from luffyapi.utils.my_logging import logger
...
class PayResAPIView(APIView):
# 對前端轉發的支付寶同步回調數據作出響應
def get(self, request, *args, **kwargs):
return Response(data='received')
# 處理支付寶異步回調傳過來的數據
def post(self, request, *args, **kwargs):
data = request.data.dict() # QueryDict類的對象沒有pop方法, 可以通過".dict()"轉化為dict類的對象
# 驗簽
sign = data.pop('sign')
result = alipay.verify(data, sign)
out_trade_no = data.get('out_trade_no') # 獲取訂單號
trade_status = data.get("trade_status") # 獲取交易狀態
# 根據驗簽結果和交易狀態修改數據庫中的訂單狀態
if result and trade_status in ("TRADE_SUCCESS", "TRADE_FINISHED"):
models.Order.objects.filter(out_trade_no=out_trade_no).update(order_status=1)
logger.critical('訂單號:%s, 交易狀態: %s' % (out_trade_no, trade_status)) # 項目上線后, 沒有控制台輸出結果, 需要通過日志記錄訂單支付信息
return Response('success')
return Response('failed')
'''
其他需要注意的點
-
drf-jwt的JWT_AUTH配置需要寫在drf的REST_FRAMEWORK配置之前
-
前端攜帶token
''' this.$axios({ ..., headers: { authorization: `jwt ${token}`, } }).then(response => { ...; }).catch(error => { ...; }) '''
-
前端非同站點頁面跳轉:
window.open(url, '_self')
, _self表示跳轉時不新開標簽頁, 前端同站點頁面跳轉:this.$router.push(url)
-
前端使用 location.search 獲取url中?以及?后的字符串
-
前端字符串裁剪:
"cql".substring(1, 2) # q
-
前端異常處理語句:
try {} catch (e) {}
-
前端對url編碼數據進行解碼: decodeURLComponet(...)
-
碼雲上配置的公鑰私鑰與電腦進行綁定, 支付寶開放平台配置的公鑰私鑰與項目應用進行綁定