zoukankan      html  css  js  c++  java
  • AI学习吧-支付宝支付

    支付宝支付流程

    1、接收前端发过来的贝里数和结算金额

    2、检查贝里数是否够用

    3、获取结算中心的课程并应用优惠券

    4、应用未绑定课程的优惠券

    5、判断总价格减去优惠券价格是否等于实际支付金额

    6、生成订单

    7、生成去支付宝支付的链接

    支付宝支付详细流程

     表结构

     1 class Order(models.Model):
     2     """订单"""
     3     payment_type_choices = ((0, '微信'), (1, '支付宝'), (2, '优惠码'), (3, '贝里'))
     4     payment_type = models.SmallIntegerField(choices=payment_type_choices)
     5     payment_number = models.CharField(max_length=128, verbose_name="支付第3方订单号", null=True, blank=True)
     6     order_number = models.CharField(max_length=128, verbose_name="订单号", unique=True)  # 考虑到订单合并支付的问题
     7     user = models.ForeignKey("User")
     8     actual_amount = models.FloatField(verbose_name="实付金额")
     9 
    10     status_choices = ((0, '交易成功'), (1, '待支付'), (2, '退费申请中'), (3, '已退费'), (4, '主动取消'), (5, '超时取消'))
    11     status = models.SmallIntegerField(choices=status_choices, verbose_name="状态")
    12     date = models.DateTimeField(auto_now_add=True, verbose_name="订单生成时间")
    13     pay_time = models.DateTimeField(blank=True, null=True, verbose_name="付款时间")
    14     cancel_time = models.DateTimeField(blank=True, null=True, verbose_name="订单取消时间")
    15 
    16     class Meta:
    17         verbose_name_plural = "订单表"
    18 
    19     def __str__(self):
    20         return "%s" % self.order_number
    21 
    22 class OrderDetail(models.Model):
    23     """订单详情"""
    24     order = models.ForeignKey("Order")
    25 
    26     content_type = models.ForeignKey(ContentType)  # 可关联普通课程或学位
    27     object_id = models.PositiveIntegerField()
    28     content_object = GenericForeignKey('content_type', 'object_id')
    29 
    30     original_price = models.FloatField("课程原价")
    31     price = models.FloatField("折后价格")
    32     content = models.CharField(max_length=255, blank=True, null=True)  #
    33     valid_period_display = models.CharField("有效期显示", max_length=32)  # 在订单页显示
    34     valid_period = models.PositiveIntegerField("有效期(days)")  # 课程有效期
    35     memo = models.CharField(max_length=255, blank=True, null=True)
    36 
    37     def __str__(self):
    38         return "%s - %s - %s" % (self.order, self.content_type, self.price)
    39 
    40     class Meta:
    41         verbose_name_plural = "订单详细"
    42         unique_together = ("order", 'content_type', 'object_id')
    models.py

    逻辑

    默认-会校验用户是否登录,使用UserAuth
    
    数据格式:
    {
          course:
              {1:{choice_price_id:1,coupon_id:2},
                2:{choise_price_id:4,coupon_record_id:3}},
    
          global_coupon_id:3,
    
          beli:2000,
    
          total_money:2000
    }
    
    计算价格公式:(课程1原价格*课程1优惠券+课程2原价格*课程2优惠券)*通用优惠券-贝里/10
    
    1.接收前端来的数据
     获取用户的贝里数,课程的头像、名称、价格等信息,全局优惠券,总价格
    
    2.校验数据
        2.1校验贝里数是否在用户拥有的范围之内
        2.2校验课程信息
            2.2.1校验课程是否存在
            2.2.2校验价格策略
            2.2.3校验课程优惠券
            2.2.4计算优惠后的价格-按照三种优惠券分为三种计算方式,
        2.3校验通用优惠券的合法性
        2.4校验最终价格是否一致   #防止别人篡改支付宝的支付信息,让我们受害!!
    
    3.生成订单
        Order记录
        OrderDetail
    4.调支付宝接口
        利用支付宝的沙箱环境,使用公钥私钥一些知识,使用Alipay这个类中的逻辑,完成支付宝支付
    #payment.py

    from
    rest_framework.views import APIView from rest_framework.viewsets import ViewSetMixin,ModelViewSet from app01.utils.auth_class import UserAuth from app01.utils.response import BaseResponse from rest_framework.response import Response from app01.utils.exceptions import CommonException from app01.models import Course,CouponRecord,PricePolicy,CourseDetail import datetime class PaymentView(APIView,ViewSetMixin): """ 支付接口===订单接口: 1 接收数据 2 校验数据 3 生成订单(默认未支付状态) --- Order --- OrderDetail --- OrderDetail --- OrderDetail 4 调支付宝的支付接口: 返回的post:修改订单,修改优惠券,修改贝里 返回的get: 查看订单状态 """ authentication_classes = [UserAuth] def create(self, request, *args, **kwargs): ''' 请求发送的数据: { courses:{ 1:{ choose_price_id:1, coupon_id:2, }, 2:{ choose_price_id:4, coupon_record_id:3, }, }, global_coupon_id:3, beli:2000, total_money:2000 } 计算价格优先级 (课程1原价格*课程1优惠券+课程2原价格*课程2优惠券)*通用优惠券-贝里/10 ''' res=BaseResponse() try: # 1 获取数据 user = request.user beli = int(request.data.get("beli", 0)) courses_dict=request.data.get("courses") global_coupon_id=request.data.get("global_coupon_id") total_money=request.data.get("total_money") # 2 校验数据 # 2.1 校验内里数是否在登录用户实际拥有范围内 if user.beli < beli: raise CommonException("贝里数有问题!",1004) # 2.2 校验课程信息 now = datetime.datetime.now() course_price_list=[] for course_pk,course_info in courses_dict.items(): # 2.2.2 校验课程是否存在 course_obj=Course.objects.filter(pk=course_pk).first() if not course_obj: raise CommonException("课程不存在!",1002) if course_obj.status != 0: raise CommonException("课程未上线或者已下线!", 1005) # 2.2.3 校验价格策略 choose_price_id=course_info.get("choose_price_id") price_policy_all=course_obj.price_policy.all() if choose_price_id not in [obj.pk for obj in price_policy_all]: raise CommonException("价格策略错误!",1003) # 2.2.4 校验课程优惠券 coupon_record_id=course_info.get("coupon_record_id") coupon_record=CouponRecord.objects.filter(pk=coupon_record_id, user=user, status=0, coupon__valid_begin_date__lt=now, coupon__valid_end_date__gt=now, ).first() if not coupon_record: raise CommonException("优惠券有问题!", 1006) rel_course_obj=coupon_record.coupon.content_object if course_obj != rel_course_obj: raise CommonException("优惠券与课程不匹配!", 1007) # 计算优惠后的价格 price=PricePolicy.objects.filter(pk=choose_price_id).first().price rebate_price=self.cal_price(price,coupon_record) course_price_list.append(rebate_price) # 2.3 校验通用优惠券合法性 global_coupon_record = CouponRecord.objects.filter(pk=global_coupon_id, user=user, status=0, coupon__valid_begin_date__lt=now, coupon__valid_end_date__gt=now, ).first() if not global_coupon_record: raise CommonException("通用优惠券有问题!", 1009) # 2.4 校验最终价格是否一致 cal_price = self.cal_price(sum(course_price_list), global_coupon_record) final_price = cal_price - beli / 10 if final_price < 0: final_price = 0 if total_money != final_price : raise CommonException("支付价格有问题!", 1010) # 3 生成订单 # Order记录 # OrderDetail # OrderDetail # 4 调用支付宝接口 # alipay = ali() # 生成支付的url # query_params = alipay.direct_pay( # subject="Django课程", # 商品简单描述 # out_trade_no="x2" + str(time.time()), # 商户订单号 # total_amount=money, # 交易金额(单位: 元 保留俩位小数) # ) # # pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params) # # return redirect(pay_url) # 注意: # POST请求访问notify_url: # 更改订单 # 更改优惠券 # 更改贝里数 # GET请求return_url,用于页面的跳转展示 except CommonException as e: res.code=1004 res.error=e.error return Response(res.dict) def cal_price(self,price,coupon_record): """ price:原价格 coupon_record:优惠券对象 目的:计算优惠后的价格 :param price: :param coupon_record: :return: """ # 获取优惠券的类型 coupon_type=coupon_record.coupon.coupon_type if coupon_type == 0: # 立减券 money_equivalent_value=coupon_record.coupon.money_equivalent_value rebate_price=price - money_equivalent_value if rebate_price < 0 : rebate_price=0 elif coupon_type == 1: # 满减券 minimum_consume=coupon_record.coupon.minimum_consume if price > minimum_consume: money_equivalent_value = coupon_record.coupon.money_equivalent_value rebate_price=price-money_equivalent_value else: raise CommonException("优惠券不符合条件",1008) elif coupon_type == 2: off_percent=coupon_record.coupon.off_percent rebate_price=price*(off_percent/100) else: rebate_price=price return rebate_price

    支付逻辑

    from django.shortcuts import render, redirect, HttpResponse
    from utils.pay import AliPay
    import json
    import time
    
    def ali():
    
        # 沙箱环境地址:https://openhome.alipay.com/platform/appDaily.htm?tab=info
        app_id = "2016091100486897"
        # POST请求,用于最后的检测
        notify_url = "http://47.94.172.250:8804/page2/"
        # notify_url = "http://www.wupeiqi.com:8804/page2/"
        # GET请求,用于页面的跳转展示
        return_url = "http://47.94.172.250:8804/page2/"
        # return_url = "http://www.wupeiqi.com:8804/page2/"
        merchant_private_key_path = "keys/app_private_2048.txt"
        alipay_public_key_path = "keys/alipay_public_2048.txt"
    
        alipay = AliPay(
            appid=app_id,
            app_notify_url=notify_url,
            return_url=return_url,
            app_private_key_path=merchant_private_key_path,
            alipay_public_key_path=alipay_public_key_path,  # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥
            debug=True,  # 默认False,
        )
        return alipay
    
    
    def page1(request):
        if request.method == "GET":
    
            return render(request, 'page1.html')
        else:
            money = float(request.POST.get('money'))
            alipay = ali()
            # 生成支付的url
            query_params = alipay.direct_pay(
                subject="Django课程",  # 商品简单描述
                out_trade_no="x2" + str(time.time()),  # 商户订单号
                total_amount=money,  # 交易金额(单位: 元 保留俩位小数)
            )
    
            pay_url = "https://openapi.alipaydev.com/gateway.do?{}".format(query_params)
    
            return redirect(pay_url)
    
    
    def page2(request):
        alipay = ali()
        if request.method == "POST":
            # 检测是否支付成功
            # 去请求体中获取所有返回的数据:状态/订单号
            from urllib.parse import parse_qs
            body_str = request.body.decode('utf-8')
            post_data = parse_qs(body_str)
    
            post_dict = {}
            for k, v in post_data.items():
                post_dict[k] = v[0]
            print(post_dict)
    
            sign = post_dict.pop('sign', None)
            status = alipay.verify(post_dict, sign)
            print('POST验证', status)
    
            if status:
                # 修改订单状态
                pass
            return HttpResponse('POST返回')
    
        else:
            params = request.GET.dict()
            sign = params.pop('sign', None)
            status = alipay.verify(params, sign)
            print('GET验证', status)
            if status:
                 # 获取订单状态,显示给用户
                 return HttpResponse('支付成功')
  • 相关阅读:
    判断用户没有点击页面几秒后强制返回
    sql中sum()函数与case()函数的使用
    footer高度任意+js实现footer在底部
    让footer固定在底部(转自阮一峰老师博客)
    quartz不实现job接口的demo
    mybatis参数类型为map
    告诉你一个将 footer 保持在底部的最好方法
    多维数组介绍和使用
    数组
    数据类型
  • 原文地址:https://www.cnblogs.com/djfboai/p/10120158.html
Copyright © 2011-2022 走看看