支付宝接入

支付宝接入

python-alipay-sdk
pycryptodome

一、电脑网站支付

1.1 获取支付宝密钥

沙箱网址

1.APPID
2.应用私钥
3.支付宝公钥

在这里插入图片描述

在这里插入图片描述

1.2 存放密钥

  1. 在与 settings.py 的同级目录下创建 pem 文件夹
  2. pem 文件夹下创建 app_private_key.pemalipay_public_key.pem
    • app_private_key.pem :存放应用私钥(选择非Java语言)
    • alipay_public_key.pem :存放支付宝公钥
  3. 注意存放格式!!!

在这里插入图片描述

支付宝公钥

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END PUBLIC KEY-----

应用私钥

-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAgZXESCF0wkPXCKjLxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END RSA PRIVATE KEY-----

1.3 配置 settings

settings.py

import os
# 应用私钥(选择非Java语言)pem 是文件夹名称
APP_PRIVATE_KEY_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'pem', 'app_private_key.pem')
# 支付宝公钥
ALIPAY_PUBLIC_KEY_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'pem', 'alipay_public_key.pem')# 沙箱应用的 APPID --> https://open.alipay.com/develop/sandbox/app
APP_ID = '20xxxx9'# 加密方式
SIGN = 'RSA2'# 是否是支付宝测试环境(沙箱环境),如果采用真是支付宝环境,配置False
DEBUG = True# 支付网关
GATEWAY = 'https://openapi.alipaydev.com/gateway.do' if DEBUG else 'https://openapi.alipay.com/gateway.do'BASE_URL = 'http://127.0.0.1:8000'  # dajngo 服务器
LUFFY_URL = 'http://127.0.0.1:3000'  # react 前端地址NOTIFY_URL = BASE_URL + "/order/success/"  # 付款后支付宝向 http://127.0.0.1:8000/order/success/ 发送post请求
RETURN_URL = LUFFY_URL + "/#/pay/success"  # 付款后支付宝向 http://127.0.0.1:3000/#/pay/success  发送get请求,请求前端的支付成功页面

在这里插入图片描述

理论知识(必看)

1.用户选中订单点击支付,向Dajngo后端发送请求,Django后端接口根据商品信息生成能够唤醒支付界面的 pay_url,前端接收并打开pay_url
2.用户登录支付宝,并成功支付后
3.第三方服务器【支付宝】向我们配置的return_url发送一个GET请求
4.同时向notify_url发送一个异步回调POST请求

注意

  • RETURN_URL 和 NOTIFY_URL 必须能够使用公网访问
  • 公网访问:其它电脑能访问你设置的 url
  • 借助内网穿透工具即可natapp内网穿透应用

auth.py

# 1、放行的网址
if request.path_info in ['/login/', '/register/', '/order/success/']:return

注意

如果你的Django项目设置了拦截的话,请放行notify_url地址,因为支付宝发送过来的请求不带认证令牌!

aliPayConfig.py

