最新博客更新见我的个人主页: https://xzajyjs.cn

我们在使用Django构建网站时常需要对接第三方支付平台的支付接口,这里就以支付宝为例(其他平台大同小异),使用支付宝开放平台的沙箱环境进行实验。

我们这里使用一个第三方的AliPay Python SDK(github)

下面看一下它的基本使用


调用流程

事实上需要我们网站服务端做的事并不多,只需要生成一个订单向支付宝发出支付请求,等用户支付完毕后向支付宝(通过同步和异步的方式)查询订单、交易信息即可。

在实际生产环境中,需要注意如下各种安全性问题:

  • 由于同步返回的不可靠性,支付结果必须以异步通知或查询接口返回为准,不能依赖同步跳转。

  • 商户系统接收到异步通知以后,必须通过验签(验证通知中的 sign 参数)来确保支付通知是由支付宝发送的。

  • 接收到异步通知并验签通过后,请务必核对通知中的 app_id、out_trade_no、total_amount 等参数值是否与请求中的一致,并根据 trade_status 进行后续业务处理。

  • 在支付宝端,partnerId 与 out_trade_no 唯一对应一笔单据,商户端保证不同次支付 out_trade_no 不可重复;若重复,支付宝会关联到原单据,基本信息一致的情况下会以原单据为准进行支付。


具体实践

1.准备工作

由于使用真实环境需要商户支付宝账号、上线应用需要审批等流程,我们这里使用支付宝开放平台的沙箱环境

沙箱环境中提供了后面需要的参数如APPIDAPP_PRIVATE_KEYALIPAY_PUBLIC_KEY支付宝网关等。

接下来安装AliPay Python SDK

pip3 install python-alipay-sdk --upgrade

由于是沙箱环境,平台已经提供给我们需要的公钥和私钥,如果是生产环境,则需要通过openssl生成

openssl
OpenSSL> genrsa -out app_private_key.pem 2048 # 私钥
OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # 导出公钥
OpenSSL> exit

在支付宝上下载的公钥是一个字符串,你需要在文本的首尾添加标记位:

-----BEGIN PUBLIC KEY----- 和 -----END PUBLIC KEY-----

2.创建订单

先在settings.py中设定一些关键参数

# 读取公钥和私钥为字符串
app_private_key_string = open("/path/to/your/private/key.pem").read()
alipay_public_key_string = open("/path/to/alipay/public/key.pem").read()
# 沙箱环境提供的APPID
ALIPAY_APP_ID = "2021000120607609"
# 同步回调url(这里需要一个公网ip)
RETURN_URL = "http://xxx.xxx.xxx.xxx/"
# 支付宝网关地址。注意:正式环境和沙箱环境的网关地址不同
GATEWAY = "https://openapi.alipaydev.com/gateway.do?"
from alipay import AliPay, AliPayConfig
from .settings import APP_PRIVATE_KEY, ALIPAY_PUBLIC_KEY, ALIPAY_APP_ID, RETURN_URL, GATEWAY def create_alipay():
# 使用应用公钥进行报文验签
alipay = AliPay(
appid=ALIPAY_APP_ID,
app_notify_url=None, # 默认异步回调 url
app_private_key_string=APP_PRIVATE_KEY,
alipay_public_key_string=ALIPAY_PUBLIC_KEY,
sign_type="RSA2",
debug=False, # 默认 False
verbose=False, # 输出调试数据
config=AliPayConfig(timeout=15) # 可选,请求超时时间
)
return alipay

下面可以创建支付订单了(官方文档)

# 向支付宝提交订单信息
def alipay_pay(subject, total_amount, out_trade_no, return_url_view):
alipay = create_alipay() # 先实例化alipay
return_url = RETURN_URL + return_url_view # 同步回调url,用于支付完后跳转回网站并对支付状态进行即时检验。这里的return_url_view是用于接收支付宝回调的状态检验的视图函数
order_string = alipay.api_alipay_trade_page_pay(
out_trade_no=out_trade_no, # 商户订单号,这个需要商户自定义
total_amount=total_amount, # 支付总金额
subject=subject, # 订单标题
return_url=return_url, # 同步回调url,用于支付完后跳转回网站并对支付状态进行即时检验
notify_url="https://example.com/notify" # 可选,不填则使用默认 notify url
)
return order_string # 返回订单字符串

调用

import string
import random
from .settings import GATEWAY # 随机生成32位商户交易号
out_trade_no = "".join(random.sample(string.ascii_letters+string.digits, 32)) # 在视图函数对alipay_return进行绑定
# 同步回调url为: http://xxx.xxx.xxx.xxx/alipay_return
order_string = alipay_pay(subject="测试商品",total_amount=100,out_trade_no=out_trade_no,return_url_view='alipay_return')
return HttpResponseRedirect(GATEWAY+order_string)

调用后会跳转到支付宝平台,使用沙箱环境提供的买家账号即可完成支付

但是此时我们还不能回调跳转到我们自己的网站,也不能获得订单支付信息,下面还有最后一步。

3.同步回调

