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')
写完了记得测试哦