支付宝支付功能
-
阅读支付宝开放平台的电脑网站支付文档
- 支付宝服务器同步回调一次结果给前端
- 支付宝服务器最多异步回调8次结果给后端, 如果后端返回success, 则支付宝服务器结束异步回调
-
在github上搜索alipay, 选择星最多的sdk, 然后安装: pip install python-alipay-sdk --upgrade
-
下载支付宝官方提供的一键生成 RSA 密钥工具, 生成应用公钥和应用私钥, 将应用公钥添加到支付宝开放平台, 然后获取支付宝公钥
-
二次封装网页支付sdk
''' # ...luffyapiluffyapilibsalipayweb_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, ) # ...luffyapiluffyapilibsalipay\__init__.py from .web_pay import alipay from .settings import GATEWAY as alipay_gateway # 支付宝网关接口 '''
创建订单并生成支付链接的接口
'''
# ...luffyapiluffyapiappsorderviews.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)
# ...luffyapiluffyapiappsorderorder_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
'''
后端支付宝异步回调接口
'''
# ...luffyapiluffyapiappsorderviews.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(...)
-
码云上配置的公钥私钥与电脑进行绑定, 支付宝开放平台配置的公钥私钥与项目应用进行绑定