目录

1.支付宝同步结果通知

2.用户购买记录表

3.接受异步支付结果

4.善后事宜

5.我的订单

1.支付宝同步结果通知

1.get请求支付宝,支付宝返回给你的参数

当用户输入用户名和密码确认支付的时候,支付宝会给vue前端回复一个url,这个url很长,后面包含了很多参数,参数如下所示:

http://www.luffycity.cn:8080/payments/result?
charset=utf8&
out_trade_no=20190929151453000001000020&
method=alipay.trade.page.pay.return&
total_amount=310.00&
sign=kebIZBI%2FpCNXmCivfJPPw21gcobulPZoSh%2BXiHR8l6cgexQi2STG4AZgr%2FEUhvc5kEMacJLvCmBaw1Wqo4WK3sPzbUaPmzq3NshUNzYK2lWTsmOauidNxlk1bK0Q%2FANBfQUkmj6TQjyB5T9QqEnS80KFsDrGrasU%2B%2Fz9W%2FjOCLrSji6TnKhRkI9pqBMdw823ABU75b7zOtXzcXKduO%2B6vsXVvluMzedss9dHs1celxPAWQV9jcKjzq%2F1bPbZcmgAGNQQecoJ%2BFSc3uTmTk24uV39PM54LIlg8aeRlkPNjvhBkJh%2FG0%2BURNDdG2593IFIF%2BUqoU%2F7ixm19dX222GCWg%3D%3D&
trade_no=2019092922001439881000120282&
auth_app_id=2016091600523592&
version=1.0&
app_id=2016091600523592&
sign_type=RSA2&
seller_id=2088102175868026&
timestamp=2019-09-29%2015%3A15%3A53

这些参数的含义:https://opendocs.alipay.com/apis/api_1/alipay.trade.page.pay

2.后端实现处理支付宝同步通知结果的视图

支付宝将参数传递给了vue前端,vue前端要这些参数发送给后端,让后端去对这些参数做一个校验。判断这个url是不是支付宝发过来的

后端校验支付宝返回给你的那些参数

payment/urls.py

from django.urls import path,re_path
from . import views urlpatterns = [
path('result/',views.AlipayResultView.as_view(),)
]

payment/view.py

class AlipayResultView(APIView):
permission_classes = [IsAuthenticated, ]
def get(self,request): # 1.创建alipay对象
alipay = AliPay(
appid=settings.ALIAPY_CONFIG['appid'],
app_notify_url=None, # 默认回调url
app_private_key_string=open(settings.ALIAPY_CONFIG['app_private_key_path']).read(),
# 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
alipay_public_key_string=open(settings.ALIAPY_CONFIG['alipay_public_key_path']).read(),
sign_type=settings.ALIAPY_CONFIG['sign_type'], # RSA 或者 RSA2
debug=settings.ALIAPY_CONFIG['debug'], # 默认False
)
# 2.校验支付宝响应数据
data = request.query_params.dict() # 获取那一大堆的参数 out_trade_no = data.get('out_trade_no') # 获取商户订单号
sign = data.pop('sign') # 获取签名
success = alipay.verify(data,sign) # 通过参数和签名来验证那一堆参数是不是支付宝发过来的
if not success:
logger.error('%s,支付宝响应数据校验失败' % out_trade_no)
return Response('支付宝响应数据校验失败',status=status.HTTP_400_BAD_REQUEST) # 响应结果
return Response({'msg':'ok','data':res_data})

3.vue前端发送请求验证get参数

前端发送请求 将支付宝发给vue前端的一大堆参数传递到后端,后端做完校验返回一个成功与否的结果

如果校验没有问题 就可以显示购买成功了。

Success.vue

