【25.0】路飞支付相关

发布时间 2023-08-19 17:25:38作者: Chimengmeng

【一】订单相关表设计

  • Order 订单表
  • OrderDetail 订单详情表
from django.db import models
from luffyCity.apps.course.models import Course
from luffyCity.apps.user.models import User


# Order  订单表
class Order(models.Model):
    """订单模型"""
    status_choices = (
        (0, '未支付'),
        (1, '已支付'),
        (2, '已取消'),
        (3, '超时取消'),
    )
    pay_choices = (
        (1, '支付宝'),
        (2, '微信支付'),
    )
    subject = models.CharField(max_length=150, verbose_name="订单标题")
    total_amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="订单总价", default=0)
    out_trade_no = models.CharField(max_length=64, verbose_name="订单号", unique=True)  # 唯一,不能重复,后期可以根据订单号修改订单状态
    trade_no = models.CharField(max_length=64, null=True, verbose_name="交易流水号")  # 支付宝支付,生成流水号,存起来
    order_status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="订单状态")
    pay_type = models.SmallIntegerField(choices=pay_choices, default=1, verbose_name="支付方式")
    pay_time = models.DateTimeField(null=True, verbose_name="支付时间")  # 支付宝返回的时间
    user = models.ForeignKey(User, related_name='order_user', on_delete=models.DO_NOTHING, db_constraint=False,
                             verbose_name="下单用户")
    created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')

    class Meta:
        db_table = "luffy_order"
        verbose_name = "订单记录"
        verbose_name_plural = "订单记录"

    def __str__(self):
        return "%s - ¥%s" % (self.subject, self.total_amount)


# OrderDetail  订单详情表
class OrderDetail(models.Model):
    """订单详情"""
    order = models.ForeignKey(Order, related_name='order_courses', on_delete=models.CASCADE, db_constraint=False,
                              verbose_name="订单")
    course = models.ForeignKey(Course, related_name='course_orders', on_delete=models.CASCADE, db_constraint=False,
                               verbose_name="课程")
    price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程原价")
    real_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="课程实价")

    class Meta:
        db_table = "luffy_order_detail"
        verbose_name = "订单详情"
        verbose_name_plural = "订单详情"

    def __str__(self):
        try:
            return "%s的订单:%s" % (self.course.name, self.order.out_trade_no)
        except:
            return super().__str__()
  • 数据库迁移
python ../../manage.py makemigrations
python ../../manage.py migrate

【二】下单接口

【1】分析

(1)前端

  • 前端传入数据

    • 在订单表中插入记录,生成支付链接,返回给前端
  • 前端传入数据

{'subject':'某个课程', 'total_amount':100, 'pay_type':1, 'courses':[1,2,3]}

(2)后端

  • 后端做的事情--要保存,要校验

    • 序列化类来做
  • 1)订单总价校验

  • 2)生成订单号:唯一的

  • 3)获取支付用户:request.user

  • 4)支付链接生成:支付宝支付链接

  • 5)入库(两个表)的信息准备

【2】代码实现

(1)视图类

  • luffyCity\luffyCity\apps\orders\views.py
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from luffyCity.apps.orders.serializers.OrderSerializers import PaySerializer
from luffyCity.libs.ALiPaySDK import ALiPay
from luffyCity.utils.common_response import CommonResponse
from rest_framework.viewsets import GenericViewSet
from rest_framework.permissions import IsAuthenticated


# Create your views here.

# 测试封装包
class ALiPayView(APIView):
    def get(self, request, *args, **kwargs):
        pay = ALiPay()
        order_id = "201102102102102150"
        total_amount = 644491.03
        order_name = '兰博基尼'
        order_url = pay.create_order_url(order_id=order_id, total_amount=total_amount, order_name=order_name)

        return CommonResponse(order_url=order_url)


class PayOrderView(GenericViewSet):
    serializer_class = PaySerializer
    authentication_classes = [JSONWebTokenAuthentication]
    permission_classes = [IsAuthenticated]

    def create(self, request, *args, **kwargs):
        pay_ser = self.get_serializer(context={"request": request}, data=request.data)
        pay_ser.is_valid(raise_exception=True)
        pay_ser.save()
        pay_url = pay_ser.context.get('pay_url')

        return CommonResponse(msg="保存成功", pay_url=pay_url)

