支付和订单

Posted pankypan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了支付和订单相关的知识,希望对你有一定的参考价值。

支付和订单

为了方便开发,和以后项目的维护,我们再次创建子应用orders来完成接下来的订单和订单支付功能。

cd luffy/apps
python ../../manage.py startapp orders

注册子应用,settings/dev.py,代码:

INSTALLED_APPS = [
    # 子应用
    。。。
    
    'orders',
]

订单模型

from django.db import models

# Create your models here.
from luffy.utils.models import BaseModel
from users.models import User
from courses.models import Course
class Order(BaseModel):
    """订单记录"""
    status_choices = (
        (0, '未支付'),
        (1, '已支付'),
        (2, '已取消'),
        (3, '超时取消'),
    )
    pay_choices = (
        (0, '支付宝'),
        (1, '微信支付')
    )
    order_title = models.CharField(max_length=150,verbose_name="订单标题")
    total_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="订单总价", default=0)
    real_price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="实付金额", default=0)
    order_number = models.CharField(max_length=64,verbose_name="订单号")
    order_status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="订单状态")
    pay_type = models.SmallIntegerField(choices=pay_choices, default=0, verbose_name="支付方式")
    use_coupon = models.BooleanField(default=False,verbose_name="是否使用优惠券")
    coupon = models.IntegerField(null=True, verbose_name="用户优惠券ID")
    order_desc = models.TextField(max_length=500,null=True,blank=True, verbose_name="订单描述")
    pay_time = models.DateTimeField(null=True, verbose_name="支付时间")
    user = models.ForeignKey(User, related_name='user_orders', on_delete=models.DO_NOTHING,verbose_name="下单用户")

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

    def __str__(self):
        return "%s,总价: %s,实付: %s" % (self.order_title, self.total_price, self.real_price)

from courses.models import CourseTime
class OrderDetail(BaseModel):
    """订单详情"""
    order = models.ForeignKey(Order, related_name='order_courses', on_delete=models.CASCADE, verbose_name="订单")
    course = models.ForeignKey(Course, related_name='course_orders', on_delete=models.CASCADE, verbose_name="课程")
    expire = models.IntegerField(default='-1', 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="课程实价")
    discount_name = models.CharField(max_length=120,default="",verbose_name="优惠活动类型")
    class Meta:
        db_table="ly_order_detail"
        verbose_name= "订单详情"
        verbose_name_plural= "订单详情"

makemigrations && migrate

python manage.py makemigrations;
python manage.py migrate;

把当前子应用注册到xadmin中

在当前子应用下创建adminx.py,代码:

import xadmin

from .models import Order
class OrderModelAdmin(object):
    """订单模型管理类"""
    pass

xadmin.site.register(Order, OrderModelAdmin)


from .models import OrderDetail
class OrderDetailModelAdmin(object):
    """订单详情模型管理类"""
    pass

xadmin.site.register(OrderDetail, OrderDetailModelAdmin)

后端实现生成订单的api接口

from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from django_redis import get_redis_connection
from rest_framework.response import Response
from .models import Order,OrderDetail
from datetime import datetime
from courses.models import Course, CourseTime
import random
from django.db import transaction
from rest_framework import status

import logging
log = logging.getLogger("django")

