目录
@H_
301_4@
1.支付宝同步结果通知
@H_
301_4@
2.用户购买记录表
@H_
301_4@
3.接受异步支付结果
@H_
301_4@
4.善后事宜
@H_
301_4@
5.我的订单
1.支付宝同步结果通知
1.get请求支付宝,支付宝返回给你的参数
@H_
301_4@当
用户输入
用户名和密码确认支付的时候,支付宝会给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=
sign_type=RSA2&
seller_id=2088102175868026&
timestamp=2019-09-29%2015%3A15%3A53
@H_
301_4@
这些参数的含义:https://opendocs.alipay.com/apis/api_1/alipay.trade.page.pay
2.后端实现处理支付宝同步通知结果的视图
@H_
301_4@支付宝将参数传递给了vue前端,vue前端要这些参数发送给后端,让后端去对这些参数做一个校验。判断这个url是不是支付宝发过来的
后端校验支付宝返回给你的那些参数
@H_
301_4@
payment/urls.py
from django.urls import path,re_path
from . views
urlpatterns = [
path('result/',views.AlipayResultView.as_view(),)
]
@H_
301_4@
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_pathsign_type'],1)"> 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':okdata:res_data})
3.vue前端发送请求验证get参数
前端发送请求 将支付宝发给vue前端的一大堆参数传递到后端,后端做完校验返回一个成功与否的结果
@H_
301_4@
如果校验没有问题 就可以显示购买成功了。
@H_
301_4@
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)=>{
.$message.error(error.response.data.msg);
})
},
2.用户购买记录表
@H_
301_4@
当用户购买成功之后,应该生成用户记录,主要用来存支付平台的流水号,有了这个流水号就可以去支付宝查账单了。以及课程的购买时间和过期时间。
@H_
301_4@
users/models.py
UserCourse(BaseModel):
"""用户的课程购买记录"""
pay_choices = (
(1,1)">用户购买),(2,1)">免费活动活动赠品系统赠送user_courses"用户")
course = models.ForeignKey(Course,1)">course_users课程)
trade_no = models.CharField(max_length=128,null=True,blank=True,1)">支付平台的流水号",help_text=将来依靠流水号到支付平台查账单)
buy_type = models.SmallIntegerField(choices=pay_choices,default=1,1)">购买方式)
pay_time = models.DateTimeField(null=True,1)">购买时间)
out_time = models.DateTimeField(null=True,1)">过期时间") null表示永不过期
Meta:
db_table = ly_user_course
verbose_name = 课程购买记录
verbose_name_plural = verbose_name
3.接受异步支付结果
@H_
301_4@
payment/views.py
post(self,1)"> 创建alipay对象
alipay = )
校验支付宝响应数据
data = request.data.dict()
sign = data.pop()
success = alipay.verify(data,sign)
if success and data[trade_status"] in (TRADE_SUCCESSTRADE_FINISHED):
self.change_order_status(data)
success')
4.善后事宜
@H_
301_4@
当一切都完成后,还有几件事情要做:
@H_
301_4@
1.修改订单状态
@H_
301_4@
2.扣除优惠劵 积分
@H_
301_4@
3.清空购物车数据和结算页面全部数据
4.将相关信息存到用户购买记录表中
AlipayResultView(APIView):
def change_order_status(self,data): 修改订单状态
with transaction.atomic():
out_trade_no = data.get()
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: [],1)">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[].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,1)">course:course,1)">:trade_no,1)">buy_type':1:now,1)">out_time:out_time,})
购物车redis数据删除
pipe.hdel(cart_%s self.request.user.id,course.id)
pipe.execute()
return res_data
create:
order_obj.coupon = coupon_id
order_obj.credit = credit
order_obj.save()


