目前国内比较流行的第三方支付主要有支付宝和微信支付,博主最近研究了下如何用Python接入支付宝支付,这里我以Tornado作为web框架,接入支付宝构造支付接口。

使用Tornado异步接入支付宝支付流程:

1. 进入蚂蚁金服开放平台填写开发者信息、应用信息

2. 配置RSA256密钥,生成支付宝和应用的密钥

3. 构造订单接口API,生成订单

4. 构造支付接口

1. 进入蚂蚁金服开放平台填写开发者信息、应用信息

这里通过沙箱环境开发测试接口,蚂蚁金服开放平台-->开发者中心-->研发者服务-->沙箱应用,配置沙箱应用信息:

设置授权回调地址,注意:这个地址一定要是外网IP地址(我这里是我的阿里云服务器地址),回调地址是自己支付完回调的api地址,可通过扫码下载沙箱板支付宝钱包进行支付测试:

设置沙箱账号,设置买家和买家的测试账号,支付宝会默认给买家账户99999元,可用来测试支付接口是否成功:

2. 配置RSA256密钥,生成支付宝和应用的密钥

支付宝默认有两种加密算法生成密钥:RSA(SHA1)和RSA2(SHA256),鉴于安全性支付宝推荐使用RSA2(SHA256)密钥。通过查看密钥生成文档https://docs.open.alipay.com/291/105971得知密钥生成方法,按文档提示下载密钥生成工具,解压后打开生成工具,选择密码格式(Python当然就是选择PKCS1了)和密码长度,生成公钥和私钥:

生成后可在RSA密钥文件夹下查看应用的公钥和私钥,并将应用公钥上传到开放平台的开发者环境中:

3. 构造订单接口API,生成订单

查看支付接口文档:https://docs.open.alipay.com/270/alipay.trade.page.pay/可知:

支付接口的必填参数有out_trade_no(订单号)、total_amount(订单金额)、subject(订单标题),所以先构造订单接口,生成订单:

 class OrderSnHandler(BaseHandler):
@authenticated
async def post(self, *args, **kwargs):
"""
创建订单信息
:param request:
:return:
"""
res_data = {}
req_data = self.request.body.decode("utf8")
req_data = json.loads(req_data)
post_script = req_data.get("post_script")
order_form = TradeOrderSnForm.from_json(req_data)
if order_form.validate():
try:
order_mount = order_form.order_mount.data
orders_object = await self.application.objects.create(
OrderInfo,
pay_status=OrderInfo.ORDER_STATUS[4][0],
pay_time=datetime.now(),
order_sn=OrderInfo.generate_order_sn(),
user=self.current_user,
order_mount=order_mount,
post_script=post_script
)
res_data["id"] = orders_object.id
except Exception:
self.set_status(400)
res_data["content"] = "订单创建失败"
else:
res_data["content"] = order_form.errors self.finish(res_data)

4. 构造支付接口

(1) 构造支付接口类

流程:RSA导入公钥和私钥-->构造请求参数biz_content-->构造支付宝公共请求参数-->排序并拼接参数为规范字符串-->生成签名后的字符串-->请求支付宝接口-->对支付宝接口返回的数据进行签名比对

 class AliPay(object):