class OrderAPIView(APIView):
    permission_classes = [IsAuthenticated]
    def post(self,request):
        """生成订单"""
        # 获取用户ID
        # user_id = 1
        user_id = request.user.id

        # 订单号,必须保证唯一
        order_number =  datetime.now().strftime("%Y%m%d%H%M%S") + "%08d" % user_id + "%04d" % random.randint(0,9999)

        with transaction.atomic():

            # 数据库事务的回滚标记
            save_id = transaction.savepoint()

            # 生成空的订单
            try:
                order = Order.objects.create(
                    order_title="路飞学城课程购买",
                    total_price=0,
                    real_price=0,
                    order_number= order_number,
                    user_id=user_id,
                )

                # 到redis获取购物车信息
                redis = get_redis_connection("cart")
                # 勾选状态
                course_selects_set = redis.smembers("cart_selected_%s" % user_id )
                print( course_selects_set )
                # 购物车中商品课程列表
                cart_course_list = redis.hgetall("cart_%s" % user_id )
                print( cart_course_list )
                # 通过购物车信息到数据中提取相关数据
                # 计算订单总价
                total_price = 0

                # 开启redis的管道操作[事务操作]
                pipeline = redis.pipeline()
                pipeline.multi()

                for course_id_byte,expire_byte in cart_course_list.items():
                    if course_id_byte in course_selects_set:
                        expire = expire_byte.decode()
                        course_id = course_id_byte.decode()
                        course = Course.objects.get(pk=course_id)
                        if expire == '-1':
                            """永久有效"""
                            course_price = course.get_course_price()

                        else:
                            """有购买周期"""
                            coursetime = CourseTime.objects.get(course=course_id,timer=expire)
                            course_price = coursetime.course.get_course_price(coursetime.price)

                        total_price += course_price

                        # 生成订单详情
                        OrderDetail.objects.create(
                            order_id=order.id,
                            course_id=course_id,
                            expire=expire,
                            price=course.price if expire=='-1' else coursetime.price,
                            real_price=course_price,
                            discount_name=course.get_course_discount_type()
                        )

                        # 从购物车中移除已经加入订单的商品
                        pipeline.srem("cart_selected_%s" % user_id, course_id )
                        pipeline.hdel("cart_%s" % user_id, course_id )

                # 提交redis的事务操作
                pipeline.execute()
                # 补充订单的总价格
                order.total_price=total_price
                order.real_price=total_price
                order.save()

            except Exception:
                # 记录错误日志
                log.error( "%s" % Exception )
                # 回滚事务
                transaction.savepoint_rollback(save_id)
                # 响应结果
                return Response("message":"系统异常!",status=status.HTTP_507_INSUFFICIENT_STORAGE)

        # 响应结果
        return Response("message":"成功生成订单!","order":order.order_number)

上面我们使用了redis的事务操作保证数据的一致性。但是mysql里面我们也是在进行多表操作,所以也是需要使用事务来保证数据的一致性的。

事务有四大特性:

原子性(Atomicity)
一致性(Consistency)
隔离性(Isolation)[事务隔离级别->幻读,脏读]
持久性(Durability)

django框架本身就提供了2种事务操作的用法。

django的事务操作方法主要通过 django.db.transation模块完成的。

启用事务用法1:

from django.db import transaction
from rest_framework.views import APIView
class OrderAPIView(APIView):
    @transaction.atomic          # 开启事务,当方法执行完成以后,自动提交事务
    def post(self,request):
        ....

启用事务用法2:

from django.db import transaction
from rest_framework.views import APIView
class OrderAPIView(APIView):
    def post(self,request):
        ....
        with transation.atomic(): # 开启事务,当with语句执行完成以后,自动提交事务
            # 数据库操作

在使用事务过程中, 有时候会出现异常,当出现异常的时候,我们需要让程序停止下来,同时需要回滚事务。

from django.db import transaction
from rest_framework.views import APIView
class OrderAPIView(APIView):
    def post(self,request):
        ....
        with transation.atomic():
            # 设置事务回滚的标记点
            sid = transation.savepoint()

            ....

            try:
                ....
            except:
                transation.savepoint_rallback(sid)

使用django提供的mysql事务操作保证下单过程中的数据一致性

视图代码:

from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from django_redis import get_redis_connection
from rest_framework.response import Response
from .models import Order,OrderDetail
from datetime import datetime
from courses.models import Course, CourseTime
import random
from django.db import transaction
from rest_framework import status

import logging
log = logging.getLogger("django")