我们刚刚创建的订单信息中填写了return_url,我们需要一个视图函数来接收,并对其返回值进行分析

def alipay_return(request):
processed_dict = {}
# 回调时alipay会把一些公用信息通过GET方式传参回来,这里用字典去接收存储
for key, value in request.GET.items():
processed_dict[key] = value
"""
processed_dict = {
'charset': 'utf-8',
'out_trade_no': 'xxxxxxx', # 这个是我们之前创建订单时生成的商户交易号
'method': 'alipay.trade.page.pay.return',
'total_amount': '100.00', # 交易金额
'trade_no': '20220xxxxxxxx24353', # 支付宝交易号
'auth_app_id': '2021xxxxxx609', # 用户appid
'version': '1.0',
'app_id': '2021xxxxxx7609', # 沙箱提供的APPID 应用ID
'sign_type': 'RSA2',
'seller_id': '2088xxxxx844', # 收款支付宝账号对应的支付宝唯一用户号。
以2088开头的纯16位数字
'timestamp': '2022-05-28 23:40:55'
}
"""
sign = processed_dict.pop("sign", None) new_alipay = create_alipay()
verify_re = new_alipay.verify(processed_dict, sign)
if verify_re is True:
print("支付成功")
else:
print("支付失败")

注意:同步回调往往不可靠,因此需要增加一个异步回调检验

另外,在订单创建后需要向数据库存储订单信息,包括订单金额、商户订单号、appid等,等待回调后与参数校验一致无误后再将订单支付信息进行更新。下面的完整示例不会包括该部分,请自行完成


完整示例

项目结构

# alipay.py
from alipay import AliPay, AliPayConfig
from .settings import APP_PRIVATE_KEY, ALIPAY_PUBLIC_KEY, ALIPAY_APP_ID, RETURN_URL def create_alipay():
alipay = AliPay(
appid=ALIPAY_APP_ID,
app_notify_url=None, # 默认回调 url
app_private_key_string=APP_PRIVATE_KEY,
# 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
alipay_public_key_string=ALIPAY_PUBLIC_KEY,
sign_type="RSA2", # RSA 或者 RSA2
debug=False, # 默认 False
verbose=False, # 输出调试数据
config=AliPayConfig(timeout=15) # 可选,请求超时时间
)
return alipay def alipay_pay(subject, total_amount, out_trade_no, return_url_view):
alipay = create_alipay()
return_url = RETURN_URL + return_url_view
order_string = alipay.api_alipay_trade_page_pay(
out_trade_no=out_trade_no,
total_amount=total_amount,
subject=subject,
return_url=return_url,
notify_url="https://example.com/notify" # 可选,不填则使用默认 notify url
)
return order_string
# settings.py
...
... ALIPAY_APP_ID = "xxxxxx"
APP_PRIVATE_KEY = open(os.path.join(BASE_DIR, 'alipay/app_private_key.pem'), 'r').read()
ALIPAY_PUBLIC_KEY = open(os.path.join(BASE_DIR, 'alipay/alipay_public_key.pem'), 'r').read()
RETURN_URL = "http://xxxxxx/"
GATEWAY = "https://openapi.alipaydev.com/gateway.do?"
# urls.py
from django.contrib import admin
from django.urls import path
from . import views urlpatterns = [
path('admin/', admin.site.urls),
path('', views.index),
path('alipay_return/', views.alipay_return)
]
# views.py
import random
import string
from django.http import HttpResponseRedirect
from django.shortcuts import render
from ali_django.alipay import alipay_pay, create_alipay
from django.conf import settings def index(request):
if request.method == "GET":
return render(request, 'index.html')
elif request.method == "POST":
# 随机生成32位商户交易号
out_trade_no = "".join(random.sample(string.ascii_letters + string.digits, 32)) order_string = alipay_pay(subject="测试商品", total_amount=100, out_trade_no=out_trade_no,return_url_view='alipay_return')
return HttpResponseRedirect(settings.GATEWAY + order_string) def alipay_return(request):
processed_dict = {}
# 回调时alipay会把一些公用信息通过GET方式传参回来,这里用字典去接收存储
for key, value in request.GET.items():
processed_dict[key] = value
sign = processed_dict.pop("sign", None) new_alipay = create_alipay()
verify_re = new_alipay.verify(processed_dict, sign)
if verify_re is True:
print("支付成功")
else:
print("支付失败")
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>支付宝支付接口测试</title>
</head>
<body>
<form action="" method="post">
<input type="submit" value="提交">
</form>
</body>
</html>