"""
支付宝支付接口
""" def __init__(self, appid, app_notify_url, app_private_key_path,
alipay_public_key_path, return_url, debug=False):
self.appid = appid
self.app_notify_url = app_notify_url
self.app_private_key_path = app_private_key_path
self.app_private_key = None
self.return_url = return_url
with open(self.app_private_key_path) as fp:
self.app_private_key = RSA.importKey(fp.read()) self.alipay_public_key_path = alipay_public_key_path
with open(self.alipay_public_key_path) as fp:
self.alipay_public_key = RSA.import_key(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, **kwargs): # NOQA
"""
构造请求参数biz_content,
并将其放入公共请求参数中,
返回签名sign的data
:param subject:
:param out_trade_no:
:param total_amount:
:param kwargs:
:return:
"""
biz_content = {
"subject": subject,
"out_trade_no": out_trade_no,
"total_amount": total_amount,
"product_code": "FAST_INSTANT_TRADE_PAY",
} 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):
"""
构造公共请求参数
:param method:
:param biz_content:
:param return_url:
:return:
"""
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:
data["notify_url"] = self.app_notify_url
data["return_url"] = self.return_url return data def sign_data(self, data):
"""
拼接排序后的data,以&连接成符合规范的字符串,并对字符串签名,
将签名后的字符串通过quote_plus格式化,
将请求参数中的url格式化为safe的,获得最终的订单信息字符串
:param data:
:return:
"""
# 签名中不能有sign字段
if "sign" in data:
data.pop("sign") unsigned_items = self.ordered_data(data)
unsigned_string = "&".join("{0}={1}".format(k, v) for k, v in unsigned_items)
sign = self.sign_string(unsigned_string.encode("utf-8"))
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_string def ordered_data(self, data):
"""
将请求参数字典排序,
支付宝接口要求是拼接的有序参数字符串
:param data:
:return:
"""
complex_keys = []
for key, value in data.items():
if isinstance(value, dict):
complex_keys.append(key) for key in complex_keys:
data[key] = json.dumps(data[key], separators=(',', ':')) return sorted([(k, v) for k, v in data.items()]) def sign_string(self, unsigned_string):
"""
生成签名,并进行base64 编码,
转换为unicode表示并去掉换行符
:param unsigned_string:
:return:
"""
key = self.app_private_key
signer = PKCS1_v1_5.new(key)
signature = signer.sign(SHA256.new(unsigned_string))
sign = encodebytes(signature).decode("utf8").replace("\n", "")
return sign def _verify(self, raw_content, signature):
"""
对支付宝接口返回的数据进行签名比对,
验证是否来源于支付宝
:param raw_content:
:param signature:
:return:
"""
key = self.alipay_public_key
signer = PKCS1_v1_5.new(key)
digest = SHA256.new()
digest.update(raw_content.encode("utf8"))
if signer.verify(digest, decodebytes(signature.encode("utf8"))):
return True
return False def verify(self, data, signature):
"""
验证支付宝返回的数据,防止是伪造信息
:param data:
:param signature:
:return:
"""
if "sign_type" in data:
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)

(2) 构造支付链接接口

通过步骤3创建的订单信息生成支付链接,这里接口我采用协程+异步的方式,authenticated是自定义的JWT验证装饰器,private_key_path和ali_pub_key_path是前面生成的应用私钥和支付宝公钥文件地址

 class GenPayLinkHandler(BaseHandler):
@authenticated
async def get(self, *args, **kwargs):
"""
通过订单生成支付链接
:param args:
:param kwargs:
:return:
"""
res_data = {}
order_id = get_int_or_none(self.get_argument("id", None))
if not order_id:
self.set_status(400)
self.write({"content": "缺少order_id参数"}) try:
order_obj = await self.application.objects.get(
OrderInfo, id=order_id,
pay_status=OrderInfo.ORDER_STATUS[4][0]
)
out_trade_no = order_obj.order_sn
order_mount = order_obj.order_mount
subject = order_obj.post_script
alipay = AliPay(
appid=settings["ALI_APPID"],
app_notify_url="{}/alipay/return/".format(settings["SITE_URL"]),
app_private_key_path=settings["private_key_path"],
alipay_public_key_path=settings["ali_pub_key_path"],
debug=True,
return_url="{}/alipay/return/".format(settings["SITE_URL"])
)
url = alipay.direct_pay(
subject=subject,
out_trade_no=out_trade_no,
total_amount=order_mount,
return_url="{}/alipay/return/".format(settings["SITE_URL"])
)
re_url = settings["RETURN_URI"].format(data=url)
res_data["re_url"] = re_url
except OrderInfo.DoesNotExist:
self.set_status(400)
res_data["content"] = "订单不存在" self.finish(res_data)