class OrderAPIView(APIView):
    permission_classes = [IsAuthenticated]
    def post(self,request):
        """生成订单"""
        # 获取用户ID
        # user_id = 1
        user_id = request.user.id

        # 订单号,必须保证唯一
        order_number =  datetime.now().strftime("%Y%m%d%H%M%S") + "%08d" % user_id + "%04d" % random.randint(0,9999)

        with transaction.atomic():

            # 数据库事务的回滚标记
            save_id = transaction.savepoint()

            # 生成空的订单
            try:
                order = Order.objects.create(
                    order_title="路飞学城课程购买",
                    total_price=0,
                    real_price=0,
                    order_number= order_number,
                    user_id=user_id,
                )

                # 到redis获取购物车信息
                redis = get_redis_connection("cart")
                # 勾选状态
                course_selects_set = redis.smembers("cart_selected_%s" % user_id )
                print( course_selects_set )
                # 购物车中商品课程列表
                cart_course_list = redis.hgetall("cart_%s" % user_id )
                print( cart_course_list )
                # 通过购物车信息到数据中提取相关数据
                # 计算订单总价
                total_price = 0

                # 开启redis的管道操作[事务操作]
                pipeline = redis.pipeline()
                pipeline.multi()

                for course_id_byte,expire_byte in cart_course_list.items():
                    if course_id_byte in course_selects_set:
                        expire = expire_byte.decode()
                        course_id = course_id_byte.decode()
                        course = Course.objects.get(pk=course_id)
                        if expire == '-1':
                            """永久有效"""
                            course_price = course.get_course_price()

                        else:
                            """有购买周期"""
                            coursetime = CourseTime.objects.get(course=course_id,timer=expire)
                            course_price = coursetime.course.get_course_price(coursetime.price)

                        total_price += course_price

                        # 生成订单详情
                        OrderDetail.objects.create(
                            order_id=order.id,
                            course_id=course_id,
                            expire=expire,
                            price=course.price if expire=='-1' else coursetime.price,
                            real_price=course_price,
                            discount_name=course.get_course_discount_type()
                        )

                        # 从购物车中移除已经加入订单的商品
                        pipeline.srem("cart_selected_%s" % user_id, course_id )
                        pipeline.hdel("cart_%s" % user_id, course_id )

                # 提交redis的事务操作
                pipeline.execute()
                # 补充订单的总价格
                order.total_price=total_price
                order.real_price=total_price
                order.save()

            except Exception:
                # 记录错误日志
                log.error( "%s" % Exception )
                # 回滚事务
                transaction.savepoint_rollback(save_id)
                # 响应结果
                return Response("message":"系统异常!",status=status.HTTP_507_INSUFFICIENT_STORAGE)

        # 响应结果
        return Response("message":"成功生成订单!","order":order.order_number)

change the utils/models.py

from django.db import models


class BaseModel(models.Model):
    """公共字段模型"""
    orders = models.IntegerField(verbose_name='显示顺序', null=True, blank=True)
    is_show = models.BooleanField(verbose_name="是否上架", default=False)
    is_delete = models.BooleanField(verbose_name="逻辑删除", default=False)
    create_time = models.DateTimeField(auto_now_add=True, verbose_name="添加时间")
    update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间")

    class Meta:
        # 设置当前模型在数据迁移的时候不要为它创建表
        abstract = True

the main urls.py

from django.contrib import admin
from django.urls import path, re_path, include
import xadmin
from xadmin.plugins import xversion
from django.views.static import serve
from django.conf import settings

xadmin.autodiscover()
xversion.register_models()  # version模块自动注册需要版本控制的 Model

urlpatterns = [
    ......
    path('orders/', include("orders.urls")),
]

orders/urls.py

from django.urls import path, re_path
from . import views


urlpatterns = [
    path("", views.OrderAPIView.as_view()),
]

前端请求生成订单

<template>
  。。。
            <span class="go-pay" @click="gotopay">去结算</span>
  。。。
</template>
<script>
import Header from "./common/Header"
import Footer from "./common/Footer"
export default 
    name: "Cart",
    。。。。
    methods:
      。。。。
      gotopay()
        // 提交结算,生成订单
        this.$axios.post(this.$settings.Host+"/orders/",,
            headers:
              // 注意:jwt后面必须有且只有一个空格!!!!
              "Authorization":"jwt " + this.token
            
        ).then(response=>
          let _this = this;
          this.$alert(response.data.message,"提示",
            callback()
              _this.$router.push(`orders/$response.data.order`);
            
          )
        ).catch(error=>
          console.log(error.response)
        )
      
    ,
    components:Header,Footer

</script>

create a Order.vue

<template>
    
</template>

<script>
    export default 
        name: "Order"
    
</script>

<style scoped>

</style>

router/index.js

import Vue from 'vue'
import Router from 'vue-router'
import Home from '../components/Home'
import Login from "../components/Login";
import Register from "../components/Register";
import Course from "../components/Course";
import Detail from "../components/Detail";
import Player from "../components/Player";
import Cart from "../components/Cart";
import Order from "../components/Order";

Vue.use(Router);