from datetime import datetime
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from urllib.parse import quote_plus
from urllib.parse import urlparse, parse_qs
from base64 import decodebytes, encodebytes
import jsonclass AliPay(object):"""支付宝支付接口(PC端支付接口)"""def __init__(self, appid, app_notify_url, app_private_key_path,alipay_public_key_path, return_url, debug=False):self.appid = appidself.app_notify_url = app_notify_urlself.app_private_key_path = app_private_key_pathself.app_private_key = Noneself.return_url = return_urlwith open(self.app_private_key_path) as fp:self.app_private_key = RSA.importKey(fp.read())self.alipay_public_key_path = alipay_public_key_pathwith open(self.alipay_public_key_path) as fp:self.alipay_public_key = RSA.importKey(fp.read())if debug is True:self.__gateway = "https://openapi.alipaydev.com/gateway.do"else:self.__gateway = "https://openapi.alipay.com/gateway.do"def direct_pay(self, subject, out_trade_no, total_amount, return_url=None, **kwargs):biz_content = {"subject": subject,"out_trade_no": out_trade_no,"total_amount": total_amount,"product_code": "FAST_INSTANT_TRADE_PAY",  # 默认不需要修改# "qr_pay_mode":4}biz_content.update(kwargs)data = self.build_body("alipay.trade.page.pay", biz_content, self.return_url)return self.sign_data(data)def build_body(self, method, biz_content, return_url=None):data = {"app_id": self.appid,"method": method,"charset": "utf-8","sign_type": "RSA2","timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),"version": "1.0","biz_content": biz_content}if return_url is not None:data["notify_url"] = self.app_notify_urldata["return_url"] = self.return_urlreturn datadef sign_data(self, data):data.pop("sign", None)# 排序后的字符串unsigned_items = self.ordered_data(data)unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)sign = self.sign(unsigned_string.encode("utf-8"))# ordered_items = self.ordered_data(data)quoted_string = "&".join("{0}={1}".format(k, quote_plus(v)) for k, v in unsigned_items)# 获得最终的订单信息字符串signed_string = quoted_string + "&sign=" + quote_plus(sign)return signed_stringdef ordered_data(self, data):complex_keys = []for key, value in data.items():if isinstance(value, dict):complex_keys.append(key)# 将字典类型的数据dump出来for key in complex_keys:data[key] = json.dumps(data[key], separators=(',', ':'))return sorted([(k, v) for k, v in data.items()])def sign(self, unsigned_string):# 开始计算签名key = self.app_private_keysigner = PKCS1_v1_5.new(key)signature = signer.sign(SHA256.new(unsigned_string))# base64 编码,转换为unicode表示并移除回车sign = encodebytes(signature).decode("utf8").replace("\n", "")return signdef _verify(self, raw_content, signature):# 开始计算签名key = self.alipay_public_keysigner = PKCS1_v1_5.new(key)digest = SHA256.new()digest.update(raw_content.encode("utf8"))if signer.verify(digest, decodebytes(signature.encode("utf8"))):return Truereturn Falsedef verify(self, data, signature):if "sign_type" in data:sign_type = data.pop("sign_type")# 排序后的字符串unsigned_items = self.ordered_data(data)message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)return self._verify(message, signature)

alipayTools.py

from AIChatProject import settings
from chatApp.aliPayConfig import AliPay  # 注意是从aliPayConfig.py中引用的,而不是python-alipay-sdk的基础AliPay包def ali():curr_alipay = AliPay(appid=settings.APP_ID,  # 应用IDapp_notify_url=settings.NOTIFY_URL,  # 异步POST回调地址return_url=settings.RETURN_URL,  # 同步GET回调地址app_private_key_path=settings.APP_PRIVATE_KEY_PATH,  # 应用私钥存放路径alipay_public_key_path=settings.ALIPAY_PUBLIC_KEY_PATH,  # 支付宝公钥存放路径debug=settings.DEBUG  # true是沙箱, false是真实环境)return curr_alipay

models.py

class UserInfo(models.Model):id = models.AutoField(primary_key=True, verbose_name='用户编号')user_account = models.CharField(verbose_name='账号', max_length=32, unique=True)user_password = models.CharField(verbose_name='密码', max_length=255)user_account_balance = models.DecimalField(verbose_name='账户余额', max_digits=10, decimal_places=2, default=10000)auto_type = ((1, '本地用户'),(2, '第三方用户'),)user_type = models.SmallIntegerField(verbose_name='用户类型', choices=auto_type, default=1)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="支付时间")# DO_NOTHING 表示不采取任何操作,当关联的对象被删除时,不执行任何处理动作。user = models.ForeignKey(UserInfo, related_name='chatapp_userinfo', on_delete=models.DO_NOTHING, db_constraint=False, verbose_name="下单用户")created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')

views.py

