vue_drf之支付宝接口

Posted 12345huangchun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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:支付失败})

 

以上是关于vue_drf之支付宝接口的主要内容,如果未能解决你的问题,请参考以下文章

C#版支付宝手机网站支付接口API之(转)

Android 开发之Android 应用程序如何调用支付宝接口

呕心之作:支付宝的手机网站支付接口的应用

支付宝手机网站支付接口API之C#版

支付宝支付之App支付

wemall app商城源码Android之支付宝通知处理类