支付宝官方SDK是相当的难用,而且python官方SDK不支持验签,验签功能需要自己写,因此使用了第三方SDK:python-alipay-sdk
地址:github连接
验签步骤:支付宝平台官方说明
如果需要自己实现验签:验签实现逻辑
首先安装:
pip install python-alipay-sdk --upgrade
第三方SDK实际上已经实现了验签逻辑,但是功能比较少,我们需要的发红包、小程序创建支付订单、登录逻辑之类的都没有,因此我们自己来扩展一下
首先在官方的验签步骤页我们应该生成了自己的密钥文件,大概是这样:
先看下demo创建一个支付宝web订单,之后模仿源码添加我们需要的功能即可:
from alipay import AliPay, DCAlipay
dc_alipay = DCAliPay(
appid="appid",
app_notify_url="http://example.com/app_notify_url",
app_private_key_string=app_private_key_string,
app_public_key_cert_string=app_public_key_cert_string,
alipay_public_key_cert_string=alipay_public_key_cert_string,
alipay_root_cert_string=alipay_root_cert_string
)
subject = "测试订单"
# Pay via Web,open this url in your browser: https://openapi.alipay.com/gateway.do? + order_string
order_string = dc_alipay.api_alipay_trade_page_pay (
out_trade_no="20161112",
total_amount=0.01,
subject=subject,
return_url="https://example.com",
notify_url="https://example.com/notify" # this is optional
)
from alipay import DCAliPay
from alipay import compat
class CBAliPay(DCAliPay):
def api_alipay_fund_trans_uni_transfer(self, out_biz_no, trans_amount, payee_info, biz_scene=None,
product_code=None, **kwargs):
""" 商家转账给用户即发红包 """
if not product_code:
product_code = "STD_RED_PACKET"
if not biz_scene:
biz_scene = "DIRECT_TRANSFER"
biz_content = {
"out_biz_no": out_biz_no,
"trans_amount": trans_amount,
"payee_info": payee_info,
"product_code": product_code,
"biz_scene": biz_scene
}
print(biz_content)
biz_content.update(**kwargs)
data = self.build_body("alipay.fund.trans.uni.transfer", biz_content)
url = self._gateway + "?" + self.sign_data(data)
raw_string = compat.urlopen(url, timeout=15).read().decode("utf-8")
return self._verify_and_return_sync_response(raw_string, "alipay_fund_trans_uni_transfer_response")
def api_alipay_trade_create(self, out_trade_no, total_amount, subject, buyer_id, notify_url, **kwargs):
""" 小程序创建订单,返回out_trade_no给前端即可 """
biz_content = {
"out_trade_no": out_trade_no,
"total_amount": total_amount,
"subject": subject,
"buyer_id": buyer_id,
}
print(biz_content)
biz_content.update(**kwargs)
data = self.build_body("alipay.trade.create", biz_content)
# SDK里有个坑,如果不是他设定好的method将不会添加回调url,因此我们自己加上
data['notify_url'] = notify_url
url = self._gateway + "?" + self.sign_data(data)
raw_string = compat.urlopen(url, timeout=15).read().decode("utf-8")
return self._verify_and_return_sync_response(raw_string, "alipay_trade_create")
def api_alipay_system_oauth_token(self, grant_type="authorization_code", code='', **kwargs):
""" 登录验签 """
data = self.build_body("alipay.system.oauth.token", {})
# 登录需要的参数我们也自己添加
data['grant_type'] = grant_type
data['code'] = code
url = self._gateway + "?" + self.sign_data(data)
raw_string = compat.urlopen(url, timeout=15).read().decode("utf-8")
return self._verify_and_return_sync_response(raw_string, "alipay_system_oauth_token")
def api_alipay_user_info_share(self, auth_token, **kwargs):
""" 获取用户信息 """
data = self.build_body("alipay.user.info.share", {})
data['auth_token'] = auth_token
print(data)
url = self._gateway + "?" + self.sign_data(data)
raw_string = compat.urlopen(url, timeout=15).read().decode("utf-8")
return self._verify_and_return_sync_response(raw_string, "alipay_user_info_share")
from util.CBAlipay import CBAliPay
def getCBAlipay(app_id):
""" 初始化对象 """
app_private_key_string = open("./cert/{}/app_private.pem".format(appid)).read()
# app_public_key_cert_string = open("./cert/{}/caibo-inc.com_public.pem".format(appid)).read()
app_public_key_cert_string = open("./cert/{}/app_public.crt".format(appid)).read()
alipay_public_key_cert_string = open("./cert/{}/alipayCertPublicKey_RSA2.crt".format(appid)).read()
alipay_root_cert_string = open("./cert/{}/alipayRootCert.crt".format(appid)).read()
dc_alipay = CBAliPay(
appid=appid,
# 默认回调url
app_notify_url=current_app.config['ALIPAY_NOTIFY_URL'],
app_private_key_string=app_private_key_string,
app_public_key_cert_string=app_public_key_cert_string,
alipay_public_key_cert_string=alipay_public_key_cert_string,
alipay_root_cert_string=alipay_root_cert_string,
sign_type="RSA2"
)
return dc_alipay
def transferRequest(app_id, alipay_user_id, amount):
""" 发红包 """
dc_alipay = getCBAlipay(app_id)
if not dc_alipay:
return False
payee_info = {"identity_type": "ALIPAY_USER_ID", "identity": alipay_user_id}
result = dc_alipay.api_alipay_fund_trans_uni_transfer(out_biz_no=datetime.datetime.now().strftime("%Y%m%d%H%M%S"),
trans_amount=amount, payee_info=payee_info)
print(result)
return result
def alipayOrder(app_id, subject, out_trade_no, buyer_id, total_amount, notify_url):
""" 付款 """
dc_alipay = getCBAlipay(app_id)
if not dc_alipay:
return False
result = dc_alipay.api_alipay_trade_create(subject=subject, out_trade_no=out_trade_no,
total_amount=total_amount, buyer_id=buyer_id,
notify_url=notify_url)
print(result)
return result
def systemOAuthToken(code="", app_id="", grant_type="authorization_code"):
""" 用户登录 """
dc_alipay = getCBAlipay(app_id)
if not dc_alipay:
return False
result = dc_alipay.api_alipay_system_oauth_token(grant_type=grant_type,
code=code)
print(result)
return result
# coding=utf-8
import time
from flask import current_app, request
from . import home
from util import UserFinanceUtil, AlipayUtil
@home.route('/alipay/notify_url', methods=['GET', 'POST'])
def notify():
""" 支付回调 """
print(request.form.to_dict())
data = request.form.to_dict()
if data is None or data == "":
return "error: empty data"
signature = data.pop("sign")
res = 'success'
# 根据订单号动态配置
out_trade_no = data.get('out_trade_no')
# 这里从数据库获取信息,判断是哪个小程序
app_id = ... # 省略
alipay = AlipayUtil.getCBAlipay(app_id)
# verify
success = alipay.verify(data, signature)
print(success)
if success and data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED"):
print("trade succeed")
# 这里已经验签成功了,执行自己需要的操作即可
...
else:
res = 'false'
return res