支付宝支付功能, 创建订单并生成支付链接的接口, 后端支付宝异步回调接口

支付宝支付功能

  1. 阅读支付宝开放平台的电脑网站支付文档

    1. 支付宝服务器同步回调一次结果给前端
    2. 支付宝服务器最多异步回调8次结果给后端, 如果后端返回success, 则支付宝服务器结束异步回调
  2. 在github上搜索alipay, 选择星最多的sdk, 然后安装: pip install python-alipay-sdk --upgrade

  3. 下载支付宝官方提供的一键生成 RSA 密钥工具, 生成应用公钥和应用私钥, 将应用公钥添加到支付宝开放平台, 然后获取支付宝公钥

  4. 二次封装网页支付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')
'''

其他需要注意的点

  1. drf-jwt的JWT_AUTH配置需要写在drf的REST_FRAMEWORK配置之前

  2. 前端携带token

    '''
                    this.$axios({
                        ...,
                        headers: {
                            authorization: `jwt ${token}`,
                        }
                    }).then(response => {
                        ...;
                    }).catch(error => {
                        ...;
                    })
    '''
    
  3. 前端非同站点页面跳转: window.open(url, '_self') , _self表示跳转时不新开标签页, 前端同站点页面跳转: this.$router.push(url)

  4. 前端使用 location.search 获取url中?以及?后的字符串

  5. 前端字符串裁剪: "cql".substring(1, 2) # q

  6. 前端异常处理语句: try {} catch (e) {}

  7. 前端对url编码数据进行解码: decodeURLComponet(...)

  8. 码云上配置的公钥私钥与电脑进行绑定, 支付宝开放平台配置的公钥私钥与项目应用进行绑定

原文地址:https://www.cnblogs.com/-406454833/p/12709354.html