class OrderSerializer(serializers.ModelSerializer):class Meta:model = Orderfields = '__all__'# 一、理论知识第1步
class AliPayView(APIView):def post(self, request):# 1.实例化支付宝curr_alipay = ali()# 2.获取用户购买的订单data = request.datauser = request.userInfo# 3.建立支付宝唤醒pay_url# 3.1 必要参数(subject, out_trade_no, total_amount)subject = data.get('subject', '')out_trade_no = str(uuid.uuid4())total_amount = data.get('total_amount', '')# 3.2 调用实例的方法生成请求参数query_params = curr_alipay.direct_pay(subject, out_trade_no, total_amount)# 3.3 参数拼接pay_url = settings.GATEWAY + '?' + query_params# 4.创建数据库订单user_order = {'subject': subject,'total_amount': total_amount,'out_trade_no': out_trade_no,'user': user.id}serializer = OrderSerializer(data=user_order)if serializer.is_valid():# 5.数据库存储订单serializer.save()return Response(R(data=pay_url).to_dict())else:return Response(R(error='订单存档失败!').to_dict())# 二、理论知识第4步:支付宝服务器发送POST请求
# notify_url 异步请求异步通知接口
class PayNotifyView(APIView):def post(self, request):curr_alipay = ali()try:# 1.获取支付传递多来的参数result_data = request.data.dict()# 2.将 sign 取出signature = result_data.pop('sign')out_trade_no = result_data.get('out_trade_no')trade_no = result_data.get('trade_no')# 3.验签result = curr_alipay.verify(result_data, signature)# 确认是否支付成功if result and result_data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED"):# 4.完成订单更新:订单状态、流水号、支付时间result_order = Order.objects.filter(out_trade_no=out_trade_no).first()# 4.1 获取订单对应的用户u_id = result_order.user.idorder_user = UserInfo.objects.filter(id=u_id).first()print('验签成功,当前用户是:', u_id, order_user.user_account)# 4.2 校验订单是否重复提交!if result_order.order_status == 1:return Response(R(data={'message': '该订单已经完成','free_account': order_user.user_account_balance}).to_dict())# 4.3 修改订单状态Order.objects.filter(out_trade_no=out_trade_no, order_status=0).update(order_status=1, trade_no=trade_no)# 5.更新用户的余额new_free_tokens = order_user.user_account_balance + result_order.total_amount * Constant.PRICE_ONEprint(f'old token {order_user.user_account_balance} new token {new_free_tokens}')UserInfo.objects.filter(id=u_id).update(user_account_balance=new_free_tokens)# 完成日志记录return Response(R(data={'message': '充值成功', 'free_account': new_free_tokens}).to_dict())else:# logger.error('%s订单支付失败' % out_trade_no)print('%s订单支付失败,校验未通过或trade_status不对' % out_trade_no)except Exception as e:print('报错--->', e)passprint('%s订单支付失败' % out_trade_no)return Response(R(error='%s订单支付失败' % out_trade_no).to_dict())

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/183.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

神经网络初谈

文章目录 简介神经网络的发展历程神经网络的初生神经网络的第一次折戟神经网络的新生,Hinton携BP算法登上历史舞台命途多舛,神经网络的第二次寒冬神经网络的重生,黄袍加身,一步封神神经网络的未来,众说纷纭其他时间点 …

STM32 Proteus仿真LCD12864俄罗斯方块-FZ0063

STM32 Proteus仿真LCD12864俄罗斯方块-FZ0063 Proteus仿真小实验: STM32 Proteus仿真LCD12864俄罗斯方块-FZ0063 功能: 硬件组成:STM32F103R6单片机 LCD12864显示器多个按键 1.标准俄罗斯方块经典游戏玩法,带计时&#xff0c…

Kong 服务和路由的添加

管理服务 这里参考DB-less-Mode,因为使用的是yaml配置文件的形式,所以所有的相关配置只需要往初始化的kong.yml文件中添加就可以了,就像nginx的配置文件 DB-less-Mode 创建服务 vim /etc/kong/kong.yml services: - name: my-service #…

MySQL---表数据高效率查询(简述)

目录 前言 一、聚合查询 💖聚合函数 💖GROUP BY子句 💖HAVING 二、联合查询 💖内连接 💖外连接 💖自连接 💖子查询 💖合并查询 🎁博主介绍:博客名…

Idea 修改默认 Maven 为自己的

每次我们打开新项目时,都要去配置一遍 maven,很麻烦,其实可以去修改 idea 里面默认的 maven 配置,这样后面不管是打开新项目还是老项目,就都是用的自己的 maven 了. 1.文件->新项目设置->新项目的设置 File->Other Settings -> Settings for New Project 2.然后和…

git下载源码及环境搭建之数据库(二)

学习目标: 数据库 新项目使用 数据库文件 的配置 及相关属性的设置 步骤: 数据库 下图所示为开发时所用数据库 第一步:新建一个数据库 注意: 字符集与排序规则我们应该选择utf-8 相关 选中新创建的表,点击备份—还…

MySQL单表查询练习题