export default new Router(
  mode: 'history',
  routes: [
      ...
    
      name: "Order",
      path: "/orders/:order",  // 地址栏的命名绑定参数, 在组件中可以通过 this.$router.param.order可以获取数据
      component: Order,
    
  ]
)

显示结算页面

Order.vue

<template>
  <div class="cart">
    <Header/>
    <div class="cart-info">
        <h3 class="cart-top">购物车结算 <span>共1门课程</span></h3>
        <div class="cart-title">
           <el-row>
             <el-col :span="2">&nbsp;</el-col>
             <el-col :span="10">课程</el-col>
             <el-col :span="8">有效期</el-col>
             <el-col :span="4">价格</el-col>
           </el-row>
        </div>
          <div class="cart-item" v-for="item in course_list">
          <el-row>
             <el-col :span="2" class="checkbox">&nbsp;&nbsp;</el-col>
             <el-col :span="10" class="course-info">
               <img :src="item.course.course_http_img" alt="">
                <span>item.course.name</span>
             </el-col>
             <el-col :span="8"><span>永久有效</span></el-col>
             <el-col :span="4" class="course-price">¥item.unit_price</el-col>
           </el-row>
        </div>
        <div class="calc">
            <el-row class="pay-row">
              <el-col :span="4" class="pay-col"><span class="pay-text">支付方式:</span></el-col>
              <el-col :span="8">
                <span class="alipay"><img src="../../static/img/alipay2.png" alt=""></span>
                <span class="alipay wechat"><img src="../../static/img/wechat.png" alt=""></span>
              </el-col>
              <el-col :span="8" class="count">实付款: <span>¥total</span></el-col>
              <el-col :span="4" class="cart-pay"><span @click="payhander">支付宝支付</span></el-col>
            </el-row>
        </div>
    </div>
    <Footer/>
  </div>
</template>

<script>
  import Header from "./common/Header"
  import Footer from "./common/Footer"
  export default 
    name:"Order",
    data()
      return 
        token: localStorage.token || sessionStorage.token,
        id: localStorage.id || sessionStorage.id,
        order_id:sessionStorage.order_id || null,
        course_list:[],
        total:0,
      
    ,
    components:
      Header,
      Footer,
    ,
    created()

    ,
    methods: 
      payhander()

      
    
  
</script>

<style scoped>
.cart
  margin-top: 80px;

.cart-info
  overflow: hidden;
  width: 1200px;
  margin: auto;

.cart-top
  font-size: 18px;
  color: #666;
  margin: 25px 0;
  font-weight: normal;

.cart-top span
    font-size: 12px;
    color: #d0d0d0;
    display: inline-block;

.cart-title
    background: #F7F7F7;
    height: 70px;

.calc
  margin-top: 25px;
  margin-bottom: 40px;


.calc .count
  text-align: right;
  margin-right: 10px;
  vertical-align: middle;

.calc .count span
    font-size: 36px;
    color: #333;

.calc .cart-pay
    margin-top: 5px;
    width: 110px;
    height: 38px;
    outline: none;
    border: none;
    color: #fff;
    line-height: 38px;
    background: #ffc210;
    border-radius: 4px;
    font-size: 16px;
    text-align: center;
    cursor: pointer;

.cart-item
  height: 120px;
  line-height: 120px;

.course-info img
    width: 175px;
    height: 115px;
    margin-right: 35px;
    vertical-align: middle;

.alipay
  display: inline-block;
  height: 48px;

.alipay img
  height: 100%;
  width:auto;


.pay-text
  display: block;
  text-align: right;
  height: 100%;
  line-height: 100%;
  vertical-align: middle;
  margin-top: 20px;

</style>

后端提供订单数据的api

orders/serializers.py,序列化器,代码:

from rest_framework import serializers
from .models import Order,OrderDetail

class OrderCourseSerializer(serializers.ModelSerializer):
    class Meta:
        model = OrderDetail
        fields = ("course_name","expire_text","price","real_price","discount_name","course_img")

class OrderDetailModelSerializer(serializers.ModelSerializer):
    order_courses = OrderCourseSerializer(many=True)
    class Meta:
        model = Order
        fields = ("id", "total_price", "real_price","pay_type","use_coupon","coupon","order_courses")

模型中新增返回课程图片的字段,代码:

