46 后台 - 支付接口

后台 - 支付接口

# 登录认证()
# 前端传入:商品总价格,登录用户(已经登录了,不需要携带了),courses:[1,2,3]
    {total_amount:99,courses:[1,2,3]}
    
# 后端:
    -存单个Order表,顺带存Orderdetail表----》重写create
    -所有判断逻辑写到序列化类中
        -# 订单金额校验
        -#  生成订单号   ---》uuid
        -#  获取支付人  ----》request对象中
        -#  获取支付链接--->封装好的支付宝支付
        -# 入库(两个表)的信息准备
        -# 入库----》重写create

创建order的app,并注册

模型表:order/models.py(数据库迁移)
"""
class Order(models.Model):
    # 主键、总金额、订单名、订单号、订单状态、创建时间、支付时间、流水号、支付方式、支付人(外键) - 优惠劵(外键,可为空)
    pass

class OrderDetail(models.Model):
    # 订单号(外键)、商品(外键)、实价、成交价 - 商品数量
    pass
"""
from django.db import models
from user.models import User
from course.models import Course


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)

    @property
    def courses(self):
        data_list = []
        for item in self.order_courses.all():
            data_list.append({
                "id": item.id,
                "course_name": item.course.name,
                "real_price": item.real_price,
            })
        return data_list


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__()

视图类order/views.py

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import CreateModelMixin
from .models import Order
from utils.response import APIResponse
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated
from .serializer import PaySerializer

from utils.logging import logger

class OrderView(GenericViewSet, CreateModelMixin):
    authentication_classes = [JSONWebTokenAuthentication, ]
    permission_classes = [IsAuthenticated]
    serializer_class = PaySerializer
    queryset = Order.objects.all()

    def create(self, request, *args, **kwargs):
        try:
            ser = self.get_serializer(data=request.data, context={'request': request})
            ser.is_valid(raise_exception=True)
            ser.save()
            pay_url = ser.context.get('pay_url')
        except Exception as e:
            raise e

        return APIResponse(pay_url=pay_url)

序列化类order/serializer.py

from .models import Order, OrderDetail
from course.models import Course
from rest_framework import serializers
from rest_framework.validators import ValidationError
from libs.apay import pay,gateway
from django.conf import settings
class PaySerializer(serializers.ModelSerializer):
    # courses=serializers.ListField()  # 不用这个
    # 如果这个  courses:1--->courses:id为1的这个课程   many=False
    # 如果 courses:[1,2,3]---->courses:[对象1,对象2,对象3]
    courses = serializers.PrimaryKeyRelatedField(queryset=Course.objects.all(), write_only=True, many=True)

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

    def validate(self, attrs):
        # attrs 中有:total_amount,courses是对象列表
        # 订单金额校验(校验传入的价格跟我们计算的价格是否一致)
        self._check_price(attrs)
        # 生成订单号   ---》uuid
        trade_no = self._get_trade_no()
        # 获取支付人  ----》request对象中
        user=self._get_user()
        # 获取支付链接--->封装好的支付宝支付
        pay_url=self.get_pay_url(attrs,trade_no)
        # 入库(两个表)的信息准备
        attrs['subject']='路飞线上课程'
        attrs['out_trade_no']=trade_no
        attrs['user']=user

        self.context['pay_url']=pay_url


        return attrs


    def _check_price(self, attrs):
        total_amount = attrs.get('total_amount')
        courses = attrs.get('courses')
        real_total_amount = 0
        for course in courses:
            real_total_amount += course.price
        if not total_amount == real_total_amount:
            raise ValidationError('价格不一致')

    def _get_trade_no(self):
        import uuid
        trade_no = str(uuid.uuid4()).replace('-', '')
        return trade_no

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


    def get_pay_url(self,attrs,trade_no):
        '''
        out_trade_no='123456',  # 订单号
        total_amount=float(99.99),  # 只有生成支付宝链接时,不能用Decimal
        subject='精品内衣',
        return_url='http://127.0.0.1:8080/home',
        notify_url='http://127.0.0.1:8080/home',

        '''
        res=pay.api_alipay_trade_page_pay(
            out_trade_no=trade_no,
            total_amount=float(attrs.get('total_amount')),
            subject='路飞线上课程',
            return_url=settings.RETURN_URL, # 放到配置文件 前端回调就是个前端页面,支付宝回调到支付成功页面
            notify_url=settings.NOTIFY_URL, # 后端回调 接收支付宝的post回调,在这里面修改订单状态
        )
        return gateway+res



    def create(self, validated_data):
        # validated_data:  [subject, out_trade_no,  user courses  total_amount]
        courses = validated_data.pop('courses')
        order=Order.objects.create(**validated_data)
        # 存订单详情表
        for course in courses:
            OrderDetail.objects.create(course=course,order=order,price=course.price,real_price=course.price)

        return order

路由

router.register('pay', views.OrderView, 'pay')

 写完了记得测试哦

 

 

 

posted @ 2022-03-03 21:08  甜甜de微笑  阅读(59)  评论(0编辑  收藏  举报