一、配置
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> <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> <span class="old_price">原价:{{item.course.price}}span>td> tr> tbody> table> <div class="nothing" v-show="info.length==1"> 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"> 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>
发起支付的逻辑,上面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':'支付失败'})