目前国内比较流行的第三方支付主要有支付宝和微信支付,博主最近研究了下如何用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. java 判断语句和循环语句

    一.判断语句:if和switch if(关系表达式1) { 语句体1; }else if (关系表达式2) { 语句体2; }…else { 语句体n+; } switch(表达式) { case 值 ...

  2. 浅谈Kubernetes生产架构

    注意本文,只是笔者针对Kubernetes生产环境运行的一些关于架构设计和实现方案的总结,内容很粗糙,同时也会不断完善. 首先,我们来梳理下Kubernetes生产架构,其设计适用于绝大多数环境.如下 ...

  3. 获取windows凭证管理器明文密码

    1.运行cmdkey /list查看windows保存凭证 方法1.mimikaz mimikatz vault::cred 2.利用powershell尝试获取 windows 普通凭据类型中的明文 ...

  4. 如何快速上手Mac

    网络上关于Mac的教程很多,大部分问题通过百度和谷歌就能搞定了.对于技巧的细节,我将不再过多的重复,看了我的参考资料基本就能够全部了解,他们也比我讲得详细得很多.我这篇文章想做的,是以一个普通的win ...

  5. Neo4j安装

    一.Windows版本 1)下载java8,并配置环境变量 java下载请点击,提取码:f6ci 2)Neo4j下载 选windows版本 新建系统环境变量: 并配置Path环境变量,添加bin所在目 ...

  6. 使Python走向Effective系列目录

    Effective以一词,并不单单局限于执行速度层面的高效率,同时有着令代码易于阅读.易于测试且易于维护等意思,此外,它还蕴藏着易于扩展.易于修改和易于多人协作等更为高阶的理念.如果能够通过一些具体的 ...

  7. HDU 2594(求最长公共前后缀 kmp)

    题意是在所给的两个字符串中找最长的公共前后缀,即第一个字符串前缀和第二个字符串后缀的最长相等串. 思路是将两个字符串拼接在一起,然后直接套用 kmp 算法即可. 要注意用 next 会报编译错误,改成 ...

  8. xin

    测试文件 行内公式 y = x 独立公式 limx → 0x = 0

  9. gunicorn+anaconda+nginx部署django项目(ubuntu)

    首先进入conda 虚拟环境: source activate test 安装gunicorn: pip install gunicorn 运行gunicorn gunicorn -w 2 -b 12 ...

  10. 查询SQL Server执行过的SQL语句

    SELECT TOP 1000         ST.text AS '执行的SQL语句',        QS.execution_count AS '执行次数',        QS.total_ ...