目录 第一题 第二题 第三题 第一题 1.创建数据表pet,并对表进行插入、更新与删除操作,pet表结构如表8.3所示。 (1)首先创建数据表pet,使用不同的方法将表8.4中的记录插入到pet表中。 mysql> create table pet( name varchar(…

centos7.9php8swoole5swoft2环境安装遇到确实redis扩展的解决办法

1、环境介绍 运行系统:centos7.9 php版本:php8.0.29 swoole版本:swoole5 swoft版本:swoft2.02、遇到的问题 The requested PHP extension ext-redis * is missing from your system. Install or enable PHPs redis extension。这…

python爬虫哪个库用的最多

目录 常用的python爬虫库有哪些 1. Requests: 2. BeautifulSoup: 3. Scrapy: 4. Selenium: 5. Scrapy-Redis: 哪个爬虫库用的最多 Scrapy示例代码 总结 常用的python爬虫库有哪些 Python拥有许多常用的爬虫库…

Java反射与“整活--(IOC容器)”

文章目录 前言反射什么是反射基本操作获取类对象获取类属性获取类方法方法的执行对构造方法的操作 注解定义获取注解 整活(IOC容器)项目结构IOC/DI流程ApplicationContextBeanDefinitionReaderBeanDefinitionBeanWrappergetBean()…

Django_admin数据管理后台

目录 一、基础操作 二、自定义后台操作数据行为 源码等资料获取方法 admin数据管理后台是django内置的一个后台管理界面,能查看已注册模型类的数据结构,以及对数据的增删改。 一、基础操作 1.1 检查项目目录下的urls.py有没有如下配置 1.2 创建djan…

技术讨论:我心中TOP1的编程语言

欢迎关注博主 六月暴雪飞梨花 或加入【六月暴雪飞梨花】一起学习和分享Linux、C、C、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和技术…

Flutter——最详细(NavigationBar)使用教程

NavigationBar简介 Material 3 导航栏组件! 导航栏提供了一种持久且便捷的方式来在应用程序的主要目的地之间进行切换。 使用场景: 底部菜单栏模块 属性作用onDestinationSelected选择索引回调监听器selectedIndex目前选定目的地的索引destinations存放菜单按钮back…

七大排序算法——冒泡排序,通俗易懂的思路讲解与图解(完整Java代码)

文章目录 一、排序的概念排序的概念排序的稳定性七大排序算法 二、冒泡排序核心思想代码实现 三、性能分析四、七大排序算法 一、排序的概念 排序的概念 排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或…

C++之工厂模式

目录 一、为什么要使用工厂模式 优点 缺点 二、简单工厂(Simple Factory) 好处: 不足: 三、工厂方法: 好处: 不足: 四、抽象工厂(Abstract Factory) 一、为什…

【HCIA】10.VLAN间通信

VLAN间通信的解决方法 使用路由器的物理接口 路由器三层接口作为网关,转发本网段前往其它网段的流量。路由器三层接口无法处理携带VLAN Tag的数据帧,因此交换机上联路由器的接口需配置为Access。路由器的一个物理接口作为一个VLAN的网关,因此…

Django_re_path_使用正则匹配url

与path定义的路由相比,re_path 定义的路由可以使用正则表达式匹配url。 需要注意的是: 如果未定义匹配结果的变量名,匹配的结果默认传入视图的第2个形参。如果定义了匹配结果的变量名,匹配的结果会传给视图的同名字段&#xff0…

【GitOps系列】从零上手GitOps

文章目录 GitOps 介绍如何将业务代码构建为容器镜像?如何将容器镜像部署到K8s?K8s如何实现自动扩容和自愈?1.传统的扩容和自愈2.k8s自愈机制3.k8s弹性扩容 如何借助GitOps实现应用秒级自动发布和回滚?1.传统 K8s 应用发布流程2.从…

【Arduino小车实践】陀螺仪的使用

一、MPU6050简介 MPU6050是一款陀螺仪模块,可以测量X、Y、Z三轴的角速度和加速度,还带有温度传感器和数字运动处理器(DMP)。 二、学习步骤 1. I2C协议 MPU6050是通过I2C协议进行驱动的,配置寄存器和获取数据都需要通过I2C协议去实现开发板与…

CentOS环境下的Nginx安装

Nginx 安装 下载 nginx 下载地址:http://nginx.org/en/download.html 将下载好的压缩包拷贝到根目录下 通过xshell如果出现 bash: rz: 未找到命令 ,需要先运行下面的命令 yum -y install lrzsz安装 解压到当前目录 tar -zxvf nginx-1.22.1.tar.gz安…