from courses.models import CourseTime
class OrderDetail(BaseModel):
    """订单详情"""
    order = models.ForeignKey(Order, related_name='order_courses', on_delete=models.CASCADE, verbose_name="订单")
    course = models.ForeignKey(Course, related_name='course_orders', on_delete=models.CASCADE, verbose_name="课程")
    expire = models.IntegerField(default='-1', 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="课程实价")
    discount_name = models.CharField(max_length=120,default="",verbose_name="优惠活动类型")
    class Meta:
        db_table="ly_order_detail"
        verbose_name= "订单详情"
        verbose_name_plural= "订单详情"

    def course_img(self):
        # 返回图片的url地址
        return self.course.course_img.url

    def expire_text(self):
        if self.expire == -1:
            return "永久有效"
        else:
            print( self.expire )
            coursetime = CourseTime.objects.get(timer=self.expire,course=self.course)
            return coursetime.title

    def course_name(self):
        return self.course.name

视图代码:

from .serializers import OrderDetailModelSerializer
class OrderDetailAPIView(APIView):
    permission_classes = [IsAuthenticated]
    def get(self,request,pk):
        try:
            order = Order.objects.get(order_number=pk)
        except Order.DoesNotExist:
            return Response("message":"订单信息有误!",status=status.HTTP_400_BAD_REQUEST)

        serializer = OrderDetailModelSerializer(instance=order)

        return Response(serializer.data, status=status.HTTP_200_OK)

路由代码:

    re_path(r"(?P<pk>\d+)/",views.OrderDetailAPIView.as_view()),

前端请求后端的订单信息

<template>
  <div class="cart">
    <Header/>
    <div class="cart-info">
        <h3 class="cart-top">购物车结算 <span>共1门课程</span></h3>
        <div class="cart-title">
           <el-row>
             <el-col :span="2">&nbsp;</el-col>
             <el-col :span="10">课程</el-col>
             <el-col :span="8">有效期</el-col>
             <el-col :span="4">价格</el-col>
           </el-row>
        </div>
        <div class="cart-item" v-for="course in order_info.order_courses">
          <el-row>
             <el-col :span="2" class="checkbox">&nbsp;&nbsp;</el-col>
             <el-col :span="10" class="course-info">
               <img style="float: left;" :src="$settings.Host+course.course_img" alt="">
               <span class="course_name">
                 <span>course.course_name</span><br>
                 <span class="discount_name">course.discount_name</span>
               </span>
             </el-col>
             <el-col :span="8" class="lh"><span>course.expire_text</span></el-col>
             <el-col :span="4">
               <div class="course-price">
                  <p class="real_price">¥course.real_price</p>
                  <span class="original_price">原价: ¥course.price</span>
               </div>
             </el-col>
           </el-row>
        </div>
        <div>
        <div class="coupon">
          <div id="accordion">
            <div class="coupon-box">
              <div class="coupon-title">
                <span class="select-coupon">使用优惠劵:</span>
                <a data-toggle="collapse" data-parent="#accordion" href="#collapseOne" style="width: 20px; height: 20px" class="collapsed" aria-expanded="false">
                  <img class="sign" src="../../static/img/12.png" width="20" height="20" alt=""></a>
                  <span class="coupon-num">有0张可用</span>
              </div>
              <p class="sum-price-wrap" style="margin-right: 45px">商品总金额:<span class="sum-price">order_info.total_price元</span></p>
            </div>
            <div style="text-align: left; height: 0px;" id="collapseOne" class="panel-collapse out collapse" aria-expanded="false">
              <ul class="coupon-list" style="display: none;">

              </ul>
              <div style="text-align: center; width: 100%; padding: 50px 0px; align-items: center; justify-content: center; border-bottom: 1px solid rgb(232, 232, 232);">
                <span style="font-size: 16px; color: #9b9b9b">暂无可用优惠券</span>
              </div>
            </div>
          </div>
          <div style="height: 30px; margin-top: 40px; display: flex; align-items: center; justify-content: flex-end">
            <input type="checkbox" class="ok" id="color-input-red">
            <label for="color-input-red"><img src=""alt=""></label>
            <p class="discount-num" style="color:#9B9B9B">使用我的贝里</p>
            <p class="discount-num" style="margin-right: 45px">
              <span style="display: none;">可用0个已抵扣 ¥0</span>
            </p>
          </div>
          <p class="sun-coupon-num" style="margin-right: 45px;margin-bottom:43px">优惠券抵扣:<span>0元</span></p>
        </div>
        </div>
        <div class="calc">
            <el-row class="pay-row">
              <el-col :span="4" class="pay-col"><span class="pay-text">支付方式:</span></el-col>
              <el-col :span="8">
                <span class="alipay"><img src="../../static/img/alipay2.png" alt=""></span>
                <span class="alipay wechat"><img src="../../static/img/wechat.png" alt=""></span>
              </el-col>
              <el-col :span="8" class="count">实付款: <span>¥order_info.total_price</span></el-col>
              <el-col :span="4" class="cart-pay"><span @click="payhander">支付宝支付</span></el-col>
            </el-row>
        </div>
    </div>
    <Footer/>
  </div>
