• vue_drf之支付宝接口


      一、配置

      1,生成应用秘钥和公钥

      下载对应系统的秘钥生成工具: https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=105971&docType=1

      

      2,配置秘钥

      2.1 把我们生成的应用公钥放到支付宝上,把支付宝公钥复制一份

      2.2 存储我们应用私钥和支付宝公钥

      存储格式

      3,安装依赖

      pip install python-alipay-sdk --upgrade

      二、代码

      payment.vue

    <template>
      <div id="payment">
        <div class="payment">
          <el-container>
            <el-header><Header :current_state="current_state"/></el-header>
            <div class="content">
              <div class="top_title">结算中心</div>
              <div>
                <table class="table" >
                  <thead>
                    <tr>
                      <th width="8%"></th>
                      <th width="32%">课程</th>
                      <th width="30%">课程名称</th>
                      <th width="30%">课程价格</th>
                    </tr>
                  </thead>
                  <tbody> <!--v-for-start-->
                  <tr v-for="item in info">
                    <td width="8%"></td>
                    <td width="32%">
                      <img alt=" " class="product-img" :src="'http://127.0.0.1:8000'+item.course.course_img">
                    </td>
                    <td width="30%">
                      {{item.course.name}}
                    </td>
                    <td width="30%"><span>{{item.discount_price}}</span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="old_price">原价:{{item.course.price}}</span></td>
    
                  </tr>
                  </tbody>
                </table>
                <div class="nothing" v-show="info.length==1">&nbsp;</div>
                <div class="final">
                  <el-row>
                    <el-col :span="10">支付方式:<span class="method">
                      <img src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjlweCIgaGVpZ2h0PSIyOXB4IiB2aWV3Qm94PSIwIDAgMjkgMjkiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDQ5LjMgKDUxMTY3KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT7mlK/ku5jlrp08L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZGVmcz48L2RlZnM+CiAgICA8ZyBpZD0i56ys5LqM54mIIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8ZyBpZD0i6LSt5Lmw6K++56iLIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNDkyLjAwMDAwMCwgLTU4OC4wMDAwMDApIiBmaWxsPSIjNEE5MEUyIj4KICAgICAgICAgICAgPGcgaWQ9IuaUr+S7mOWunSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNDkyLjU0OTQ1MSwgNTg4LjU0OTQ1MSkiPgogICAgICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwLTMiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAuMDAwMDAwLCAwLjAxNTY5OCkiPgogICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNS44OTQ5MzksMTguMTQ2NDE1OSBDMTUuODk0OTM5LDE4LjE0NjQxNTkgMTMuODM2ODYzLDE5Ljc5MTMzODkgMTMuMDg4MTUwMSwyMC4yMjIyMTc2IEMxMi40MzAyNzA2LDIwLjYwMDc2OTggMTEuODEyNTAwOSwyMC44NTI3ODY4IDExLjI2MjI2MDgsMjEuMDI3NDM1MiBDMTAuNzExODUwNywyMS4yMDIwODM2IDEwLjIyOTQ0NjYsMjEuMzEyMDAzNCA5LjgxNTExNjYxLDIxLjM1NzE2MDUgQzkuNDAwODU0NjYsMjEuNDAyNDg3NiA5LjA1NzU5MjUyLDIxLjQzMTUzOSA4Ljc4NTYzNjM2LDIxLjQ0NDUxODcgQzguNzU5ODE1MTYsMjEuNDU3NDY0NSA4LjcyNzI5MjAxLDIxLjQ2NDA1NjMgOC42ODg1NDMxOSwyMS40NjQwNTYzIEw4LjM3NzY2ODEyLDIxLjQ2NDA1NjMgQzcuNjY1NDI0NjgsMjEuNDY0MDU2MyA2Ljk5NTMzMjA3LDIxLjM3OTg1OCA2LjM2NzI4ODIzLDIxLjIxMTY5OTUgQzUuNzM5MzEyNDIsMjEuMDQzNDczIDUuMTkyMjM2MjUsMjAuNzk0NjE2IDQuNzI1OTU3NjYsMjAuNDY0NzIwNyBDNC4yNTk3MTMxLDIwLjEzNDk2MTQgMy44OTA2NjM3NywxOS43MjEwNzE4IDMuNjE4ODc3NzIsMTkuMjIzMTg3OSBDMy4zNDY5ODk2LDE4LjcyNTI3IDMuMjEwOTQzNDksMTguMTQ2NDQ5OCAzLjIxMDk0MzQ5LDE3LjQ4NjkzMTIgQzMuMjIzOTA1MTIsMTYuOTE3NzYwOCAzLjM3OTEzODU0LDE2LjQwMDQ0MTMgMy42NzcwMTc5NiwxNS45MzQ5MDQ4IEMzLjk3NDc5NTMxLDE1LjQ2OTI2NjMgNC4zNjAwNzIyLDE1LjA3NDcxMDMgNC44MzI4NDg2MSwxNC43NTEzMDUgQzUuMzA1NDU0OTIsMTQuNDI4MTAzNCA1LjgyOTkwNzc3LDE0LjE4MjIwMjUgNi40MDYxNzMxMiwxNC4wMTQxNDU5IEM2Ljk4MjM3MDQ0LDEzLjg0NTk4NzQgNy41NjE3OTk2NiwxMy43NzQ5Mzg3IDguMTQ0NjMwODgsMTMuODAwODY0MiBDOC43MTQzOTg0MSwxMy44MjY1ODU3IDkuMjY0NjM4NDQsMTMuODk0NTA4NCA5Ljc5NTU4OTExLDE0LjAwNDM5NDEgQzEwLjMyNjUwNTgsMTQuMTE0NDQ5OCAxMC44MzQ4MzMxLDE0LjI0NjkzMTIgMTEuMzIwNDM1MSwxNC40MDIwNzYxIEMxMS44MDYxMDUxLDE0LjU1NzQyNDggMTIuMjY1NzQ5OCwxNC43MjU0ODE1IDEyLjY5OTYwNzMsMTQuOTA2NTUxOCBDMTMuMTMzNDY0OCwxNS4wODc2NTYxIDEzLjU0NDU5NjksMTUuMjYyMjAyNSAxMy45MzMwMzc2LDE1LjQzMDM2MTEgQzE0LjA4ODQ3NTEsMTUuNDk0OTUzOCAxNC4yNTAzNDI1LDE1LjU1OTc1MDQgMTQuNDE4Nzc1NywxNS42MjQ0NDUxIEMxNC41ODY5NzA3LDE1LjY4OTAzNzggMTQuNzYxODMzNywxNS43NTM2MzA2IDE0Ljk0MzA5MjQsMTUuODE4MzU5MiBDMTUuMzE4NzA3NiwxNS4zMzk3NDEgMTUuNjM1ODc2NCwxNC44NzEwMTA1IDE1Ljg5NDg3MDksMTQuNDExODI3OCBDMTYuMTUzODY1NCwxMy45NTI3MTMyIDE2LjM2NzU3OTMsMTMuNTMyNDAxNyAxNi41MzU4NDI0LDEzLjE1MDgyNTUgQzE2LjcwNDIwNzUsMTIuNzY5MTgxMyAxNi44MzM3NTU4LDEyLjQ0OTIwNzcgMTYuOTI0NDUzMiwxMi4xOTAzNjExIEMxNy4wMTUwNDg2LDExLjkzMTg1NDMgMTcuMDczMTg4OCwxMS43NTA3NSAxNy4wOTkxNDYxLDExLjY0NzI4NjEgTDguNjY5MTE3NzUsMTEuNjQ3Mjg2MSBMOC42NjkxMTc3NSwxMC43MTU5MDcxIEwxMi42NTEwOTQ3LDEwLjcxNTkwNzEgTDEyLjY1MTA5NDcsOC41NjI1MzI4OSBMNy4xMTUxODQ2NSw4LjU2MjUzMjg5IEw3LjExNTE4NDY1LDcuNjMxMzkxNzkgTDEyLjY1MTA5NDcsNy42MzEzOTE3OSBMMTIuNjUxMDk0Nyw1Ljg4NTMxNTM0IEMxMi42NTEwOTQ3LDUuNzY4OTczNjcgMTIuNzE1NzY2OCw1LjY3NTMyOTUgMTIuODQ1MzE1MSw1LjYwNDA3NzAyIEMxMi45NzQ3OTUzLDUuNTMzMDYyMzkgMTMuMTIzNzAxLDUuNDg0NTQxMzkgMTMuMjkyMTY4Miw1LjQ1ODY0OTkzIEMxMy40ODYyODY1LDUuNDE5Nzc4NzYgMTMuNzA2NDk4Miw1LjQwMDM3NzE2IDEzLjk1MjM5NSw1LjQwMDM3NzE2IEwxNS40NDgxMTk4LDUuNDAwMzc3MTYgTDE1LjQ0ODExOTgsNy42MzEzOTE3OSBMMjEuMTM5NDY3NCw3LjYzMTM5MTc5IEwyMS4xMzk0Njc0LDguNTYyNjM0ODMgTDE1LjQ0ODA4NTgsOC41NjI2MzQ4MyBMMTUuNDQ4MDg1OCwxMC43MTYwMDkxIEwxOS45NzA2NzUxLDEwLjcxNjAwOTEgQzE5Ljk3MTcyOTcsMTAuNzA5NzIzMSAxOS45NzI5ODg1LDEwLjcwMzE5OTIgMTkuOTczOTc1MSwxMC42OTY3MDk0IEwxOS45NzM5NzUxLDEwLjcxNjAwOTEgTDE5Ljk3MDY3NTEsMTAuNzE2MDA5MSBDMTkuODc5NjAzNSwxMS4yOTI0MTY4IDE5LjcyNTU5NDgsMTEuOTAwMTg2NSAxOS41MDc2OTY1LDEyLjUzOTc5MzkgQzE5LjMyNjUwNTgsMTMuMDk1OTE2NiAxOS4wNTc5MTc2LDEzLjcyOTY3OTcgMTguNzAxNzI3OCwxNC40NDA5MTMzIEMxOC4zNDU2MDYxLDE1LjE1MjM4NDcgMTcuODc2MTk3NywxNS44ODMwNTM5IDE3LjI5MzQzNDUsMTYuNjMzMTI0NyBDMTcuMjkzNDM0NSwxNi42MzMxMjQ3IDIxLjc1MTM4NTcsMTguNzYyNjgwMSAyNi40NTczMDksMTkuNTEyOTU0OCBDMjcuMjM4OTE5MywxNy43ODE3MjY5IDI3LjY3Mzk2NzUsMTUuODYwNzMwMiAyNy42NzM5Njc1LDEzLjgzODE3MjMgQzI3LjY3Mzg5OTUsNi4yMDU2OTY2NSAyMS40Nzg5NTMzLDAuMDE4MjgzNzE4NyAxMy44MzY4NjMsMC4wMTgyODM3MTg3IEM2LjE5NDk0MjczLDAuMDE4MjgzNzE4NyAtMy40MDIwMDExMWUtMDYsNi4yMDU2OTY2NSAtMy40MDIwMDExMWUtMDYsMTMuODM4Mzc2MiBDLTMuNDAyMDAxMTFlLTA2LDIxLjQ3MDg4NTkgNi4xOTQ5NDI3MywyNy42NTgzMzI4IDEzLjgzNjkzMSwyNy42NTgzMzI4IEMxOC41NzczNTA2LDI3LjY1ODMzMjggMjIuNzYwNTkwMSwyNS4yNzc0NzM5IDI1LjI1NDI5MjYsMjEuNjQ3Nzc2OSBDMjIuNzE5NzY2LDIxLjA5NzA1NjcgMjAuMDI4MjM3LDE5Ljk4MDQ2MjEgMTUuODk0OTM5LDE4LjE0NjQxNTkgTDE1Ljg5NDkzOSwxOC4xNDY0MTU5IFoiIGlkPSJGaWxsLTEiPjwvcGF0aD4KICAgICAgICAgICAgICAgIDwvZz4KICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik01LjAxNzM2OTg3LDE3LjU0MTM2MSBDNC45NjUzMTkyMiwxOS45MTI3NCA3LjY1MTgxMzI3LDIwLjA4ODkxNzQgOC4wNjU5NzMxNiwyMC4xMDE1NTczIEM5LjQzNTAwNzM3LDIwLjE0Mzc1ODQgMTAuNTQwNzYwNSwxOS42MjcxNTI0IDExLjQwMjAxMTcsMTkuMTE2Mjg4OCBDMTIuMjYyOTkwNywxOC42MDU0NTkyIDEzLjY2NzYwOTksMTcuMjg2MTE2MSAxMy43MTk0OTA1LDE3LjI0MDc1NTEgQzEzLjc3MTIwMDksMTcuMTk1NDI4IDEzLjgyMzA0NzQsMTcuMTQ2OTc1IDEzLjg3NDc5MTksMTcuMDk1MjYgQzEzLjQ5OTI0NDgsMTYuODg4MjY0MyAxMy4xMzAxOTU0LDE2LjY5NzQ3NjIgMTIuNzY3Njc3OSwxNi41MjI4OTU3IEMxMi40MDUxNjA1LDE2LjM0ODM0OTIgMTAuNTYzMDA5NiwxNS4zNjAxOTI1IDguOTI4MTQyODgsMTUuMTkxMTUwNSBDNS44MzIzMTk3OSwxNC44NzA4MzcyIDUuMDMzMDUzMTEsMTYuODIwMDM1OSA1LjAxNzM2OTg3LDE3LjU0MTM2MSBMNS4wMTczNjk4NywxNy41NDEzNjEgWiIgaWQ9IkZpbGwtNCI+PC9wYXRoPgogICAgICAgICAgICA8L2c+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4=" alt="" class="img1" _v-1663bd96="">
                    <span class="right">支付宝</span></span></el-col>
                    <el-col :span="4">&nbsp;</el-col>
                    <el-col :span="6">
                      <span style="margin-right: 62px">实付款:<span style="font-size: 36px">¥{{order.total_price}}</span></span>
                    </el-col>
                    <el-col :span="4">
                      <button class="go-charge-btn cursors" @click="pay">立即支付</button>
                    </el-col>
                  </el-row>
                </div>
              </div>
            </div>
            <Footer/>
          </el-container>
        </div>
      </div>
    </template>
    
    <script>
      import Header from '../common/header'
      import Footer from '../common/footer'
      export default {
        name:'payment',
        data:function () {
          return {
            info:[],
            current_state:0,
            token: localStorage.token || sessionStorage.token,
            order:{},
          }
        },
        components:{
          Header,Footer
        },
        methods:{
          pay:function () {
            this.$axios.get('http://127.0.0.1:8000/pay/'+this.order.id,{headers:{
                // 附带已经登录用户的jwt token 提供给后端,一定不能疏忽这个空格
                'Authorization':'JWT '+this.token
              },
              responseType:"json",
              withCredentials: true,
            }).then(function (res) {
              window.location.href=res.data.alp_url
            }).catch(function (error) {
              console.log(error.response)
            })
          }
        },
        created:function () {
          let _this=this;
          this.$axios.get('http://127.0.0.1:8000/order/payment',{headers:{
                // 附带已经登录用户的jwt token 提供给后端,一定不能疏忽这个空格
                'Authorization':'JWT '+this.token
              },
              responseType:"json",
              withCredentials: true,
            })
            .then(function (res) {
              _this.order=res.data;
              _this.info=res.data.order_course;
            }).catch(function (error) {
            console.log(error.response);
          })
        }
    
      }
    </script>
    
    <style scoped>
    .el-header,.el-footer{
      padding: 0;
    }
    .el-header{
      height: 80px !important;
    }
    .nothing{
      height: 155px;
    }
    .content{
      width: 1200px;
      margin: 0 auto;
    }
    .top_title{
      margin: 25px 0;
      font-size: 18px;
      color: #666;
    }
    .table {
      width: 100%;
      max-width: 100%;
      margin-bottom: 20px;
    }
    table {
      background-color: transparent;
      border-spacing: 0;
      border-collapse: collapse;
    }
    thead {
      text-align: left;
      height: 80px;
      line-height: 80px;
      background-color: #F7F7F7;
      color: #333;
      font-size: 14px;
    }
    .product-img{
      width: 175px;
      height: 115px;
      margin-right: 35px;
      vertical-align: middle;
    }
    tbody tr{
      height: 250px;
    }
    .final{
      width: 1200px;
      height: 80px;
      background-color: #F7F7F7;
      line-height: 80px;
      margin-bottom: 100px;
    }
    .go-charge-btn{
      width: 200px;
      height: 81px;
      outline: none;
      border: none;
      background: #ffc210;
      font-size: 18px;
      color: #fff;
    }
    .cursors{
      cursor: pointer;
    }
    .method{
      display: inline-block;
      width: 118px;
      height: 46px;
      border: 1px solid orange;
      line-height: 46px;
      position: relative;
    }
    .method img{
      position: absolute;
      top: 8.5px;
      left: 20px;
    }
    .method .right{
      margin-left: 60px;
    }
    .old_price{
      text-decoration: line-through;
      font-size: 14px;
      color: #9b9b9b;
    }
    </style>
    View Code

      发起支付的逻辑,上面payment.vue的js代码

    #向服务器发起支付请求
    pay:function () { this.$axios.get('http://127.0.0.1:8000/pay/'+this.order.id,{headers:{ // 附带已经登录用户的jwt token 提供给后端,一定不能疏忽这个空格 'Authorization':'JWT '+this.token }, responseType:"json", withCredentials: true, }).then(function (res) { window.location.href=res.data.alp_url }).catch(function (error) { console.log(error.response) }) }

      settings.py配置

    # 支付宝
    ALIPAY_APP_ID="xxxxxxxx" # 应用ID
    APLIPAY_APP_NOTIFY_URL = None      # 应用回调地址[支付成功以后,支付宝返回结果到哪一个地址下面]
    APP_PRIVATE_KEY_PATH = os.path.join(BASE_DIR,"lufei_drf/apps/Pay/keys/app_private_key.pem")   #应用私钥的路径
    ALIPAY_PUBLIC_KEY_PATH = os.path.join(BASE_DIR,"lufei_drf/apps/Pay/keys/alp_public_key.pem")   #支付宝公钥的路径
    ALIPAY_DEBUG = True
    # APIPAY_GATEWAY="https://openapi.alipay.com/gateway.do"               #这是上线后,真实的支付路径
    APIPAY_GATEWAY="https://openapi.alipaydev.com/gateway.do"                  #这是沙箱环境的支付宝提供的路径
    ALIPAY_RETURN_URL = "http://localhost/pay_success"                   #这是支付成功后,支付宝最后跳转的页面路径
    ALIPAY_NOTIFY_URL = "http://api.lufei.cn:8000/pay_success"

      views.py

    from datetime import datetime
    
    from django.conf import settings
    from django.shortcuts import render
    
    # Create your views here.
    from rest_framework import status
    from rest_framework.permissions import IsAuthenticated
    from rest_framework.response import Response
    from rest_framework.views import APIView
    from Order.models import *
    from alipay import AliPay
    import os
    
    class PayView(APIView):
        """支付宝"""   #会接受上面我们发送的请求,然后向支付宝请求,完成支付,跳转到我们提供的页面
        permission_classes = [IsAuthenticated]
        def get(self, request,pk):
            """获取支付链接"""
            # 判断订单信息是否正确
            try:
                order = Order.objects.get(id=pk, user=request.user,
                                          order_status=0, )
            except Order.DoesNotExist:
                return Response({'message': '订单信息有误'}, status=status.HTTP_400_BAD_REQUEST)
    
            # 构造支付宝支付链接地址
            alipay = AliPay(
                appid=settings.ALIPAY_APP_ID,
                app_notify_url=None,  # 默认回调url
                app_private_key_path=settings.APP_PRIVATE_KEY_PATH,
                alipay_public_key_path=settings.ALIPAY_PUBLIC_KEY_PATH, # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
                sign_type="RSA2",  # RSA 或者 RSA2
                debug=settings.ALIPAY_DEBUG
            )
    
            order_string = alipay.api_alipay_trade_page_pay(
                out_trade_no=order.id,
                total_amount=str(order.total_price),
                subject=order.order_desc,
                return_url=settings.ALIPAY_RETURN_URL,
            )
            alp_url = settings.APIPAY_GATEWAY + "?" + order_string
            return Response({'alp_url': alp_url}, status=status.HTTP_201_CREATED)
    
    #支付宝在支付成功后会跳转到我们提供的页面路径,还会发送一个post请求,但开发中,没有公网ip,所以接收不到post请求,从而导致后端不知道是否支付成功。于是,当支付宝最后跳转到我们的页面时,我们自动发送一个get请求,
    把支付成功的消息告诉后端,后端接收到支付成功的消息后,把数据库数据进行修改。在项目上线后,有了公网ip,我们就可以接收到支付宝的post请求,就不需要我们自己发送get请求了,我们就可以把下面的get请求改成post请求,逻辑都是差不多的

    class Pay_successView(APIView): permission_classes = [IsAuthenticated] def get(self,request): data=request.query_params.dict() # sign 不能参与签名验证 signature = data.pop("sign") alipay = AliPay( appid=settings.ALIPAY_APP_ID, app_notify_url=None, # 默认回调url # 应用私钥 app_private_key_path=settings.APP_PRIVATE_KEY_PATH, # 支付宝的公钥, alipay_public_key_path=settings.ALIPAY_PUBLIC_KEY_PATH, sign_type="RSA2", # 密码加密的算法 # 开发时属于调试模式 debug=settings.ALIPAY_DEBUG # 默认False ) # verify验证支付结果,布尔值 success = alipay.verify(data, signature) if success: order_id=data.get('out_trade_no') print(order_id) pay_time=datetime.now().strftime("%Y-%m-%d %H:%M:%S") order=Order.objects.filter(id=order_id) print(order) order.update(order_status=1,pay_time=pay_time) return Response({ "amount": order[0].order_course.count(), "paytime": order[0].pay_time, "price": order[0].total_price, "desc": order[0].order_desc, }, status=status.HTTP_200_OK) return Response({'message':'支付失败'})
  • 相关阅读:
    Manage It! Part 2 规划和组织项目
    【转载】如何迅速成为Java高手
    Eclipse中最常用的快捷键
    向SQL Server全文索引进军,艰难历程
    数据库函数整理
    ASP.NET MVC简单编程篇(一)
    SQL Server存储过程及高级应用
    定义和赋值的区别 构造函数和拷贝构造函数
    SQL Server 2000
    Coustom web control 自定义控件
  • 原文地址:https://www.cnblogs.com/12345huangchun/p/10659323.html
Copyright © 2020-2023  润新知