created(){
// 把地址栏上面的支付结果,转发给后端
this.send_alipay_params(); methods:{
send_alipay_params(){
let token = localStorage.token || sessionStorage.token;
this.$axios.get(`${this.$settings.Host}/payment/result/${location.search}`,{
headers:{
'Authorization':'jwt ' + token
}
}).then((res)=>{ // 如果后端验证这些参数没有问题 购买成功页面需要的参数就可以传递过来了
this.pay_time = res.data.data.pay_time; // 支付时间
this.course_list = res.data.data.course_list; // 购买课程列表
this.total_real_price = res.data.data.total_real_price; // 课程总真实价格
}).catch((error)=>{
this.$message.error(error.response.data.msg);
})
},

2.用户购买记录表

当用户购买成功之后,应该生成用户记录,主要用来存支付平台的流水号,有了这个流水号就可以去支付宝查账单了。以及课程的购买时间和过期时间。

users/models.py

class UserCourse(BaseModel):
"""用户的课程购买记录"""
pay_choices = (
(1, '用户购买'),
(2, '免费活动'),
(3, '活动赠品'),
(4, '系统赠送'),
) user = models.ForeignKey(User, related_name='user_courses', on_delete=models.DO_NOTHING, verbose_name="用户")
course = models.ForeignKey(Course, related_name='course_users', on_delete=models.DO_NOTHING, verbose_name="课程")
trade_no = models.CharField(max_length=128, null=True, blank=True, verbose_name="支付平台的流水号", help_text="将来依靠流水号到支付平台查账单")
buy_type = models.SmallIntegerField(choices=pay_choices, default=1, verbose_name="购买方式")
pay_time = models.DateTimeField(null=True, blank=True, verbose_name="购买时间")
out_time = models.DateTimeField(null=True, blank=True, verbose_name="过期时间") # null表示永不过期 class Meta:
db_table = 'ly_user_course'
verbose_name = '课程购买记录'
verbose_name_plural = verbose_name

3.接受异步支付结果

payment/views.py

class AlipayResultView(APIView):
permission_classes = [IsAuthenticated, ]
def post(self,request): # 创建alipay对象
alipay = AliPay(
appid=settings.ALIAPY_CONFIG['appid'],
app_notify_url=None, # 默认回调url
app_private_key_string=open(settings.ALIAPY_CONFIG['app_private_key_path']).read(),
# 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
alipay_public_key_string=open(settings.ALIAPY_CONFIG['alipay_public_key_path']).read(),
sign_type=settings.ALIAPY_CONFIG['sign_type'], # RSA 或者 RSA2
debug=settings.ALIAPY_CONFIG['debug'], # 默认False
) # 校验支付宝响应数据
data = request.data.dict()
sign = data.pop('sign')
success = alipay.verify(data,sign)
if success and data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED"): self.change_order_status(data) return Response('success')

4.善后事宜

当一切都完成后,还有几件事情要做:

1.修改订单状态

2.扣除优惠劵 积分

3.清空购物车数据和结算页面全部数据

4.将相关信息存到用户购买记录表中

payment/views.py

class AlipayResultView(APIView):
def change_order_status(self,data): # 修改订单状态
with transaction.atomic():
out_trade_no = data.get('out_trade_no')
trade_no = data.get('trade_no') # A.修改订单状态
# 1.获取当前订单号的订单对象
order_obj = Order.objects.get(order_number=out_trade_no) # 2.将当前订单的订单状态改为1:已支付
order_obj.order_status = 1 # 3.保存订单信息
order_obj.save() # B.修改优惠券的使用状态
if order_obj.coupon > 0: # 如果用户使用了优惠劵
# 1.获取用户使用的那张优惠劵对象
user_coupon_obj = UserCoupon.objects.get(is_use=False, id=order_obj.coupon) # 2.将用户使用的那张优惠劵的状态由未使用改为已使用
user_coupon_obj.is_use = True # 3.保存用户的优惠劵的信息
user_coupon_obj.save() # C.支付成功,用户积分应该对应的扣除 # 1.查询当前订单使用了多少积分
use_credit = order_obj.credit # 2.查询到用户的总积分数减去使用的积分数得到用户的剩余积分数
self.request.user.credit -= use_credit # 3.保存用户的积分信息
self.request.user.save() # D.保存支付宝的交易流水号(购买记录表) # 1.通过订单对象反向查询到所有的订单详情对象
order_detail_objs = order_obj.order_courses.all() # 2.获取当前时间
now = datetime.datetime.now() # 购买成功 从redis中将课程的选中状态删除掉
conn = get_redis_connection('cart')
pipe = conn.pipeline()
pipe.delete('selected_cart_%s' % self.request.user.id) # 需要给购买成功页面(Success.vue返回的数据)
res_data = {
'pay_time': now,
'course_list': [],
'total_real_price': order_obj.real_price,
} for order_detail in order_detail_objs:
# 购买成功 课程学习的学生数+1
course = order_detail.course
course.students += 1
course.save() # 购买成功的课程应该显示课程列表的每个课程
res_data['course_list'].append(course.name) # 从课程详情页获取当前课程的有效期数值
expire_id = order_detail.expire
if expire_id > 0: # 如果不是永久有效 # 根据expire_id查询到课程有效期model对象
expire_obj = CourseExpire.objects.get(id=expire_id) # 查询到课程的有效期(天数)
expire_time = expire_obj.expire_time # 计算课程的过期时间
out_time = now + datetime.timedelta(days=expire_time)
else: # 如果是永久有效,就没有过期时间
out_time = None # 一切处理完毕,将相关信息存到用户购买记录表中
UserCourse.objects.create(**{
'user':self.request.user,
'course':course,
'trade_no':trade_no,
'buy_type':1,
'pay_time':now,
'out_time':out_time, })
# 购物车redis数据删除
pipe.hdel('cart_%s' % self.request.user.id, course.id)
pipe.execute() return res_data

order/serializers.py

def create:
order_obj.coupon = coupon_id
order_obj.credit = credit
order_obj.save()

5.我的订单

1.我的订单界面-初始化

Myorder.vue

<template>
<div class="user-order">
<Vheader/>
<div class="main">
<div class="banner"></div>
<div class="profile">
<div class="profile-info">
<div class="avatar"><img class="newImg" width="100%" alt="" src="../../static/img/logo@2x.png"></div>
<span class="user-name">吴某某</span>
<span class="user-job">北京市 | 程序员</span>
</div>
<ul class="my-item">
<li>我的账户</li>
<li class="active">我的订单</li>
<li>个人资料</li>
<li>账号安全</li>
</ul>
</div>
<div class="user-data">
<ul class="nav">
<li class="order-info">订单</li>
<li class="course-expire">有效期</li>
<li class="course-price">课程价格</li>
<li class="real-price">实付金额</li>
<li class="order-status">交易状态</li>
<li class="order-do">交易操作</li>
</ul>
<div class="my-order-item" v-for="(order_obj,index) in order_list" :key="index">
<div class="user-data-header">
<span class="order-time">xxxx</span>
<span class="order-num">订单号:
<span class="my-older-number">xxxx</span>
</span>
</div>
<ul class="nav user-data-list" v-for="(course_obj,course_index) in order_obj.order_detail_data">
<li class="order-info">
<img :src="course_obj.course_img" alt="">
<div class="order-info-title">
<p class="course-title">xxxx</p>
<p class="price-service">xxxx</p>
</div>
</li>
<li class="course-expire">xxxx</li>
<li class="course-price">xxxx</li>
<li class="real-price">xxxx</li>
<li class="order-status">xxxx</li>
<li class="order-do">
<span class="btn btn2" v-if="order_obj.get_order_status_display==='已支付'">去学习</span>
<span class="btn btn2" v-else-if="order_obj.get_order_status_display==='未支付'" @click="go_pay(order_obj.order_number)">去付款</span>
<span class="btn btn2" v-else-if="order_obj.get_order_status_display==='超时取消'">超时取消</span>
<span class="btn btn2" v-else>已取消</span>
</li>
</ul>
</div>
</div>
</div>
<Footer/>
</div>
</template> <script>
import Vheader from "./common/Vheader"
import Footer from "./common/Footer"
export default{
name:"Myorder",
data(){
return { };
},
created(){ },
methods:{ },
components:{
Vheader,
Footer,
}
}
</script> <style scoped>
.main .banner{
width: 100%;
height: 324px;
background: url(../../static/img/my_bkging.0648ebe.png) no-repeat;
background-size: cover;
z-index: 1;
}
.profile{
width: 1200px;
margin: 0 auto;
}
.profile-info{
text-align: center;
margin-top: -80px;
}
.avatar{
width: 120px;
height: 120px;
border-radius: 60px;
overflow: hidden;
margin: 0 auto;
}
.user-name{
display: block;
font-size: 24px;
color: #4a4a4a;
margin-top: 14px;
}
.user-job{
display: block;
font-size: 11px;
color: #9b9b9b;
}
.my-item{
list-style: none;
line-height: 1.42857143;
color: #333;
width: 474px;
height: 31px;
display: -ms-flexbox;
display: flex;
cursor: pointer;
margin: 41px auto 0;
-ms-flex-pack: justify;
justify-content: space-between;
}
.my-item .active{
border-bottom: 1px solid #000;
}
.user-data{
width: 1200px;
height: auto;
margin: 0 auto;
padding-top: 30px;
border-top: 1px solid #e8e8e8;
margin-bottom: 63px;
}
.nav{
width: 100%;
height: 60px;
background: #e9e9e9;
display: -ms-flexbox;
display: flex;
-ms-flex-align: center;
align-items: center;
}
.nav li{
margin-left: 20px;
margin-right: 28px;
height: 60px;
line-height: 60px;
list-style: none;
font-size: 13px;
color: #333;
border-bottom: 1px solid #e9e9e9;
width: 160px;
}
.nav .order-info{ width: 325px; }
.nav .course-expire{ width: 60px; }
.nav .course-price{ width: 130px; }
.user-data-header{
display: flex;
height: 44px;
color: #4a4a4a;
font-size: 14px;
background: #f3f3f3;
-ms-flex-align: center;
align-items: center;
}
.order-time{
font-size: 12px;
display: inline-block;
margin-left: 20px;
}
.order-num{
font-size: 12px;
display: inline-block;
margin-left: 29px;
}
.user-data-list{
height: 100%;
display: flex;
}
.user-data-list{
background: none;
}
.user-data-list li{
height: 60px;
line-height: 60px;
}
.user-data-list .order-info{
display: flex;
align-items: center;
margin-right: 28px;
}
.user-data-list .order-info img{
max-width: 100px;
max-height: 75px;
margin-right: 22px;
}
.course-title{
width: 203px;
font-size: 13px;
color: #333;
line-height: 5px;
margin-top: -10px;
}
.order-info-title .price-service{
line-height: 18px;
}
.price-service{
font-size: 12px;
color: #fa6240;
padding: 0 5px;
border: 1px solid #fa6240;
border-radius: 4px;
margin-top: 4px;
position: absolute;
}
.order-info-title{
margin-top: -10px;
}
.user-data-list .course-expire{
font-size: 12px;
color: #ff5502;
width: 60px;
text-align: center;
}
.btn {
width: 100px;
height: 32px;
font-size: 14px;
color: #fff;
background: #ffc210;
border-radius: 4px;
border: none;
outline: none;
transition: all .25s ease;
display: inline-block;
line-height: 32px;
text-align: center;
cursor: pointer;
}
</style>

我的订单页面-初始化

index.js

{
path: '/myorder/',
component: Myorder
},

2.我的订单页面-后端接口

users/urls.py

urlpatterns = [
path(r'myorder/', views.MyOrderView.as_view()),
]

users/views.py

class MyOrderView(ListAPIView):
permission_classes = [IsAuthenticated, ]
serializer_class = MyOrderModelSerializer def get_queryset(self):
return Order.objects.filter(user=self.request.user)

user/serializers.py

class MyOrderModelSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ['id', 'order_number' ,'pay_time', 'get_order_status_display', 'order_detail_data']

order/models.py

在我的订单页面中,需要展示一些数据

class Order(BaseModel):
def order_detail_data(self):
# 获取所有课程详情对象
order_detail_objs = self.order_courses.all()
data_list = [] for order_detail in order_detail_objs:
expire_id = order_detail.expire # 根据有效期来决定expire_text返回什么
if expire_id > 0:
expire_obj = CourseExpire.objects.get(id=expire_id)
expire_text = expire_obj.expire_text
else:
expire_text = '永久有效' # 每个课程应该包含的字段
order_dict = {
'course_img':contains.SERVER_ADDR + order_detail.course.course_img.url,
'course_name':order_detail.course.name,
'expire_text':expire_text,
'price':order_detail.price,
'real_price': self.real_price,
'discount_name':order_detail.discount_name, }
# 将每个课程的详情信息添加到一个列表里返回给前端
data_list.append(order_dict) return data_list

3.我的订单页面-前端

1.获取后端的订单数据

Myorder.vue

// js
get_order_data(){
// 检查当前访问者是否登录了!
let token = localStorage.token || sessionStorage.token;
this.$axios.get(`${this.$settings.Host}/users/myorder/`,{
headers:{
'Authorization':'jwt ' + token
}
}).then((res)=>{
this.order_list = res.data;
}).catch((error)=>{ })
<!-- html -->
<div class="my-order-item" v-for="(order_obj,index) in order_list" :key="index">
<div class="user-data-header">
<span class="order-time">{{order_obj.pay_time.replace('T', ' ')}}</span>
<span class="order-num">订单号:
<span class="my-older-number">{{order_obj.order_number}}</span>
</span>
</div>
<ul class="nav user-data-list" v-for="(course_obj,course_index) in order_obj.order_detail_data">
<li class="order-info">
<img :src="course_obj.course_img" alt="">
<div class="order-info-title">
<p class="course-title">{{course_obj.course_name}}</p>
<p class="price-service">{{course_obj.discount_name}}</p>
</div>
</li>
<li class="course-expire">{{course_obj.expire_text}}</li>
<li class="course-price">{{course_obj.price}}</li>
<li class="real-price">{{course_obj.real_price}}</li>
<li class="order-status">{{order_obj.get_order_status_display}}</li>
<li class="order-do">
<span class="btn btn2" v-if="order_obj.get_order_status_display==='已支付'">去学习</span>
<span class="btn btn2" v-else-if="order_obj.get_order_status_display==='未支付'" @click="go_pay(order_obj.order_number)">去付款</span>
<span class="btn btn2" v-else-if="order_obj.get_order_status_display==='超时取消'">超时取消</span>
<span class="btn btn2" v-else>已取消</span>
</li>
</ul>
</div>

2.我的订单页面点击去付款

Myorder.vue

<!-- html -->
<span class="btn btn2" v-else-if="order_obj.get_order_status_display==='未支付'" @click="go_pay(order_obj.order_number)">去付款</span>
// js
go_pay(order_number){
let token = localStorage.token || sessionStorage.token;
this.$axios.get(`${this.$settings.Host}/payment/alipay/?order_number=${order_number}`,{
headers:{
'Authorization':'jwt ' + token
} }).then((res)=>{
location.href = res.data.url; }).catch((error)=>{
this.$message.error(error.response.data.msg);
})

day88:luffy:支付宝同步结果通知&接收异步支付结果&用户购买记录&我的订单的更多相关文章

  1. 通知url必须为直接可访问的url,不能携带参数 异步接收微信支付结果通知的回调地址 不能携带参数。 回调地址后是否可以加自定义参数 同步回调地址 异步回调地址 return_url和notify_url的区别

    [微信支付]微信小程序支付开发者文档 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_7 通知url必须为直接可访问的 ...

  2. day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知

    目录 1.基于支付宝提供的沙箱测试环境开发支付接口 1.后端提供创建充值订单接口 2.前端调用AlipayPlus发起支付 3.注意:自定义APPLoader完成接下来的开发 4.下载支付宝沙箱钱包A ...

  3. 同步I/O、异步I/O与阻塞I/O、非阻塞I/O的区别

    一.I/O I/O (Input/Output,输入/输出)即数据的读取(接收)或写入(发送)操作. 通常用户进程中的一个完整I/O分为两阶段:用户进程空间<-->内核空间.内核空间< ...

  4. 《Windows核心编程系列》十谈谈同步设备IO与异步设备IO之异步IO

    同步设备IO与异步设备IO之异步IO介绍 设备IO与cpu速度甚至是内存访问相比较都是比较慢的,而且更不可预测.虽然如此,通过使用异步设备IO我们仍然能够创造出更高效的程序. 同步IO时,发出IO请求 ...

  5. Linux设备驱动中的异步通知与异步I/O

    异步通知概念: 异步通知的意识是,一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上的“中断”概念,比较准确的称谓是“信号驱动的异步IO”,信号是在软件层次 ...

  6. .NET Core 实践二:事件通知和异步处理

    首先让我们来先看一个例子: 这是一个简单的用户下单购买商品的业务模型,输入端是用户,相关物料有订单和货物,相关的内部服务有业务(订单).财务(支付).仓储(备货)和物流(运输). 从图中我们可以看到, ...

  7. 转:IO模型-- 同步和阻塞,异步和非阻塞的区别

    源地址 http://hi.baidu.com/deep_pro/item/db0c581af1c1f17e7b5f2534 这些词之间的区别难倒了很多人,还有什么同步阻塞, 同步非阻塞, 异步阻塞, ...

  8. arm驱动linux异步通知与异步IO【转】

    转自:http://blog.csdn.net/chinazhangzhong123/article/details/51638793 <[ arm驱动] linux异步通知与 异步IO> ...

  9. Windows核心编程:第10章 同步设备IO与异步设备IO

    Github https://github.com/gongluck/Windows-Core-Program.git //第10章 同步设备IO与异步设备IO.cpp: 定义应用程序的入口点. // ...

随机推荐

  1. idea 2020.1 Mybatis log plugin破解插件

    下载 链接: https://pan.baidu.com/s/1FTgtJiyzxxaNxWLyX4OgZw 密码: w7w8 idea安装本地插件

  2. 从0到1进行Spark history分析

    一.总体思路 以上是我在平时工作中分析spark程序报错以及性能问题时的一般步骤.当然,首先说明一下,以上分析步骤是基于企业级大数据平台,该平台会抹平很多开发难度,比如会有调度日志(spark-sub ...

  3. JavaWeb01_html&css

    一. html简介 1. 什么是html ①. HyperText Markup Language:超文本标记语言,是最基本的网页语言 ②. 超文本:超出文本范畴 ③. 标记:标记就是标签,html所 ...

  4. EV加密播放器的分析过程+过虚拟机方法

    开启了OD载入播放器进行分析,发现如下问题:1.播放器会进行翻录检测2.防止虚拟机播放3.视频播放后,可直接对内存操作提取出源视频翻录检测:主要是对指定的文件名或进程名对比虚拟机检测:是针对虚拟机特征 ...

  5. centos8平台用NetworkManager/nmcli管理网络

    一,centos8上,网络服务的管理需要NetworkManager服务 1,NetworkManager的服务操作 启动 [root@localhost network-scripts]# syst ...

  6. matplotlib 设置标题 xy标题等

    import matplotlib.pyplot as plt import matplotlib as mpl baseclass=[1,2,3,4] name = ['class1','class ...

  7. Dubbo 常用模型

    先了解如下几个概念 Invoker Invoker 是实体域,它是 Dubbo 的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起 invoke 调用,它有可能是一个本地的实现 ...

  8. 手把手教你使用 Nginx Ingress 实现金丝雀发布

    概述 本文将介绍如何使用 Nginx Ingress 实现金丝雀发布,从使用场景分析,到用法详解,再到上手实践. 前提条件 集群中需要部署 Nginx Ingress 作为 Ingress Contr ...

  9. linux上安装mitmproxy

    一.去git上下载安装包 下载mitmproxy二进制安装包:https://github.com/mitmproxy/mitmproxy/releases/ 二.安装 #上传 rz 安装包的本地路径 ...

  10. 老板,来几道web玩玩

    好久没做web了,没想到还能自己做出来555 [MRCTF2020]Ez_bypass 签到题8 给了源码,一个md5强类型比较,然后post传参,弱类型判断,直接1234567a绕过了 I put ...