支付宝支付
1. python的垃圾回收(gc)2.python3 的八大数据类型3.Python 七类常见运算符4.python流程控制5.字符编码与字节流二进制补码形式6.python 之with open7.python 函数之传参8.python 函数之作用域、闭包9. python 装饰器10.递归、二分查找、冒泡排序11.三元表达式、列表、字典生成式、匿名函数12.迭代器和生成器、异常捕获13.python 常用的内置函数14.python模块的导入15.python 之 re (regexp expression)16.python 之时间模块17.python 随机模块random18.python json序列化模块19.hashlib 模块20.python 之logging 模块21.Python 深拷贝和浅拷贝详解22.面向对象编程之类和对象的定义23.面向对象编程之绑定方法、掩藏属性、装饰器24.面向对象之对象的三大特性25.python 魔术方法26.python 之反射27.Mixins机制和元类28.python 之http、tcp/ip29.基于tcp协议的socket编程30.python 并发编程之进程31.python 并发编程之线程32.GIL锁,互斥锁33.python并发编程之协程34.进程池和线程池35.MacOS13 m1 安装 mysql8.0.3236.mysql8 增删改查、约束条件37.mysql8 查询关键字、多表关系38.多表联合查询、脚本使用pymysql39.mysql8 索引、视图、事务、存储过程、触发器40.前端三剑客之HTML41.前端三剑客之CSS( position位置固定)42.前端三剑客之JavaScript 43.html 之 jQuery库、阻止二次提交44.html 之 Bootstrap45.python 安装pip3、虚拟环境、pip3下载离线包46.开发设计模式之单例模式47.celery 任务队列、双写一致性、异步秒杀48.py操作七牛云存储、minio、fastdfs
49.支付宝支付
50.路飞学城上线51.python之任务调度(APScheduler和schedule)52.命令行帮助信息、手动和交互执行53.python之csv、openpyxl/pandas54.python 并发整理一、沙箱环境
1、逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # 1 前端点击立即购买---》向咱们后端发送请求---》咱们后端生成一个支付链接(微信,支付宝支付)---》跳到不同的支付链接地址---》输入支付宝账号密码付款(手机扫码付款)----》付款成功---》支付宝收到了我们的付款---》<br>跳转回我们自己的项目---》支付宝会调用咱们后端的某个接口通知我们付款成功---》我们收到通知,就把订单状态改为已经支付 # 2 不同的付款 - 微信支付:工商注册(营业执照),真正备案过的网址,没有测试环境 - 支付宝支付:只支持, 工商注册(企业),沙箱环境(没有商户号的前提下测试)、、 # 3 营业执照---》申请商家账号:2088102176466324---》使用商家账号,申请应用---》应用名称+应用id号 - 应用 id 号咱们付款需要 - 公司不需要你来做 - 最终:APPID 2016092000554611 # 4 使用支付宝sdk(第三方:官方的api封装的),生成支付链接 pip install python - alipay - sdk - - upgrade # 生成公钥 私钥----》支付宝工具帮咱们生成:https://opendocs.alipay.com/common/02kipl # 我们的公钥---》配置到支付宝平台---》生成一个支付宝公钥 # 我们用支付宝公钥+我们私钥(开放平台密钥工具) 做加密和认证 # https://opendocs.alipay.com/open/270/105898?pathHash=b3b2b667 |
2、代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | from alipay import AliPay, DCAliPay, ISVAliPay from alipay.utils import AliPayConfig # 支付宝网页下载的证书不能直接被使用,需要加上头尾 # 你可以在此处找到例子: tests/certs/ali/ali_private_key.pem app_private_key_string = open ( "./pri" ).read() alipay_public_key_string = open ( "./aipay_pub" ).read() print (app_private_key_string) alipay = AliPay( appid = "9021000130615836" , app_notify_url = None , # 默认回调 url app_private_key_string = app_private_key_string, # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥, alipay_public_key_string = alipay_public_key_string, sign_type = "RSA2" , # RSA 或者 RSA2 debug = False , # 默认 False verbose = False , # 输出调试数据 config = AliPayConfig(timeout = 15 ) # 可选,请求超时时间 ) order_string = alipay.api_alipay_trade_page_pay( out_trade_no = "0x1212" , total_amount = 999 , subject = '性感内衣' , return_url = "https://example.com" , notify_url = "https://example.com/notify" # 可选,不填则使用默认 notify url ) # order_string = alipay.api_alipay_trade_wap_pay( # out_trade_no="20161112", # total_amount=0.01, # subject='性感内衣', # return_url="https://example.com", # notify_url="https://example.com/notify" # 可选,不填则使用默认 notify url # ) print ( 'https://openapi-sandbox.dl.alipaydev.com/gateway.do?' + order_string) |
3、链接
App 支付客户端 DEMO&SDK - 支付宝文档中心 (alipay.com)
支付宝支付 - 刘清政 - 博客园 (cnblogs.com)
alipay/alipay/__init__.py at master · fzlee/alipay (github.com)
二、封装进django项目
1、目录结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | alipay_pay pem alipay_public_key.pem app_private_key.pem __init__.py pay.py settings.py ##### __init__.py from .pay import alipay, gateway ###### pay.py from alipay import AliPay from . import settings # 支付对象 alipay = AliPay( appid = settings.APP_ID, app_notify_url = None , app_private_key_string = settings.APP_PRIVATE_KEY_STRING, alipay_public_key_string = settings.ALIPAY_PUBLIC_KEY_STRING, sign_type = settings.SIGN, debug = settings.DEBUG ) # 支付网关 gateway = settings.GATEWAY ### settings.py import os # 应用私钥 APP_PRIVATE_KEY_STRING = open (os.path.join(os.path.dirname(os.path.abspath(__file__)), 'pem' , 'app_private_key.pem' )).read() # 支付宝公钥 ALIPAY_PUBLIC_KEY_STRING = open (os.path.join(os.path.dirname(os.path.abspath(__file__)), 'pem' , 'alipay_public_key.pem' )).read() # 应用ID APP_ID = '9021000129694319' # 加密方式 SIGN = 'RSA2' # 是否是支付宝测试环境(沙箱环境),如果采用真是支付宝环境,配置False DEBUG = True # 支付网关 GATEWAY = 'https://openapi-sandbox.dl.alipaydev.com/gateway.do?' if DEBUG else 'https://openapi.alipay.com/gateway.do?' |
2、表关系
1 2 3 | # 分析订单板块需要的表 - 订单表:订单名称,订单总价格,订单状态。。。。 - 订单详情表:一个订单有多个详情 |
models
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | from django.db import models # Create your models here. from user.models import User from courses.models import Course class Order(models.Model): """订单模型""" status_choices = ( ( 0 , '未支付' ), ( 1 , '已支付' ), ( 2 , '已取消' ), ( 3 , '超时取消' ), ) pay_choices = ( ( 1 , '支付宝' ), ( 2 , '微信支付' ), ) subject = models.CharField(max_length = 150 , verbose_name = "订单标题" ) total_amount = models.DecimalField(max_digits = 10 , decimal_places = 2 , verbose_name = "订单总价" , default = 0 ) out_trade_no = models.CharField(max_length = 64 , verbose_name = "订单号" , unique = True ) #唯一的,生成支付链接,有个订单号 trade_no = models.CharField(max_length = 64 , null = True , verbose_name = "流水号" ) #支付宝支付流水账号 order_status = models.SmallIntegerField(choices = status_choices, default = 0 , verbose_name = "订单状态" ) pay_type = models.SmallIntegerField(choices = pay_choices, default = 1 , verbose_name = "支付方式" ) pay_time = models.DateTimeField(null = True , verbose_name = "支付时间" ) # 支付宝回调回来,会有付款时间 user = models.ForeignKey(User, related_name = 'order_user' , on_delete = models.DO_NOTHING, db_constraint = False , verbose_name = "下单用户" ) created_time = models.DateTimeField(auto_now_add = True , verbose_name = '创建时间' ) class Meta: db_table = "luffy_order" verbose_name = "订单记录" verbose_name_plural = "订单记录" def __str__( self ): return "%s - ¥%s" % ( self .subject, self .total_amount) class OrderDetail(models.Model): """订单详情""" order = models.ForeignKey(Order, related_name = 'order_courses' , on_delete = models.CASCADE, db_constraint = False , verbose_name = "订单" ) course = models.ForeignKey(Course, related_name = 'course_orders' , on_delete = models.CASCADE, db_constraint = False , verbose_name = "课程" ) price = models.DecimalField(max_digits = 6 , decimal_places = 2 , verbose_name = "课程原价" ) real_price = models.DecimalField(max_digits = 6 , decimal_places = 2 , verbose_name = "课程实价" ) class Meta: db_table = "luffy_order_detail" verbose_name = "订单详情" verbose_name_plural = "订单详情" def __str__( self ): try : return "%s的订单:%s" % ( self .course.name, self .order.out_trade_no) except : return super ().__str__() |
3、下单接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # 1 用户点击前端,立即购买按钮----》(判断是否登录,登录成功后)---》向后端提交一个下单请求---携带的数据:{courses:[1,],total_amount:99,subject:xx课程,pay_type:1}--->post到后端---》 校验: # 1)订单总价校验 # 2)生成订单号:唯一的 # 3)支付用户:request.user # 4)支付链接生成:pay_type---》支付宝支付链接 # 5)入库(两个表)的信息准备 保存 返回给前端支付链接 # 新增接口(两个表) # 1 问题一:多条的话,要写many=True courses = serializers.PrimaryKeyRelatedField(queryset = Course.objects. all (), many = True ) # 2 问题二:支付宝生成支付链接时,用float强转一下 |
序列化类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | from rest_framework import serializers from .models import Order, OrderDetail from courses.models import Course from rest_framework.validators import ValidationError import uuid from libs.alipay_pay import gateway, alipay # 校验, 保存(重写create) class PaySerializer(serializers.ModelSerializer): # courses 不是表的字段,需要重写 # courses = serializers.ListField() # 不用这个方式,一会换成别的 [1,4] courses = serializers.PrimaryKeyRelatedField(queryset = Course.objects. all (), many = True ) # [id为1的课程对象,id为4的课程对象] class Meta: model = Order fields = [ 'courses' , 'total_amount' , 'subject' , 'pay_type' ] def _check_amount( self , attrs): # 1 取出一个个课程,循环拿到总价格,跟传入的价格比较,如果一致,就ok,不一致就抛异常 course = attrs.get( 'courses' ) # PrimaryKeyRelatedField--->[课程对象,课程对象] total_amount = attrs.get( 'total_amount' ) total = 0 for course in course: total + = course.price if total_amount ! = total: raise ValidationError( '价格不一致' ) def _get_out_trade_no( self ): return str (uuid.uuid4()) def _get_user( self ): request = self .context.get( 'request' ) return request.user def _get_pay_url( self , out_trade_no, attrs): res = alipay.api_alipay_trade_page_pay( out_trade_no = out_trade_no, total_amount = float (attrs.get( 'total_amount' )), subject = attrs.get( 'subject' ), return_url = "https://example.com" , notify_url = "https://example.com/notify" # 可选,不填则使用默认 notify url ) return gateway + res def _before_create( self , pay_url, attrs, user, out_trade_no): # 1 把pay_url放到context中,供视图类中取 self .context[ 'pay_url' ] = pay_url # 2 往attrs中加数据,后续都要存到表中 attrs[ 'user' ] = user attrs[ 'out_trade_no' ] = out_trade_no def validate( self , attrs): # 1)订单总价校验 self ._check_amount(attrs) # 2)生成订单号:唯一的 out_trade_no = self ._get_out_trade_no() # 3)支付用户:request.user user = self ._get_user() # 4)支付链接生成:pay_type---》支付宝支付链接 pay_url = self ._get_pay_url(out_trade_no, attrs) # 5)入库(两个表)的信息准备 self ._before_create(pay_url, attrs, user, out_trade_no) return attrs def create( self , validated_data): # {courses,total_amount,subject,pay_type,user,out_trade_no} # 存到库中 courses = validated_data.pop( 'courses' ) # 1 Order表 order = Order.objects.create( * * validated_data) # 2 存订单详情表 for course in courses: OrderDetail.objects.create(order = order, course = course, price = course.price, real_price = course.price) return order |
视图类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | from rest_framework.viewsets import GenericViewSet from rest_framework.mixins import CreateModelMixin from .serializer import PaySerializer from .models import Order from utils.common_response import APIResponse from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework.permissions import IsAuthenticated class PayView(GenericViewSet, CreateModelMixin): # 加认证 authentication_classes = [JSONWebTokenAuthentication] permission_classes = [IsAuthenticated] serializer_class = PaySerializer def create( self , request, * args, * * kwargs): ser = self .get_serializer(data = request.data, context = { 'request' : request}) # ser = PaySerializer(data=request.data,context={'request':request}) ser.is_valid(raise_exception = True ) super ().perform_create(ser) pay_url = ser.context.get( 'pay_url' ) return APIResponse(msg = '下单成功' , pay_url = pay_url) |
路由
1 2 3 4 5 6 7 8 9 10 11 | from rest_framework.routers import SimpleRouter from .views import PayView router = SimpleRouter() # 127.0.0.1:8000/api/v1/orders/pay/---->post router.register( 'pay' , PayView, 'pay' ) urlpatterns = [ ] urlpatterns + = router.urls |
前端支付功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # 课程详情页 go_pay() { let token = this.$cookies.get( 'token' ) console.log(token) if (token) { this.$axios({ url: this.$settings.BASE_URL + 'orders/pay/' , method: 'post' , data: { "courses" : [this.course_id], "total_amount" : this.course_info.price, "subject" : this.course_info.name, "pay_type" : 1 }, headers: { 'Authorization' : 'jwt ' + token} }).then(res = > { if (res.data.code = = 100 ) { let pay_url = res.data.pay_url / / 当前页面中打开pay_url 地址 open (pay_url, '_self' ); } }) } else { this.$message( '您没有登录,请先登录' ) } }, |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # 前台路由 { path: '/pay/success' , name: 'success' , component: PaySuccessView }, # 支付宝回调地址 def _get_pay_url( self , out_trade_no, attrs): res = alipay.api_alipay_trade_page_pay( out_trade_no = out_trade_no, total_amount = float (attrs.get( 'total_amount' )), subject = attrs.get( 'subject' ), return_url = settings.RETURN_URL, # 回调前台 notify_url = settings.NOTIFY_URL # 回调后台 ) ## settings中 # 后台基URL BACKEND_URL = 'http://127.0.0.1:8000' # 前台基URL LUFFY_URL = 'http://127.0.0.1:8080' # 支付宝同步异步回调接口配置 # 后台异步回调接口 NOTIFY_URL = BACKEND_URL + "/order/success/" # 前台同步回调接口,没有 / 结尾 RETURN_URL = LUFFY_URL + "/pay/success" |
购买成功页面
PaySuccessView
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | <template> <div class = "pay-success" > <! - - 如果是单独的页面,就没必要展示导航栏(带有登录的用户) - - > <Header / > <div class = "main" > <div class = "title" > <div class = "success-tips" > <p class = "tips" >您已成功购买 1 门课程!< / p> < / div> < / div> <div class = "order-info" > <p class = "info" ><b>订单号:< / b><span>{{ result.out_trade_no }}< / span>< / p> <p class = "info" ><b>交易号:< / b><span>{{ result.trade_no }}< / span>< / p> <p class = "info" ><b>付款时间:< / b><span><span>{{ result.timestamp }}< / span>< / span>< / p> < / div> <div class = "study" > <span>立即学习< / span> < / div> < / div> < / div> < / template> <script> import Header from "@/components/Header" export default { name: "Success" , data() { return { result: {}, }; }, created() { / / url后拼接的参数:?及后面的所有参数 = > ?a = 1 &b = 2 / / console.log(location.search); / / 解析支付宝回调的url参数 let params = location.search.substring( 1 ); / / 去除? = > a = 1 &b = 2 let items = params.length ? params.split( '&' ) : []; / / [ 'a=1' , 'b=2' ] / / 逐个将每一项添加到args对象中 for (let i = 0 ; i < items.length; i + + ) { / / 第一次循环a = 1 ,第二次b = 2 let k_v = items[i].split( '=' ); / / [ 'a' , '1' ] / / 解码操作,因为查询字符串经过编码的 if (k_v.length > = 2 ) { / / url编码反解 let k = decodeURIComponent(k_v[ 0 ]); this.result[k] = decodeURIComponent(k_v[ 1 ]); / / 没有url编码反解 / / this.result[k_v[ 0 ]] = k_v[ 1 ]; } } / / 解析后的结果 console.log(this.result); / / 把地址栏上面的支付结果,再get请求转发给后端 this.$axios({ url: this.$settings.BASE_URL + 'orders/success/' + location.search, method: 'get' , }).then(response = > { if (response.data.code = = 100 ) { this.$message( '恭喜您购买成功' ) } else { alert(response.data.msg) } }).catch(() = > { console.log( '支付结果同步失败' ); }) }, components: { Header, } } < / script> <style scoped> .main { padding: 60px 0 ; 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 .tips { font - size: 26px ; color: #000; } .info 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; } .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> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | from utils.common_logger import logger from rest_framework.response import Response class PaySuccess(ViewSet): def list ( self , request, * args, * * kwargs): out_trade_no = request.query_params.get( 'out_trade_no' ) res = Order.objects. filter (out_trade_no = out_trade_no, order_status = 1 ).exists() if res: return APIResponse() else : return APIResponse(code = 101 , msg = '暂未收到你的付款,请稍后再试' ) # 支付宝回调用,修改订单状态的 def create( self , request, * args, * * kwargs): try : result_data = request.data. dict () out_trade_no = result_data.get( 'out_trade_no' ) signature = result_data.pop( 'sign' ) from libs.alipay_pay import alipay result = alipay.verify(result_data, signature) if result and result_data[ "trade_status" ] in ( "TRADE_SUCCESS" , "TRADE_FINISHED" ): # 完成订单修改:订单状态、流水号、支付时间 Order.objects. filter (out_trade_no = out_trade_no).update(order_status = 1 ) # 完成日志记录 logger.warning( '%s订单支付成功' % out_trade_no) return Response( 'success' ) else : logger.error( '%s订单支付失败' % out_trade_no) except : pass return Response( 'failed' ) # 支付宝没有收到success,在48小时内,给你发送8次 |