</template>

<script>
  import Header from "./common/Header"
  import Footer from "./common/Footer"
  export default 
    name:"Order",
    data()
      return 
        order_info:,
      
    ,
    components:
      Header,
      Footer,
    ,
    created()
      // 判断用户是否已经登录
      let token = sessionStorage.token || localStorage.token;
      let _this = this;
      if(!token)
        this.$alert("对不起,您尚未登录!请登录!","警告",
          callback()
            _this.$router.push("/login");
          
        )
      
      // 获取地址栏上面的订单号
      let order_number = this.$route.params.order;

      // 发送请求获取数据
      this.$axios.get(this.$settings.Host+`/orders/$order_number/`,
          headers:
            // 注意下方的空格!!!
            "Authorization":"jwt " + token,
          ,
      ).then(response=>
        this.order_info = response.data;
      ).catch(error=>
        console.log(error.response);
      )
    ,
    methods: 
      payhander()

      
    
  
</script>

<style scoped>
.cart
  margin-top: 80px;

.cart-info
  overflow: hidden;
  width: 1200px;
  margin: auto;

.cart-top
  font-size: 18px;
  color: #666;
  margin: 25px 0;
  font-weight: normal;

.cart-top span
    font-size: 12px;
    color: #d0d0d0;
    display: inline-block;

.cart-title
    background: #F7F7F7;
    height: 70px;

.calc
  margin-top: 25px;
  margin-bottom: 40px;


.calc .count
  text-align: right;
  margin-right: 10px;
  vertical-align: middle;

.calc .count span
    font-size: 36px;
    color: #333;

.calc .cart-pay
    margin-top: 5px;
    width: 110px;
    height: 38px;
    outline: none;
    border: none;
    color: #fff;
    line-height: 38px;
    background: #ffc210;
    border-radius: 4px;
    font-size: 16px;
    text-align: center;
    cursor: pointer;

.cart-item
  height: 120px;
  /*line-height: 120px;*/

.cart-item .lh
  line-height: 120px;

.course-info img
    width: 175px;
    height: 115px;
    margin-right: 35px;
    vertical-align: middle;

.course-price
  margin-top: 40px;

.alipay
  display: inline-block;
  height: 48px;

.alipay img
  height: 100%;
  width:auto;


.pay-text
  display: block;
  text-align: right;
  height: 100%;
  line-height: 100%;
  vertical-align: middle;
  margin-top: 20px;

.real_price
    color: #333;
    margin-bottom: 10px;

.original_price
    color: #9b9b9b;
    letter-spacing: .36px;
    text-decoration: line-through;

.coupon
  margin-top: 30px;

.coupon-box
  text-align: left;
  display: flex;
  padding-bottom: 22px;
  padding-left:30px;
  border-bottom: 1px solid #e8e8e8;

.coupon-title
  display: flex;

.sum-price-wrap
    display: inline-block;
    margin-left: auto;
    font-size: 16px;
    color: #4a4a4a;

.discount_name
    color: #ffc210;
    margin-top: 24px;
    font-size: 14px;
    letter-spacing: .32px;

.course_name
  margin-top: 40px;
  display: block;

</style>

以上是关于支付和订单的主要内容,如果未能解决你的问题,请参考以下文章

小程序日记-微信支付提示201商户订单号重复

支付和订单

微信支付错误:201 商户订单号重复

Magento 支付工作流程和事件订单已支付

分布式事务DevOps

支付业务模块