• day88:luffy:支付宝同步结果通知&接收异步支付结果&用户购买记录&我的订单


    目录

    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{
         100%;
        height: 324px;
        background: url(../../static/img/my_bkging.0648ebe.png) no-repeat;
        background-size: cover;
        z-index: 1;
    }
    .profile{
         1200px;
        margin: 0 auto;
    }
    .profile-info{
        text-align: center;
        margin-top: -80px;
    }
    .avatar{
         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;
         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{
         1200px;
        height: auto;
        margin: 0 auto;
        padding-top: 30px;
        border-top: 1px solid #e8e8e8;
        margin-bottom: 63px;
    }
    .nav{
         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;
       160px;
    }
    .nav .order-info{  325px; }
    .nav .course-expire{  60px; }
    .nav .course-price{  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- 100px;
        max-height: 75px;
        margin-right: 22px;
    }
    .course-title{
         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;
         60px;
        text-align: center;
    }
    .btn {
       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);
            })
  • 相关阅读:
    ORACLE 数据迁移到SQL SEVER2005的问题
    sql server性能分析检测数据库阻塞语句
    经典存储过程
    sql server性能分析查询死锁的sql语句
    sql server性能分析索引使用效率评估
    discuz!X2.5不改代码即可去掉网址后面的forum.php后缀
    discuz!X2.5伪静态设置
    详解ListView
    frameset、frame和iframe的区别
    android中的Context到底该怎么用
  • 原文地址:https://www.cnblogs.com/libolun/p/13964797.html
Copyright © 2020-2023  润新知