目前国内比较流行的第三方支付主要有支付宝和微信支付,博主最近研究了下如何用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. 【深度学习】RNN | GRU | LSTM

    目录: 1.RNN 2.GRU 3.LSTM 一.RNN 1.RNN结构图如下所示: 其中: $a^{(t)} = \boldsymbol{W}h^{t-1} + \boldsymbol{W}_{e} ...

  2. Spring项目使用Junit4测试配置

    我们经常要写junit测试,在不启动整个web项目的情况下,测试自己的service实现或者是dao实现,我们来充分利用下junit4的强大功能. 1.junit4的测试类 import java.u ...

  3. [转载]再谈PostgreSQL的膨胀和vacuum机制及最佳实践

    本文转载自 www.postgres.cn 下的文章: 再谈PostgreSQL的膨胀和vacuum机制及最佳实践http://www.postgres.cn/news/viewone/1/390 还 ...

  4. Exp4 恶意代码分析 20164314

    一.实践目标 1.是监控你自己系统的运行状态,看有没有可疑的程序在运行. 2.是分析一个恶意软件,就分析Exp2或Exp3中生成后门软件:分析工具尽量使用原生指令或sysinternals,systr ...

  5. spring.http.multipart.maxFileSize提示无效报错问题处理

    在SpringBoot项目中,配置spring.http.multipart.maxFileSize用于限定最大文件上传大小. 但是,SpringBoot版本不同,关于这一块的配置也不相同. 1.Sp ...

  6. ueditor接入秀米编辑器

    秀米编辑器用来编辑微信页面很方便,功能也比较强大.秀米提供了第三方编辑器接入的功能,接入方法可以参照官网示例:http://hgs.xiumi.us/uedit/ 但是这里有几点要注意: 1. 示例中 ...

  7. Sublime Text 3删除插件

    Ctrl+Shift+P调出命令窗口,输入remove: 选择第二个Remove Package,会看到如下界面: 里面列出了你已经安装的插件,之后选择你想要卸载的就好了.

  8. Java 多线程 - Java对象头, Monitor

    详见: http://www.cnblogs.com/pureEve/p/6421273.html

  9. 拷贝本地文件到docker容器

    查找所有容器 docker ps -a 查找容器长ID docker inspect -f '{{.ID}}' python 拷贝本地文件到容器 docker cp 本地路径 容器长ID:容器路径

  10. Tableau 之一 连接数据源

    导入数据源 与各类数据源建立连接关系,是使用tableau探索分析数据的第一步,本节内容包括: 数据源类型 连接数据源 数据源类型 打开tableau,可以在左侧窗口看到连接选项,目前tableau可 ...