<template>
<div class=user-order">
<Vheader/>
<div main">
<div banner"></div>
<div profile">
<div profile-info">
<div avatar"><img newImg" width=100%" alt="" src=../../static/img/logo@2x.png"></div>
<span user-name">吴某某</span>
<span user-job">北京市 | 程序员</span>
</div>
<ul my-item">
<li>我的账户</li>
<li active">我的订单</li>
<li>个人资料</li>
<li>账号安全</li>
</ul>
</div>
<div user-data">
<ul nav">
<li order-info">订单</li>
<li course-expire">有效期</li>
<li course-price">课程价格</li>
<li real-price">实付金额</li>
<li order-status">交易状态</li>
<li order-do">交易操作</li>
</ul>
<div my-order-item" v-for=(order_obj,index) in order_list" :key=indexuser-data-header">
<span order-time">xxxx</span>
<span order-num">订单号:
<span my-older-number">xxxx</span>
</span>
</div>
<ul nav user-data-list(course_obj,course_index) in order_obj.order_detail_data">
<li ">
<img :src=course_obj.course_img"">
<div order-info-title">
<p course-title">xxxx</p>
<p price-service">xxxx</p>
</div>
</li>
<li ">xxxx</li>
<li btn btn2if=order_obj.get_order_status_display==='已支付'">去学习</span>
<span else-order_obj.get_order_status_display==='未支付'" @click=go_pay(order_obj.order_number)">去付款</span>
<span order_obj.get_order_status_display==='超时取消'">超时取消</span>
<span else>已取消</span>
</li>
</ul>
</div>
</div>
</div>
<Footer/>
</div>
</template>
<script>
import Vheader from ./common/Vheader"
import Footer ./common/Footer
export default{
name:Myorderreturn {
};
},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-items: center;
}
.order-time{
font-size: 12px;
display: inline-block;
margin-left: 20px;
}
.order-num{
font-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-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-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>
我的订单页面-初始化
{
path: '/myorder/'2.我的订单页面-后端接口
@H_301_4@users/urls.py
urlpatterns = [
path(rmyorder/301_4@users/views.py
MyOrderView(ListAPIView):
permission_classes = MyOrderModelSerializer
get_queryset(self):
return Order.objects.filter(user=self.request.user)
@H_301_4@user/serializers.py
MyOrderModelSerializer(serializers.ModelSerializer):
Meta:
model = Order
fields = [idorder_numberget_order_status_displayorder_detail_data']
@H_301_4@order/models.py
@H_301_4@在我的订单页面中,需要展示一些数据
Order(BaseModel):
order_detail_data(self):
获取所有课程详情对象
order_detail_objs = self.order_courses.all()
data_list = []
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,1)">course_name:order_detail.course.name,1)">expire_text:expire_text,1)">price:order_detail.price,1)">real_price: self.real_price,1)">discount_name:order_detail.discount_name,}
将每个课程的详情信息添加到一个列表里返回给前端
data_list.append(order_dict)
data_list
3.我的订单页面-前端
1.获取后端的订单数据
@H_301_4@Myorder.vue
js
get_order_data(){
检查当前访问者是否登录了!
let token = localStorage.token ||this.$settings.Host}/users/myorder/`,1)"> token
}
}).then((res)=>this.order_list = res.data;
}).{
})
<!-- html -->
<div ="my-order-item" v-for="(order_obj,index) in order_list" :key="index">
="user-data-header">
span ="order-time">{{order_obj.pay_time.replace('T',' ')}}</span="order-num">订单号:
="my-older-number">{{order_obj.order_number}}divul ="nav user-data-list"="(course_obj,course_index) in order_obj.order_detail_data"li ="order-info">
img :src="course_obj.course_img" alt=""="order-info-title">
p ="course-title">{{course_obj.course_name}}p="price-service">{{course_obj.discount_name}}li="course-expire">{{course_obj.expire_text}}="course-price">{{course_obj.price}}="real-price">{{course_obj.real_price}}="order-status">{{order_obj.get_order_status_display}}="order-do"="btn btn2" v-if="order_obj.get_order_status_display==='已支付'">去学习 v-else-if="order_obj.get_order_status_display==='未支付'" @click="go_pay(order_obj.order_number)">去付款="order_obj.get_order_status_display==='超时取消'">超时取消 v-else>已取消ul>
>
2.我的订单页面点击去付款
@H_301_4@>
go_pay(order_number){
let token = localStorage.token ||this.$settings.Host}/payment/alipay/?order_number=${order_number}`,1)"> headers:{
'Authorization':'jwt ' + token
}
}).then((res)=>{
location.href = res.data.url;
})..$message.error(error.response.data.msg);
})