(2)路由

  • luffyCity\luffyCity\apps\orders\urls.py
from django.urls import path

from luffyCity.apps.orders.views import ALiPayView, PayOrderView
from rest_framework.routers import SimpleRouter

router = SimpleRouter()
router.register('pay', PayOrderView, 'pay')

urlpatterns = [
    # path('pay/', ALiPayView.as_view()),
]
urlpatterns += router.urls

(3)序列化类

  • luffyCity\luffyCity\apps\orders\serializers\OrderSerializers.py
# -*-coding: Utf-8 -*-
# @File : OrderSerializers .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/8/13
import uuid
from luffyCity.libs.ALiPaySDK import ALiPay
from rest_framework import serializers
from rest_framework.exceptions import APIException
from luffyCity.apps.course.models import Course
from luffyCity.apps.orders.models import Order, OrderDetail


# 校验 -- 保存
class PaySerializer(serializers.ModelSerializer):
    '''
    - 1)订单总价校验
    - 2)生成订单号:唯一的
    - 3)获取支付用户:request.user
    - 4)支付链接生成:支付宝支付链接
    - 5)入库(两个表)的信息准备
    '''
    # 要支持单购物和群购物(购物车),前台要提交 课程主键(们)
    # PrimaryKeyRelatedField:前端传入 [1,2,3] ---帮我们转成---> [id为1的课程对象,id为2的课程对象,id为3的课程对象]
    courses = serializers.PrimaryKeyRelatedField(queryset=Course.objects.all(), write_only=True, many=True)

    class Meta:
        model = Order
        fields = ['subject', 'total_amount', 'pay_type', 'courses']

    def _check_price(self, attrs):
        '''
        (1)取出前端传入的总价格
        (2)计算总价格,核验总价格
        :return:
        '''
        # (1)取出前端传入的总价格
        total_amount = attrs.get('total_amount')
        # (2)计算总价格,核验总价格
        # course 经过上面的字段序列已经转成了对象
        courses = attrs.get('courses')
        real_total_amount = 0
        for course in courses:
            real_total_amount += course.price
        if not real_total_amount == total_amount:
            raise APIException("两次价格不一致")

    def _create_order_id(self):
        return str(uuid.uuid4())

    def _get_user(self):
        request = self.context.get('request')
        return request.user

    def _get_pay_url(self, order_id, subject, total_amount, pay_type):
        # pay_type 如果是多方式付款,这里需要判断一下
        pay = ALiPay()
        pay_url = pay.create_order_url(order_id=order_id, total_amount=float(total_amount), order_name=subject)
        return pay_url

    def validate(self, attrs):
        # 1)订单总价校验
        self._check_price(attrs)
        # 2)生成订单号:唯一的
        order_id = self._create_order_id()
        # 3)获取支付用户:request.user
        user = self._get_user()
        # 4)支付链接生成:支付宝支付链接
        pay_url = self._get_pay_url(order_id, attrs.get('subject'), attrs.get('total_amount'), attrs.get('pay_type'))
        # 5)入库(两个表)的信息准备
        attrs['user'] = user
        attrs['out_trade_no'] = order_id
        self.context['pay_url'] = pay_url
        # 当前 attrs 中数据 {'subject', 'total_amount', 'pay_type', 'courses','user','order_id'}
        return attrs

    def create(self, validated_data):
        # 当前 attrs 中数据 {'subject', 'total_amount', 'pay_type', 'courses','user','order_id'}
        courses = validated_data.pop('courses')
        order = Order.objects.create(**validated_data)
        for course in courses:
            OrderDetail.objects.create(order=order, course=course, price=course.price, real_price=course.price)

        return order

【三】支付宝回调

  • 因为咱们 地址是 127.0.0.1

    • 支付宝回调,只能回调公网地址,调不回我们这,我们收不到支付宝的回调--->
  • 解决方案: