- 安装包
# 安装python-alipay-sdk
# --upgrade 是你之前安装了pycrypto, 加它是升级, 一次都没安装的用户, 直接pip install python-alipay-sdk
pip install python-alipay-sdk --upgrade
- 生成密钥文件
# openssl window上没有, linux下和mac上都带这个命令
openssl
OpenSSL> genrsa -out app_private_key.pem 2048 # 私钥
OpenSSL> rsa -in app_private_key.pem -pubout -out app_public_key.pem # 导出公钥
OpenSSL> exit
- 把支付宝公钥填写到沙箱环境
- 把私钥放在项目里
- 查看支付宝的公钥
- 把支付宝公钥复制粘贴到你项目的alipay_public_key.pem文件夹里, 记得在头加上
将-----BEGIN PUBLIC KEY-----添加到支付宝公钥的头部
将-----END PUBLIC KEY-----添加到支付宝的尾部
- 初始化
from alipay import AliPay
alipay = AliPay(
appid="", # 沙箱appid 或 线上appid
app_notify_url=None, # 默认回调url 不写的话这里用None
app_private_key_path="", # 私钥
alipay_public_key_path="", # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
sign_type="RSA", # RSA 或者 RSA2
debug=False # 默认False 沙箱环境把这里设置为True
)
- 根据对象发送请求
5.1 电脑网站支付alipay.trade.page.pay
# 如果你是Python 2用户(考虑考虑升级到Python 3吧),请确保非ascii的字符串为utf8编码:
subject = u"测试订单".encode("utf8")
# 如果你是 Python 3的用户,使用默认的字符串即可
subject = "测试订单"
# 电脑网站支付,需要跳转到https://openapi.alipay.com/gateway.do? + order_string 这是线上环境
# 电脑网站支付, 需要跳转到https://openapi.alipaydev.com/gateway.do? + order_string 这是沙箱环境
order_string = 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" # 可选, 不填则使用默认notify url 不填这里写None
)
5.2 手机网站支付 alipay.trade.wap.pay
# 手机网站支付,需要跳转到https://openapi.alipay.com/gateway.do? + order_string 这是线上环境
# 手机网站支付, 需要跳转到https://openapi.alipaydev.com/gateway.do? + order_string 这是沙箱环境
order_string = alipay.api_alipay_trade_wap_pay(
out_trade_no="20161112", # 订单编号
total_amount="0.01", # 总金额
subject=subject, # 订单标题 随便写个字符串
return_url="https://example.com", # 返回的链接地址
notify_url="https://example.com/notify" # 可选, 不填则使用默认notify url 不填这里写None
)
5.3 App支付 alipay.trade.app.pay
# App支付,将order_string返回给app即可
order_string = alipay.api_alipay_trade_app_pay(
out_trade_no="20161112", # 订单id
total_amount=0.01, # 总金额
subject=subject, # 订单标题 随便写个字符串
notify_url="https://example.com/notify" # 可选, 不填则使用默认notify url 不填这里写None
)
- 基于flask的简单验证
from flask import Flask
from flask import request
app = Flask(__name__)
@app.route('/', methods=["GET", "POST"])
def hello_world():
data = request.form.to_dict()
# sign 不能参与签名验证
signature = data.pop("sign")
print(json.dumps(data))
print(signature)
# verify
success = alipay.verify(data, signature)
if success and data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED" ):
print("trade succeed")
- 列子
- 支付宝付款接口链接返回给前端
from . import api
from ihome.models import Order
from ihome.utils.commons import login_required
from flask import g, current_app, jsonify
from ihome.utils.response_code import RET
from ihome import constants
from alipay import AliPay
import os
@api.route("/orders/<int:order_id>/payment", methods=["POST"])
@login_required
def order_pay(order_id):
"""
订单支付
:param order_id: 订单的id
:return: 支付宝支付的链接地址 格式json
"""
# 接收用户id
user_id = g.user_id
# 校验参数
# 根据order_id查询数据库, order_id是否存在, 订单是否属于该用户, 订单是否是待支付状态
try:
order = Order.query.filter(Order.id == order_id, Order.user_id == user_id, Order.status == "WAIT_PAYMENT").first()
except Exception as e:
current_app.logger.error(e)
return jsonify(errnum=RET.DBERR, errmsg=u"获取订单信息失败")
if order is None:
return jsonify(errnum=RET.NODATA, errmsg=u"订单信息不存在")
# 业务处理
# 创建支付宝sdk的工具对象
alipay_client = AliPay(
appid="2016092500596768", # 沙箱appid 或 线上appid
app_notify_url=None, # 默认回调url 不写的话这里用None
# os.path.dirname(__file__): 获取当前文件所在目录的路径
app_private_key_path=os.path.join(os.path.dirname(__file__), "keys/app_private_key.pem"), # 私钥
alipay_public_key_path=os.path.join(os.path.dirname(__file__), "keys/alipay_public_key.pem"), # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
sign_type="RSA2", # RSA 或者 RSA2
debug=False # 默认False 沙箱环境把这里设置为True
)
# 手机网站支付,需要跳转到https://openapi.alipay.com/gateway.do? + order_string 这是线上环境
# 手机网站支付, 需要跳转到https://openapi.alipaydev.com/gateway.do? + order_string 这是沙箱环境
order_string = alipay_client.api_alipay_trade_wap_pay(
out_trade_no=order.id, # 订单编号
total_amount=str(order.amount/100.0), # 总金额
subject=u"订单编号 %s" % order.id, # 订单标题 随便写个字符串
return_url="http://172.16.66.238:5000/orders.html", # 返回的链接地址
notify_url=None # 可选, 不填则使用默认notify url 不填这里写None
)
# 构造让用户跳转的支付链接地址 这里用的是沙箱环境
pay_url = constants.ALIPAY_URL_PREFIX + order_string
# 返回应答
return jsonify(errnum=RET.OK, errmsg=u"ok", data={"pay_url": pay_url})
- 前端回跳的参数
- 验证是否是支付宝发送过来的
@api.route("/order/payment", methods=["PUT"])
def save_order_payment_result():
"""保存订单支付结果"""
# 接收参数
# 将form表单数据转换成字典
alipay_dict = request.form.to_dict()
# 对支付宝的数据进行分离 提取出支付宝的签名参数sign 和剩下的其他数据
alipay_sign = alipay_dict.pop("sign")
# 业务处理
# 创建支付宝sdk的工具对象
alipay_client = AliPay(
appid="2016092500596768", # 沙箱appid 或 线上appid
app_notify_url=None, # 默认回调url 不写的话这里用None
# os.path.dirname(__file__): 获取当前文件所在目录的路径
app_private_key_path=os.path.join(os.path.dirname(__file__), "keys/app_private_key.pem"), # 私钥
alipay_public_key_path=os.path.join(os.path.dirname(__file__), "keys/alipay_public_key.pem"),
# 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
sign_type="RSA2", # RSA 或者 RSA2
debug=False # 默认False 沙箱环境把这里设置为True
)
# 借助工具验证参数的合法性
# 如果确定参数是支付宝的, 返回True, 否则返回False
result = alipay_client.verify(alipay_dict, alipay_sign)
if result:
# 获取订单id
order_id = alipay_dict.get("out_trade_no")
# 获取支付宝交易号
trade_no = alipay_dict.get("trade_no")
try:
# 这里是根据订单id查询这个订单, 并且更新订单里的status和trade_no着两个字段
Order.query.filter_by(id=order_id).update({"status": "WAIT_COMMENT", "trade_no": trade_no})
# 保存到数据库中
db.session.commit()
except Exception as e:
# 有异常记录log日志
current_app.logger.error(e)
# 回滚到提交前
db.session.commit()
# 返回应答
return jsonify(errnum=RET.OK, errmsg=u"ok")
- 线上环境就是把沙箱的appid换成线上的appid, 访问的域名用线上的域名.