序言:
使用语言:python3
框架:flask
支付宝环境:沙箱环境
支付宝电脑网站支付文档:https://docs.open.alipay.com/270
对接的主要步骤:
1、在支付宝申请开发pc网页支付功能的应用,配置公匙和私匙(此步骤是实际生产环境所需要的)。
2、配置沙箱环境
3、客户端向服务器发起支付请求,请求包含参数:支付方式(支付宝、微信、等)、支付总金额
4、服务器接收客户端发送的带参数请求,生成订单编号,向数据库写入订单信息(订单编号,用户ID,支付总金额,订单创建时间,支付状态,支付方式)
5、调用支付宝接口并填写相关参数,生成包含支付宝支付二维码的链接,return redirect到此链接
6、买家扫码支付
7、同步接收支付宝的状态通知,校验是否success,向客户端返回支付成功或支付失败的页面
8、异步接收支付宝的状态通知,校验是否success,并校验订单编号和支付总金额与数据库里保存的订单信息是否相同,如果都校验成功,把之前写入数据库的订单信息补全(支付编号,支付时间,修改支付状态)
1、在支付宝申请应用
支付宝开放平台:https://open.alipay.com/platform/home.htm
-
开发者中心→网页&移动应用→创建应用→支付接入
-
填写应用名称、图标等相关信息并创建应用
-
在功能列表中选择需要的功能,例如:电脑网站支付(可以点击添加功能按钮后进行搜索)
-
根据需要设置:应用网关、授权回调地址、加签方式(私匙和公匙)
-
生成应用的私钥和公钥(使用openssl生成或支付宝密匙生成器生成)
openssl OpenSSL> genrsa -out app_private_key.pem 2048 # 私钥RSA2 OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # 导出公钥 OpenSSL> exit
-
保存私匙文件
- 在payment应用中新建keys目录,用来保存秘钥文件。
- 将应用私钥文件app_private_key.pem复制到payment/keys目录下。
-
查看公钥
-
将公钥内容复制给支付宝
cat app_publict_key.pem
-
-
保存支付宝公钥
-
在payment/keys目录下新建alipay_public_key.pem文件,用于保存支付宝的公钥文件。
将支付宝的公钥内容复制到alipay_public_key.pem文件中
-
注意,还需要在公钥文件中补充开始与结束标志
-----BEGIN PUBLIC KEY----- 此处是公钥内容 -----END PUBLIC KEY-----
-
-
2、配置沙箱环境
- 与上步一样配置授权回调地址、公匙、私匙等信息
- 下载支付宝沙箱钱包(安卓手机)
- 点击沙箱账号,查看买家账号密码和卖家账号密码,可在支付宝沙箱钱包登录,方便后期测试。
3、配置客户端
- 可以下载客户端模板:链接:https://pan.baidu.com/s/1jJGRIet_gaPVHkbZ_3KAOQ 密码:bzsw
- 修改模板,最终效果:点击确认支付时,向服务器发送请求并携带参数:支付方式(支付宝、微信、等)、支付总金额
4、服务器接收请求
-
下载并安装python sdk,查看他的说明文档,有详细的说明
-
配置服务器的config.py文件
# alipay的APPID
ALIPAY_APPID = "2016092300580301"
# 是否开启沙箱环境
ALIPAY_DEBUG = True
ALIPAY_URL = "https://openapi.alipaydev.com/gateway.do"
- 生成订单编号,向数据库写入订单信息(订单编号,用户ID,支付总金额,订单创建时间,支付状态,支付方式)【见步骤5的代码】
####5、调用支付宝接口并填写相关参数,生成包含支付宝支付二维码的链接alipay_url,return redirect到此链接
@payment_blue.route('/pay', methods=['get'])
@user_login_data
def pay():
"""充值页面"""
user = g.user
total_amount = request.args.get("docVlGender", None)
pay_method = request.args.get("radio1", None)
try:
userid = user.id
except: # 用户没登录
return redirect(url_for('users.login_html'))
if total_amount == None or pay_method == None:
data = {
"user": user
}
return render_template('pay.html', data=data)
else:
# 生成订单编号
order_id = datetime.now().strftime('%Y%m%d%H%M%S') + "{:0>8}".format(str(userid))
print("订单编号:%s" % order_id)
# 保存订单
create_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
sql_str = "insert into tb_order_paid(order_id,user_id,total_amount,create_time,paid_status,paid_method) values ('%s',%d,'%s','%s',0,'alipay')" % (order_id, userid, total_amount, create_time)
ret = executesql(sql_str)
if ret:
if pay_method == "wechatpay":
# TODO 开发微信支付接口
data = {
"user": user
}
return render_template('pay.html', data=data)
if pay_method == "alipay":
# 初始化alipay
alipay_client = AliPay(
appid=config.ALIPAY_APPID,
app_notify_url=None, # 默认回调url
app_private_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)),
"keys/rsa_private_key.pem"),
# 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
alipay_public_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)),
"keys/alipay_public_key.pem"),
sign_type="RSA2", # RSA 或者 RSA2
debug=config.ALIPAY_DEBUG # 默认False 沙箱环境为True
)
# 电脑网站支付,需要跳转到https://openapi.alipay.com/gateway.do? + order_string
order_string = alipay_client.api_alipay_trade_page_pay(
out_trade_no=order_id,
total_amount=str(total_amount),
subject="趣看小说%s" % order_id,
return_url="http://www.qukanxs.site:6001/pay_success",
notify_url="http://www.qukanxs.site:6001/status" # 支付宝支付成功异步通知地址
)
alipay_url = config.ALIPAY_URL + "?" + order_string
return redirect(alipay_url)
else:
data = {
"user": user
}
return render_template('pay.html', data=data)
6、客户端会自动跳转到支付宝的扫码支付页面,买家扫码支付
####7、同步接收支付宝的状态通知,校验是否success,向客户端返回支付成功或支付失败的页面
@payment_blue.route('/pay_success', methods=['get'])
@user_login_data
def alipay_pay_success():
"""支付宝保存支付成功的返回信息"""
user = g.user
data = request.args.to_dict()
print("data=%s" % data)
signature = data.pop("sign")
print("signature=%s" % signature)
print("data=%s" % data)
# 初始化alipay
alipay_client = AliPay(
appid=config.ALIPAY_APPID,
app_notify_url="http://www.qukanxs.site:6001/status", # 默认回调url
app_private_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)),
"keys/rsa_private_key.pem"),
# 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
alipay_public_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)),
"keys/alipay_public_key.pem"),
sign_type="RSA2", # RSA 或者 RSA2
debug=config.ALIPAY_DEBUG # 默认False 沙箱环境为True
)
success = alipay_client.verify(data, signature)
if success:
order_id = data["out_trade_no"]
else:
trade_id = None
data = {
"trade_id": trade_id,
"user": user
}
return render_template('pay_success.html', data=data)
- pay_success.html页面
{% extends 'base.html' %}
{% block title_block %}
支付完成
{% endblock %}
{% block detail_block %}
<div class="common_list_con clearfix">
<div class="order_success">
{% if data.trade_id %}
<p><b>订单支付成功</b></p>
<p>您的订单已成功支付,支付交易号:{{ data.trade_id }}</p>
{% else %}
<p><b>订单支付失败</b></p>
<p>请返回支付页面重新支付</p>
{% endif %}
</div>
</div>
{% endblock %}
{% block foot_block %}
<div class="footer">
<hr>
<p>本站所有小说为转载作品,所有章节均由网友上传,转载至本站只是为了宣传本书让更多读者欣赏。</p>
<p>Copyright © 趣看小说 All Rights Reserved. </p>
<script>tj();</script>
</div>
</body>
</html>
{% endblock %}
####8、异步接收支付宝的状态通知,校验是否success,并校验订单编号和支付总金额与数据库里保存的订单信息是否相同,如果都校验成功,把之前写入数据库的订单信息补全(支付编号,支付时间,修改支付状态)
@payment_blue.route('/status', methods=['post'])
@user_login_data
def alipay_pay_status():
print("haha")
data = request.form.to_dict()
print("data=%s" % data)
signature = data.pop("sign")
print("signature=%s" % signature)
print("data=%s" % data)
# 初始化alipay
alipay_client = AliPay(
appid=config.ALIPAY_APPID,
app_notify_url=None, # 默认回调url
app_private_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)),
"keys/rsa_private_key.pem"),
# 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
alipay_public_key_path=os.path.join(os.path.dirname(os.path.abspath(__file__)),
"keys/alipay_public_key.pem"),
sign_type="RSA2", # RSA 或者 RSA2
debug=config.ALIPAY_DEBUG # 默认False 沙箱环境为True
)
success = alipay_client.verify(data, signature)
if success and data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED" ):
order_id = data["out_trade_no"]
trade_id = data["trade_no"]
total_amount = data["total_amount"]
paid_time = data["timestamp"]
# 判断订单编号,支付金额是否相同
sql_str = "select user_id from tb_order_paid where order_id='%s' and total_amount='%s'" % (order_id, total_amount)
user_id = selectsql(sql_str)[0] if selectsql(sql_str) else None
if user_id:
# 增加用户金币数量
sql_str = "update tb_users set golds=(%d+golds) where id=%d;" % (float(total_amount) * 100, user_id)
executesql(sql_str)
sql_str = "update tb_order_paid set trade_id=%s,paid_time='%s',paid_status=1 where order_id='%s'" % (trade_id, paid_time, order_id)
executesql(sql_str)
return "success"
return False
- 接收不到支付宝支付成功异步通知可能的原因:https://openclub.alipay.com/read.php?tid=1677&fid=25&page=1
- 关于支付宝支付成功异步通知的说明:https://docs.open.alipay.com/270/105902/
- 注意:
- 上面写的异步通知代码未测试是否成功,因为异步通知的回调地址必须是公网可以访问的,因为项目还未上线,暂无公网可以访问的网址
- 不可以使用同步的通知结果(return_url=“http://www.qukanxs.site:6001/pay_success”,)来修改数据库的订单信息,因为如果用户支付成功后立刻关闭网页,就接收不到同步的通知,数据库就不会更新数据