luffy-16/订单表设计,立即付款接口/前端,支付成功get回调用户展示,支付成功post回调修改订单状态
这里写目录标题
- 一.昨日回顾
- 二.今日内容
- 1.订单表设计
- 2.立即付款接口(一堆校验,登录后)
- 2.1视图类
- 2.2序列化类
- 2.3自定义异常类
- 2.4配置文件
- 2.5路由
- 3.立即付款前端
- 4.支付成功get回调用户展示
- 4.1luffycity/ src / views / PaySucess.vue
- 4.2luffycity/ src / router / index.js
- 4.3get回调参数
- 4.4后端接口
- 5.支付成功post回调修改订单状态
一.昨日回顾
1 支付宝支付:-商户号:营业执照(登录的沙箱环境的商家)-客户:我们客户
2 生成公钥和私钥:公钥配置到支付宝网站上----》支付宝公钥-担心私钥不安全(不要把公钥私钥直接放在代码中,而是放在服务器上)-支付宝支付(官方没有sdk,只提供了api),我们用了第三方,担心有问题
3 支付流程-用户在前端提交支付--->后端要生成订单(未支付)--->生成支付宝付款链接--->返回前端--->get请求支付链接--->支付宝付款页面(扫描付款)--->支付宝收到钱--->get回调到我们网站(展示用户付款成功)--->post回调(验签)修改订单状态-用户刚打开支付链接,你服务挂了,用户付款成功了-get回调也回不来了-支付宝的post回调,你也收不到(24小时以后,支付宝会推送8次)-25小时以内完成8次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h)
二.今日内容
1.订单表设计
1 两张表-一个订单可能包含多门课程-订单表:订单号,订单生成时间,订单总价格。。。(订单跟订单详情是一对多的关系)-订单详情表:order,course,该课程的价格。。。
from django.db import modelsfrom user.models import User
from course.models import Course
# 不同app之间的表,可以建立关联关系,导入使用
#user = models.ForeignKey(User) # 不能用引号引起来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)# 咱们生成的订单号(唯一:分布式id生成方案:雪花算法,snowflake)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="支付方式")# 支付时间(付款成功的时候,等支付宝post回调,回掉回来以后,返回数据中有支付时间)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)@propertydef 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_listclass 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__() # 相当于没写
2.立即付款接口(一堆校验,登录后)
1 付款接口需要登录后才能使用-自己写jwt的认证类-djangorestframwork-jwt 内置有个认证类,+ djangorestframwork的权限类也可以完成认证(目前使用这种)2 记得更改lib/al_pay/pay.py
这个网址后面记得加'?'
# 支付网关
GATEWAY = 'https://openapi.alipaydev.com/gateway.do?' if DEBUG else 'https://openapi.alipay.com/gateway.do?'
2.1视图类
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import CreateModelMixin
from .models import Order
from .serializer import PayOrderSerializer
from utils.response import APIResponse
class PayView(GenericViewSet,CreateModelMixin):queryset = Order.objects.all()serializer_class = PayOrderSerializer# 认证类,需要搭配权限类--->保证必须登录才能用# 只使用下面一个:(JSONWebTokenAuthentication)# 用户可以登录,也可以不登录,# 如果登录了后续能之间获取request.user# 如果没有登录,就获取不到authentication_classes = [JSONWebTokenAuthentication,]permission_classes = [IsAuthenticated,]# 重写create,控制返回的数据def create(self, request, *args, **kwargs):ser=self.get_serializer(data=request.data,context={'request':request})ser.is_valid(raise_exception=True)#保存self.perform_create(ser)# ser.save() 跟跟上面一样pay_url=ser.context.get('pay_url')return APIResponse(msg='订单创建成功',pay_url=pay_url)
2.2序列化类
from rest_framework import serializers
from course.models import Course
from rest_framework.exceptions import ValidationError
from utils.exception import MyException
from .models import Order,OrderDetail
import uuid
from django.conf import settings
from lib.al_pay import alipay,gateway
class PayOrderSerializer(serializers.ModelSerializer):# 用户传入的是courses:[1,2,3]==转成课程对象==》[obj1,obj2,obj3]# courses:[1,2,3]---->PrimaryKeyRelatedField--->指定queryset,会去queryset中过滤,主键为[1,2,3]的课程courses = serializers.PrimaryKeyRelatedField(queryset=Course.objects.all(), many=True, write_only=True)# user=serializers.PrimaryKeyRelatedField(queryset=User.objects.all(),many=False)class Meta:model = Order# 从前端传过来(订单号后端生成)'''{'subject':'python从入门到精通',total_amount:100,pay_type:1 (支付类型,目前只有支付宝),courses:[1,2,3] 课程id号}'''fields = ['subject', 'total_amount', 'pay_type', 'courses']extra_kwargs = {'total_amount': {'required': True, }}def _check_price(self, attrs):courses = attrs.get('courses')total = 0for course in courses:total += course.priceif not total == attrs.get('total_amount'):raise MyException('钱数不合法')def _get_order_no(self):order_no=str(uuid.uuid4())return order_no.replace('-','')def _get_user(self):user=self.context.get('request').userreturn userdef _gen_pay_url(self,attrs,order_no):order_string = alipay.api_alipay_trade_page_pay(out_trade_no=order_no,total_amount=float(attrs.get('total_amount')), # 转成浮点型subject=attrs.get('subject'),# 写在配置文件中return_url=settings.RETURN_URL,notify_url=settings.NOTIFY_URL)return gateway+order_stringdef _pre_create(self,attrs,user,order_no):# 把用户,订单状态,订单号,写入attrsattrs['order_status']=0attrs['user']=userattrs['out_trade_no']=order_nodef validate(self, attrs):# 1)订单总价校验self._check_price(attrs)# 2)生成订单号order_no=self._get_order_no()# 3)支付用户:request.useruser=self._get_user()# 4)支付链接生成pay_url=self._gen_pay_url(attrs,order_no)self.context['pay_url']=pay_url# 5)入库(两个表)的信息准备(重写create方法)self._pre_create(attrs,user,order_no)return attrsdef create(self, validated_data):# 存两个表# course需要存detail表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
2.3自定义异常类
'''=========>utils/exception.py'''
# 自定义异常类
from rest_framework.exceptions import APIExceptionclass MyException(APIException):status_code = 405default_detail = '校验出错了'default_code = '错误'def __init__(self, detail=None, code=None):super().__init__(detail=None, code=None)
2.4配置文件
'''========>settings/dev.py'''
# 支付宝回调地址RETURN_URL = "http://127.0.0.1:8080/course/paysucess"
NOTIFY_URL = "http://127.0.0.1:8000/" # 必须要用公网
2.5路由
# 使用相对导入
from . import views
# 自动生成路径
from rest_framework.routers import SimpleRouter
from .views import PayView
router = SimpleRouter()
# 127.0.0.1:8000/order/pay/
router.register('pay',PayView,'PayView')from .views import PayView
urlpatterns = [# path('pay', PayView.as_view()),
]
urlpatterns+=router.urls # 方式二
3.立即付款前端
views/ActualCourse.vue
buy_now(course) {//没有登录,提示请先登录let token = this.$cookies.get('token')if (token) {//发送请求获取支付链接this.$axios({method: 'post',url: this.$settings.base_url + '/order/pay/',data: {subject: course.name,total_amount: course.price,pay_type: 1,courses: [course.id,]},headers: {authorization: 'jwt ' + token}}).then(item => {console.log(item.data)if (item.data.status == 0) {open(item.data.pay_url, '_self')} else {this.$message({message: item.data.msg})}})} else {this.$message({message: "请先登录"})}
}
4.支付成功get回调用户展示
4.1luffycity/ src / views / PaySucess.vue
<template><div class="pay-success"><!--如果是单独的页面,就没必要展示导航栏(带有登录的用户)--><Header/><div class="main"><div class="title"><div class="success-tips"><p class="tips">您已成功购买 1 门课程!</p></div></div><div class="order-info"><p class="info"><b>订单号:</b><span>{{ result.out_trade_no }}</span></p><p class="info"><b>交易号:</b><span>{{ result.trade_no }}</span></p><p class="info"><b>付款时间:</b><span><span>{{ result.timestamp }}</span></span></p></div><div class="study"><span>立即学习</span></div></div></div>
</template><script>import Header from "@/components/Header"export default {name: "Success",data() {return {result: {},};},created() {// url后拼接的参数:?及后面的所有参数 => ?a=1&b=2// console.log(location.search);// 解析支付宝回调的url参数let params = location.search.substring(1); // 去除? => a=1&b=2let items = params.length ? params.split('&') : []; // ['a=1', 'b=2']//逐个将每一项添加到args对象中for (let i = 0; i < items.length; i++) { // 第一次循环a=1,第二次b=2let k_v = items[i].split('='); // ['a', '1']//解码操作,因为查询字符串经过编码的if (k_v.length >= 2) {// url编码反解let k = decodeURIComponent(k_v[0]);this.result[k] = decodeURIComponent(k_v[1]);// 没有url编码反解// this.result[k_v[0]] = k_v[1];}}// 解析后的结果// console.log(this.result);// 把地址栏上面的支付结果,再get请求转发给后端this.$axios({url: this.$settings.base_url + '/order/success/' + location.search,method: 'get',}).then(response => {console.log(response.data);if(!response.data.status==0){console.log('暂未收到您的付款,请3s钟后再刷新该页面')}}).catch(() => {console.log('支付结果同步失败');})},components: {Header,}}
</script><style scoped>.main {padding: 60px 0;margin: 0 auto;width: 1200px;background: #fff;}.main .title {display: flex;-ms-flex-align: center;align-items: center;padding: 25px 40px;border-bottom: 1px solid #f2f2f2;}.main .title .success-tips {box-sizing: border-box;}.title img {vertical-align: middle;width: 60px;height: 60px;margin-right: 40px;}.title .success-tips {box-sizing: border-box;}.title .tips {font-size: 26px;color: #000;}.info span {color: #ec6730;}.order-info {padding: 25px 48px;padding-bottom: 15px;border-bottom: 1px solid #f2f2f2;}.order-info p {display: -ms-flexbox;display: flex;margin-bottom: 10px;font-size: 16px;}.order-info p b {font-weight: 400;color: #9d9d9d;white-space: nowrap;}.study {padding: 25px 40px;}.study span {display: block;width: 140px;height: 42px;text-align: center;line-height: 42px;cursor: pointer;background: #ffc210;border-radius: 6px;font-size: 16px;color: #fff;}
</style>
4.2luffycity/ src / router / index.js
{path: '/course/paysucess',name: 'PaySucess',component: PaySucess,}
4.3get回调参数
# get回调,携带一些参数过来,展示课程购买成功
charset=utf-8&
out_trade_no=fe2baf0893d1403a89773e1e0151b4b3&
method=alipay.trade.page.pay.return&total_amount=99.00&
sign=C%2BtM1IqF9QBB7N86m6sEkJDoe8nvGKEymOPst%2FhrOTqZvZdyRTbr37a%2BAkhjV6Co6ot64mwJDVvlraJqevYltjXvWNovcViYAXL3JNZ%2FUoOo91PIsgFMJsTgXSy2R%2FyQ7NAhRhIhxGhNs5JNzTLt2JINKcZ%2FiUxzM%2Bkz3Z1EbjgB0JaDoNgRs9Wpwqb1VT%2FwZnyOAuoxOBwhij2SeP2ZC5qZfjQ8gzglSiUzbrQplbT3ZCGu5NHE1h42Zs8r3PkyxhghoK2T8UC7suI2u7l94713L8vP5hnegxkB79fNd4DQEZ4hnTV5nWDXjXw5RP9ob%2FQthMCTGssPS9%2Flme0%2F7w%3D%3D&
trade_no=2021011522001453300501032429&
auth_app_id=2016092000554611&version=1.0&
app_id=2016092000554611&
sign_type=RSA2&seller_id=2088102176466324&
timestamp=2021-01-15%2012%3A23%3A44
4.4后端接口
'''=====>order/urls.py'''
from .views import PaySuccess
path('success', PaySuccess.as_view()),
'''=====>order/views.py'''
class PaySuccess(APIView):def get(self,request,*args,**kwargs): # 咱们前端回调的out_trade_no=request.GET.get('out_trade_no')# 去数据库查询该订单是否已经支付完成order=Order.objects.filter(out_trade_no=out_trade_no,order_status=1).first()if order: # post回调回调完了,订单状态改了return APIResponse(msg='支付成功')else:return APIResponse(status=1,msg='暂未支付成功')
5.支付成功post回调修改订单状态
# lib/al_pay/init.py
from .pay import alipay,gateway# order/views.py
from rest_framework.views import APIView
from utils.logging import get_logger
logger=get_logger('pay')class PaySuccess(APIView):def post(self,request,*args,**kwargs): # 支付宝回调# 验签,通过,修改订单状态,返回给支付宝successtry:result_data = request.data.dict() # 一定不能忘out_trade_no = result_data.get('out_trade_no')signature = result_data.pop('sign') # 签名,必须验证签名from lib import al_payresult = al_pay.alipay.verify(result_data, signature)if result and result_data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED"):# 完成订单修改:订单状态、流水号、支付时间Order.objects.filter(out_trade_no=out_trade_no).update(order_status=1)# 完成日志记录logger.warning('%s订单支付成功' % out_trade_no)return Response('success') # 必须返回给支付宝,否则支付宝会一直回调else:logger.error('%s订单支付失败' % out_trade_no)except:passreturn Response('failed')
luffy-16/订单表设计,立即付款接口/前端,支付成功get回调用户展示,支付成功post回调修改订单状态相关推荐
- java mysql 订单表设计
最近由于系统日益复杂的需求系统中各种类型订单越来越多,原来的这些订单表已经不能满足当下的需求,以可扩展为目标打算对这些订单表进行重构,本文只涉及基础版的设计,对于高并发.分布式等暂不考虑. 之前的系统 ...
- php订单表设计,订单详情表,与,订单表 怎么做?
detail订单详情表 字段名 数据类型 默认值 允许非空 自动递增 备注 id int(11) NO 是 id orderid int(11) NO 订单id号 goodsid int(11) NO ...
- dj电商-数据表的设计-购物车表与订单表设计
购物车,商品数量 用户的购买数量不应该超过库存的数量 使用redis实现购物车的功能 原因 如果通过mysql查询数据库,浪费性能 通过redis来查,速度更快 订单 点击去结算,进入订单 >订 ...
- 订单表的字段类型 mysql_Mysql数据库下订单表如何设计?
Mysql数据库下订单表如何设计 商品表和订单表 . 通过一个表来关联. 那删除了商品,相关联的订单表如何显示出这个已经删除的商品 订单表需要冗余商品名.商品编号.价格等基本信息. 不能只保存一个商品 ...
- 用mysql建销售订单主表_mysql订单表如何设计?
mysql订单表如何设计? 商品表和订单表 . 通过一个表来关联. 那删除了商品,相关联的订单表如何显示出这个已经删除的商品? 订单表需要冗余商品名.商品编号.价格等基本信息. 不能只保存一个商品主键 ...
- java后台实现支付宝支付接口、支付宝订单查询接口 前端为APP
最近项目APP需要接入微信.支付宝支付功能,在分配开发任务时,听说微信支付接口比支付宝支付接口要难实现,由于我开发经验不是那么丰富(现工作经验1年半)且未接触过支付接口开发,组里刚好又有支付接口的老司 ...
- Mysql订单表如何设计?
mysql订单表如何设计? 商品表和订单表 . 通过一个表来关联. 那删除了商品,相关联的订单表如何显示出这个已经删除的商品? 订单表需要冗余商品名.商品编号.价格等基本信息. 不能只保存一个商品主键 ...
- 电子商务网站中订单号设计有什么规则和依据吗?
https://www.zhihu.com/question/19805896#answer-31069940 你是个程序员. 隔壁老王通过你老婆找到你,说要做个"巨牛逼电商网站&qu ...
- 怎么创建数据表的实体类和业务类_SSM搭建二手市场交易平台(二):数据表设计...
写在前面 从本篇开始,我们正式开始项目的搭建,首先介绍数据表的设计,具体包括:表结构,表关系,唯一索引,单索引及组合索引,时间戳这几个内容. 数据表 首先我们创建一个数据库store,然后开始创建数据 ...
最新文章
- topcoder srm 495 div1
- Leetcode 160 相交链表 (每日一题 20210802)
- 小波的秘密8_图像处理应用:图像降噪
- 18.虚拟机linux上网问题
- 如何估算代码量_没有量杯,没有称,如何估算碳水化合物?
- eclipse中如何配置tomcat
- html pdf文档的格式控制符,关于Unicode控制字符RLO,LRO,PDF 的坑
- C++读写txt文件方式以及基于opencv的Mat数据类型读写txt文件类型
- python2.7安装教程win7_win7下python2.7安装 pip,setuptools的正确方法
- matlab ones size,matlab中zeros 和 ones 这两个函数的用法以及size的用法,princomp,pcacov,pcares,barttest四大分析函数用法...
- [管理]ERP专业术语
- PHPwebshell2022免杀bypass阿里云盾等所有安全设备
- 2017 Python 问卷调查结果初步分析
- 加油站踩踏式逃亡?电网成大赢家?时代抛弃你的时候,真的一声不吭
- 开学季数码好物推荐,大学必备数码产品选购清单
- XML文件的一些操作
- eos安装区块链开发环境
- Android Send Email 发送邮件
- 【DFS(记忆化)】hdu 2452 Navy maneuvers
- 【湍流】基于matlab模拟拉盖尔高斯光束传播的光强