Django对接支付宝Alipay支付接口的更多相关文章

  1. App对接支付宝移动支付功能

    前段时间看了下app对接支付宝移动支付的功能,并自己总结了下支付宝移动支付的实现流程 一.申请流程          前提是已有现成的应用. 1.  申请地址            https://b ...

  2. 支付宝WAP支付接口开发(Node/Coffee语言)

    此博客不更新很久了, 更新的文档在这, 有兴趣到这里围观: http://neutra.github.io/2013/%E6%94%AF%E4%BB%98%E5%AE%9DWAP%E6%94%AF%E ...

  3. php支付宝在线支付接口开发教程【转】

    php支付宝在线支付接口开发教程 这篇文章主要为大家详细介绍了php支付宝在线支付接口开发教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下   1.什么是第三方支付 所谓第三方支付,就是一些和各 ...

  4. 支付宝WAP支付接口开发

    支付宝WAP支付接口开发 因项目需要,要增加支付宝手机网站支付功能,找了支付宝的样例代码和接口说明,折腾两天搞定,谨以此文作为这两天摸索的总结.由于公司有自己的支付接口,并不直接使用这个接口,所以晚些 ...

  5. 【转】支付宝WAP支付接口开发

    支付宝WAP支付接口开发 因项目需要,要增加支付宝手机网站支付功能,找了支付宝的样例代码和接口说明,折腾两天搞定,谨以此文作为这两天摸索的总结.由于公司有自己的支付接口,并不直接使用这个接口,所以晚些 ...

  6. H5网站接入支付宝的支付接口

    写本文章的目的是为了记录工作中遇到的问题,方便以后遇到可以迅速解决问题 H5手机网站接入支付宝的支付接口,推荐使用支付宝提供的SDK来快速开发 我使用的是SDK开发 引用命名空间 using Aop. ...

  7. Django 对接 支付宝支付, 回调

    平台 点击这里进入 蚂蚁金服开放平台 沙箱 点击这里进入 沙箱环境 初始界面 设置公钥 下载创建秘钥工具 1.  进入文档中心 这里 2. 选中 电脑网站支付 3. 进入后选中 API 列表 中的 统 ...

  8. thinkphp框架对接支付宝即时到账接口回调的代码

    关于支付宝即时收款接口的对接过程,很简单,也有很多人发过,我这里就不在啰嗦了,对接完成后,在线支付成功后的回调,相对来说,是个难点,,我重点分享下我的经验. 我在开发二代旅游CMS(http://ww ...

  9. 支付宝php支付接口说明

    直接把该代码放到PHP服务器下,直接访问index.php.1.文件列表: alipay_config.php    (基本参数配置页面,填写商家的支付宝安全校验码,合作id,支付宝帐号等内容)ind ...

随机推荐

  1. 一像素边框的问题(使不同dpr设备完美显示1px的border)

    问题:不同dpr的屏幕有不同的屋里像素值,而我们css像素的1px由于不同屏幕的渲染会导致宽度并不一样: 例如: dpr为2的retina屏幕是有四个物理像素点(真实屏幕上的点)组成一个逻辑(css) ...

  2. matplotlib---Annotation标注

    import numpy as np import matplotlib.pyplot as plt x = np.linspace(-3, 3, 50) y = 2 * x + 1 plt.figu ...

  3. 微信小程序,制作属于自己的Icon图标

    前言 最近在接手一个微信小程序,发现里面的图标都是使用的image组件,看起来非常别扭,加载也不太顺畅. 就想着看看微信有没有类似自带的图标库可以使用. 有是有,就是太少了,翻来翻去好像也就 8 种, ...

  4. 数组-LeetCode-笔试

    目录 数组理论基础 二分查找 二分法第一种写法 二分法第二种写法 ACM 移除元素 暴力解法 双指针法(快慢指针) ACM 有序数组的平方 暴力排序 双指针法 长度最小的子数组 暴力解法 滑动窗口 相 ...

  5. Druid 1.1.24 在控制台打印"discard long time none received connection. , jdbcUrl : jdbc:mysql://...."错误日志

    Druid 1.1.24 在控制台打印"discard long time none received connection. , jdbcUrl : jdbc:mysql://...&qu ...

  6. Sql递归查询,Sqlserver、Oracle、PG、Mysql

    递归分两种:一种由父项向下级递归,另一种是由子项向上级递归.下面就这两种情况做个简单的处理. 假设有一个表treeview,包含字段 id,parentid,text 分别代表id,上级id,描述字段 ...

  7. 帝国cms插件 解决后台修改信息时内容关键字不替换的问题

    很多站长是不是发现了帝国cms增加信息时,是有关键词替换的,这样是有利于网站优化排名. 但是在后台格式化数据之后,再去进行修改之后,对不起,内容关键字就实效了. 针对这一问题,解决方案如下: 找到 / ...

  8. Unity-动画状态机使用细节记录

    Unity动画控制器Animator功能非常强大,总结一些具体使用细节,在动作游戏中很实用: 1.动画烘焙 不同动画之间,可能存在角色朝向,重心高度不一致: 可以在动画Eidt界面设置RootTran ...

  9. Python Windows 快捷键自动给剪贴板(复制)图片添加水印

    编写一个能在windows上使用的按下快捷键自动给剪贴板(复制)的图片添加水印的小工具.plyer.PIL.pyinstaller.pynput.win32clipboard库.记录自己踩过的坑,部分 ...

  10. Educational Codeforces Round 121 (Rated for Div. 2)——A - Equidistant Letters

    A - Equidistant Letters 题源:https://codeforces.com/contest/1626/problem/A 今天上午VP了这场CF,很遗憾的是一道题也没写出来,原 ...