返回结果:

打开支付链接可以看到:

(3) 构造支付的回调接口

在支付完成后,支付宝会调用在开发者信息中配置的回调url,通过GET方法回调return_ul,通过POST方法发送notify主动通知商户返回服务器里指定的页面,这里分别实现return_ul和notify_url对应的接口,支付宝返回的notify_url是个异步的所以我这里也以异步的方式实现这个接口:

 class AlipayHandler(BaseHandler):
def get(self, *args, **kwargs):
"""
处理支付宝的return_url返回
:param request:
:return:
"""
res_data = {}
processed_dict = {}
req_data = self.request.arguments
req_data = format_arguments(req_data)
for key, value in req_data.items():
processed_dict[key] = value[0] sign = processed_dict.pop("sign", None)
alipay = AliPay(
appid=settings["ALI_APPID"],
app_notify_url="{}/alipay/return/".format(settings["SITE_URL"]),
app_private_key_path=settings["private_key_path"],
alipay_public_key_path=settings["ali_pub_key_path"],
debug=True,
return_url="{}/alipay/return/".format(settings["SITE_URL"])
) verify_re = alipay.verify(processed_dict, sign) if verify_re is True:
res_data["content"] = "success"
else:
res_data["content"] = "Failed" self.finish(res_data) async def post(self, *args, **kwargs):
"""
处理支付宝的notify_url
:param request:
:return:
"""
processed_dict = {}
req_data = self.request.body_arguments
req_data = format_arguments(req_data)
for key, value in req_data.items():
processed_dict[key] = value[0] sign = processed_dict.pop("sign", None)
alipay = AliPay(
appid=settings["ALI_APPID"],
app_notify_url="{}/alipay/return/".format(settings["SITE_URL"]),
app_private_key_path=settings["private_key_path"],
alipay_public_key_path=settings["ali_pub_key_path"],
debug=True,
return_url="{}/alipay/return/".format(settings["SITE_URL"])
) verify_re = alipay.verify(processed_dict, sign) if verify_re is True:
order_sn = processed_dict.get('out_trade_no')
trade_no = processed_dict.get('trade_no')
trade_status = processed_dict.get('trade_status') orders_query = OrderInfo.update(
pay_status=trade_status,
trade_no=trade_no,
pay_time=datetime.now()
).where(
OrderInfo.order_sn == order_sn
)
await self.application.objects.execute(
orders_query
) self.finish("success")

测试支付结果:

