zoukankan      html  css  js  c++  java
  • day87:luffy:结算页面积分&支付宝接口

    目录

    1.积分

    2.支付

    1.积分

    1.关于积分的表结构

    1.在user表中添加credit字段 + 设计一个积分的表结构

    user/models.py

    class User(AbstractUser):
        phone = models.CharField(max_length=16,null=True,blank=True)
        wechat = models.CharField(max_length=16,null=True,blank=True)
        
        # 为用户表添加积分字段 每个用户都有自己积分
        credit = models.IntegerField(default=0, blank=True, verbose_name="贝里")
     
    
    class Credit(BaseModel):
        """积分流水"""
        OPERA_OPION = (
            (1, "赚取"),
            (2, "消费"),
        )
        user = models.ForeignKey("User", related_name="user_credit", on_delete=models.CASCADE, verbose_name="用户")
        opera = models.SmallIntegerField(choices=OPERA_OPION,verbose_name="操作类型")
        number = models.SmallIntegerField(default=0, verbose_name="积分数值")
    
        class Meta:
            db_table = 'ly_credit'
            verbose_name = '积分流水'
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return "%s %s %s 贝壳" % ( self.user.username, self.OPERA_OPION[self.opera][1], self.number )

    2.设置积分和现金的兑换比例

    constant.py

    CREDIT_MONEY = 10 # 10积分顶一块钱

    2.登录成功将积分回复给前端

    1.登录成功 给前端回复除了[用户名 id token],也要把[积分和计算公式]回复给前端

    user/utils.py

    def jwt_response_payload_handler(token, user=None, request=None):
        return {
            'token': token,
            'username': user.username,
            'id':user.id,
            
             # 登录成功将积分和兑换比例也要返回给前端
            'credit':user.credit,
            'credit_to_money':contains.CREDIT_MONEY,
        }

    2.前端将积分存起来

    login.vue

     if (this.remember){
                  localStorage.token = res.data.token;
                  localStorage.username = res.data.username;
                  localStorage.id = res.data.id;
                  
                  // 前端将积分存起来
                  localStorage.credit = res.data.credit;
                  localStorage.credit_to_money = res.data.credit_to_money;
                  sessionStorage.removeItem('token');
                  sessionStorage.removeItem('username');
                  sessionStorage.removeItem('id');
                  sessionStorage.removeItem('credit');
                  sessionStorage.removeItem('credit_to_money');
    
                }else {
                  sessionStorage.token = res.data.token;
                  sessionStorage.username = res.data.username;
                  sessionStorage.id = res.data.id;
                    
                  // 前端将积分存起来
                  sessionStorage.credit = res.data.credit;
                  sessionStorage.credit_to_money = res.data.credit_to_money;
                  localStorage.removeItem('token');
                  localStorage.removeItem('username');
                  localStorage.removeItem('id');
                  localStorage.removeItem('credit');
                  localStorage.removeItem('credit_to_money');
                }

    3.自行输入使用积分

    在input标签中绑定一个change方法 当input标签输入的值发生变化的时候 触发@change下对应的方法

    order.vue

    <!-- html -->
    <p class="discount-num2" v-else><span>总积分:xxxx,
        <el-input-number v-model="num" @change="handleChange" :min="0" :max="10000" label="描述文字"></el-input-number>,已抵扣xxx元,本次花费xxx积分</span></p>
    // js
    handleChange(value){
            // 如果用户在积分输入框中没有输入值,那么默认使用了0个积分
            if (!value){
              this.num = 0
            }
            console.log(value);
    
          },

    4.拿取积分

    order.vue

    get_credit(){
        // 从前端拿取积分和兑换比例
        this.credit = localStorage.getItem('credit') || sessionStorage.getItem('credit')
        this.credit_to_money = localStorage.getItem('credit_to_money') || sessionStorage.getItem('credit_to_money')
          },

    5.在页面展示出积分

    order.vue

    <!-- html -->
    <p class="discount-num2" v-else><span>总积分:{{credit}},

    6.计算可以使用的最大积分

    order.vue

    max_credit(){
        
        // 计算此课程能使用的最大积分数:比如100元的课程最多能使用1000积分
        let a = parseFloat(this.total_price) * parseFloat(this.credit_to_money);
        
        // 如果此课程能使用的最大积分数[1000]小于你的总积分数[1500]:那么你能使用的最大积分数就是[1000]
        if (this.credit >= a){
            return a
        // 如果此课程能使用的最大积分数[1000]大于你的总积分数[600],那么你能使用的最大积分数就是你剩余的积分数[600]    
        }else {
            return parseFloat(this.credit)
        }

    7.前端发送请求

    order.vue

    payhander(){
            let token = localStorage.token || sessionStorage.token;
            this.$axios.post(`${this.$settings.Host}/order/add_money/`,{
                  "pay_type":this.pay_type,
                  "coupon":this.current_coupon,
                  "credit":this.num, // 将积分提交给后端,让后端对积分做校验
    
            },{
              headers:{
                  'Authorization':'jwt ' + token
                }
            }).then((res)=>{
              this.$message.success('订单已经生成,马上跳转支付页面')
    
            }).catch((error)=>{
              this.$message.error(error.response.data.msg);
            })
    
    
    
          }

    8.后端对积分做校验

    order/serializers.py

    def validate(self, attrs):
        
        # 获取前端发过来的积分
        credit = attrs.get('credit')
        
        # 查询用户所拥有的的所有积分
        user_credit = self.context['request'].user.credit
        
        # 如果前端积分大于用户积分,抛出异常
        if credit > user_credit:  
            raise serializers.ValidationError('积分超上限了,别乱搞')
    
        return attrs
    
    def create(self, validated_data):
        
        # 积分判断
        credit = float(validated_data.get('credit',0)) # 获取前端发过来的验证之后的积分
        if credit > constants.CREDIT_MONEY * total_real_price: # 如果使用积分超过你此课程能使用的最大积分
            transaction.savepoint_rollback(sid) # 回滚到断点处
            raise serializers.ValidationError('使用积分超过了上线,别高事情')
            
        # 积分计算
        total_real_price -= credit / contains.CREDIT_MONEY

    2.支付

    1.准备工作

    1.安装python-alipay-sdk

    pip install python-alipay-sdk --upgrade

    2.创建应用payment,并配置INSTALLAPP

    python3 manage.py startapp payment

    3.在payment应用目录创建keys文件夹

    2.公钥和私钥

    1.OpenSSL命令下生成私钥

    openssl
    OpenSSL> genrsa -out app_private_key.pem 2048   # 生成私钥到指定文件中

    2.OpenSSL命令下生成公钥

    openssl
    OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem  # 导出公钥

    3.沙箱环境应用公钥要更换成自己在openssl生成的应用公钥

    4.支付宝公钥存到alipay_public_key.pem中

    3.为支付页面配置路由

    payment/urls.py

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

    4.视图

    payment/views.py

    from alipay import AliPay, DCAliPay, ISVAliPay
    from alipay.utils import AliPayConfig
    
    
    class AlipayView(APIView):
    
        def get(self,request):
            order_number = request.query_params.get('order_number')
            order_obj = Order.objects.get(order_number=order_number)
    
    
            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
            )
    
            order_string = alipay.api_alipay_trade_page_pay(
                out_trade_no=order_obj.order_number,
                total_amount=float(order_obj.real_price),
                subject=order_obj.order_title,
                return_url=settings.ALIAPY_CONFIG['return_url'],
                notify_url=settings.ALIAPY_CONFIG['notify_url'] # 可选, 不填则使用默认notify url
            )
    
            url = settings.ALIAPY_CONFIG['gateway_url'] + order_string
    
            return Response({'url': url})

    5.支付宝配置信息

    dev.py

    # 支付宝配置信息
    ALIAPY_CONFIG = {
        "gateway_url": "https://openapi.alipaydev.com/gateway.do?",  # 沙箱支付宝网关地址
        "appid": "2016093000633754",  # 沙箱中那个应用id
        "app_notify_url": None,
        "app_private_key_path": os.path.join(BASE_DIR, "apps/payment/keys/app_private_key.pem"),
    
        "alipay_public_key_path": os.path.join(BASE_DIR, "apps/payment/keys/alipay_public_key.pem"),
        "sign_type": "RSA2",
        "debug": False,
        "return_url": "http://www.lycity.com:8080/payment/result",  # 同步回调地址
        "notify_url": "http://www.lyapi.com:8001/payment/result",  # 异步结果通知

    6.前端发送请求获取到一个支付页面的url

    payhander(){
    
            this.$axios.post(`${this.$settings.Host}/order/add_money/`,{
                 
            }).then((res)=>{
              this.$message.success('订单已经生成,马上跳转支付页面')
              let order_number = res.data.order_number
    
              this.$axios.get(`${this.$settings.Host}/payment/alipay/?order_number=${order_number}`)
                .then((res)=>{
                  // res.data :  alipay.trade...?a=1&b=2....
                  location.href = res.data.url;
    
                })
    
            }).catch((error)=>{
              this.$message.error(error.response.data.msg);
            })
    
    
    
          }

    7.支付成功的页面-初始化

    Success.vue

    <template>
      <div class="success">
        <Header/>
        <div class="main">
            <div class="title">
    <!--          <img src="../../static/images/right.svg" alt="">-->
              <div class="success-tips">
                  <p class="tips1">您已成功购买 1 门课程!</p>
                  <p class="tips2">你还可以加入QQ群 <span>747556033</span> 学习交流</p>
              </div>
            </div>
            <div class="order-info">
                <p class="info1"><b>付款时间:</b><span>2019/04/02 10:27</span></p>
                <p class="info2"><b>付款金额:</b><span >0</span></p>
                <p class="info3"><b>课程信息:</b><span><span>《Pycharm使用秘籍》</span></span></p>
            </div>
            <div class="wechat-code">
    <!--          <img src="../../static/image/server.cf99f78.png" alt="" class="er">-->
    <!--          <p><img src="../../static/image/tan.svg" alt="">重要!微信扫码关注获得学习通知&amp;课程更新提醒!否则将严重影响学习进度和课程体验!</p>-->
            </div>
            <div class="study">
              <span>立即学习</span>
            </div>
        </div>
        <Footer/>
      </div>
    </template>
    
    <script>
      import Header from "./common/Header"
      import Footer from "./common/Footer"
      export default{
        name:"Success",
        data(){
          return {
            current_page:0,
          };
        },
        created(){
          // 把地址栏上面的支付结果,转发给后端
    
        },
        components:{
          Header,
          Footer,
        }
      }
    </script>
    
    <style scoped>
    .success{
      padding-top: 80px;
    }
    .main{
        height: 100%;
        padding-top: 25px;
        padding-bottom: 25px;
        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 .tips1{
        font-size: 22px;
        color: #000;
    }
    .title .tips2{
        font-size: 16px;
        color: #4a4a4a;
        letter-spacing: 0;
        text-align: center;
        margin-top: 10px;
    }
    .title .tips2 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;
    }
    .wechat-code{
        display: flex;
        -ms-flex-align: center;
        align-items: center;
        padding: 25px 40px;
        border-bottom: 1px solid #f2f2f2;
    }
    .wechat-code>img{
        width: 100px;
        height: 100px;
        margin-right: 15px;
    }
    .wechat-code p{
        font-size: 14px;
        color: #d0021b;
        display: -ms-flexbox;
        display: flex;
        -ms-flex-align: center;
        align-items: center;
    }
    .wechat-code p>img{
        width: 16px;
        height: 16px;
        margin-right: 10px;
    }
    .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>
    支付成功的页面-初始化

    注册Success组件

    {
          path: '/payment/result/',   
          component: Success
        },
  • 相关阅读:
    万能的everything彻底解决mysql问题
    乱码问题
    机器学习学习规划
    NG机器学习笔记
    书籍与博客
    技术规划
    反置数
    多个接口出现同名函数的处理-转
    接口-imploements-委托
    接口使用中,对象生命周期管理-转
  • 原文地址:https://www.cnblogs.com/libolun/p/13960683.html
Copyright © 2011-2022 走看看