使用Tornado异步接入第三方(支付宝)支付的更多相关文章

  1. 商家 APP 如何接入新版支付宝支付,老版本商家如何升级

    代码地址如下:http://www.demodashi.com/demo/14006.html 前言 支付宝移动支付2.0版本对比1.0版本做了较大更新,新申请的商家都需要采用最新2.0版本 SDK ...

  2. PC、h5项目接入第三方支付宝扫码登录、扫码付款

    首先介绍一下pc项目接入支付宝扫码支付. 1.pc.移动接入支付宝扫码支付. 其实这个逻辑很简单,前端所需要处理的不是很多,后台会给一个连接,前端只需要将要支付的订单id拼接在这个连接上,然后打开跳转 ...

  3. asp.net mvc 接入最新支付宝支付+退款 alipay-sdk-NET-20170615110549

    第1步: https://openhome.alipay.com/developmentDocument.htm 第2步:下载sdk和demo https://docs.open.alipay.com ...

  4. cocos2d-x android工程接入第三方支付宝SDK

    1. 首先去支付宝官网下载开发者文档 2. 然后按着开发者文档将支付宝的sdk导入到你的工程中,并关联到工程中,步骤入下图: (1)将从支付宝官方网站获得的支付宝的sdk的jar包拷贝到工程中的lib ...

  5. cocos2d-x C++ iOS工程集成第三方支付宝支付功能

      一.在支付宝开放平台下载支付宝SDK(https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.WWgVz8&tr ...

  6. Java 支付宝支付,退款,单笔转账到支付宝账户(支付宝支付)

    最近一直在接触第三方,刚接入完支付宝的API做一下总结,个人能力薄弱有不对的地方望指教.  做的是一个小型电商项目,所以会接入第三方的支付和登入功能, 第一次接入第三方撸了很多官方文档. 进入主题, ...

  7. (转载)Android支付宝支付封装代码

    Android支付宝支付封装代码 投稿:lijiao 字体:[增加 减小] 类型:转载 时间:2015-12-22我要评论 这篇文章主要介绍了Android支付宝支付封装代码,Android支付的时候 ...

  8. APP支付宝支付接入

    1.app支付简介 买家可以在手机,掌上电脑等无线设备的应用程序内,通过支付宝(支付宝app或网页版支付宝)付款购买商品,且资金实行实时到账. 2.申请条件 1.申请前必须拥有经过实名认证的支付宝账户 ...

  9. PHP接入支付宝支付

    创建应用 使用支付宝账号登录开放平台创建应用,应用创建成功之后可以得到APPID等相关信息 接着需要设置RSA密钥,可以使用蚂蚁金服开放平台提供的生成工具,生成完密钥需在开放平台中填写. 代码接入 引 ...

随机推荐

  1. The 19th Zhejiang University Programming Contest - H

    Princess Cjb is caught by Heltion again! Her knights Little Sub and Little Potato are going to Helti ...

  2. DirectX11--HR宏关于dxerr库的替代方案

    DirectX11 With Windows SDK完整目录 欢迎加入QQ群: 727623616 可以一起探讨DX11,以及有什么问题也可以在这里汇报. 综述 参考文章:https://blogs. ...

  3. 使用原生 python 造轮子搭建博客

    这篇用来 记录一个 从零开始的 博客搭建,希望坚持下去,因为python 开发效率令人发指,所以会原生从零写 ORM ,Web 框架 前提是打好 异步 io 的基础, 使用异步,有一点要谨记,一旦开始 ...

  4. 【汇编语言】Win10 安装 DOXBox0.74

    1.下载package包,有用的只有前两个. 2.解压 masm 压缩文件,我把它解压到D盘. 3.双击 图1 中的 DOXBox 0.74.exe 进行安装. 4.打开 DOXBox0.74 (参考 ...

  5. httpclient用getStatusCode

      TP 定义的状态代码的值(.net HttpWebResponse.HttpStatusCode 成员名称 说明 Continue 等效于 HTTP 状态 100.Continue 指示客户端可能 ...

  6. java Socket实例

    可以实现客户端与服务端双向通信,支持多客户端连接,客户端断开连接,服务端不会出现异常 服务端代码: package com.thinkgem.jeesite.modules.socketTest.de ...

  7. 分布式系列十一: Redis进阶

    分布式锁 数据库 数据库是使用唯一索引不允许重复的特性(或自定义实现如乐观锁). 但持有锁的进程如果释放锁时异常则容易导致死锁. zookeeper 使用临时节点, watcher可以获得节点被删除的 ...

  8. Visual Studio - File Properties (Build Action, Copy to Output Directory)

    Ref: MSDN (https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2010/0c6xyb ...

  9. codeforces gym 101611C 重链剖分构造

    给一棵树 要求在一个20*1e6的矩阵上放下这棵树,每个点的坐标都是整数且所有边都不相叉 题解 按照重链遍历,先给轻儿子坐标,然后沿着重儿子向下走即可 #include <bits/stdc++ ...

  10. safari下载中文文件名乱码

    原因:响应头设置content-disposition,主要遵循 RFC 5987标准. response.setHeader("content-disposition",&quo ...