0.支付流程
1. 重点总结
1)用户进入“我的订单”页面,点击“去支付”;触发后端js中的函数,发出ajsx异步请求,调用后端相应接口order_pay(order_id),目的是构建让用户跳转的支付连接地址,并推送给用户进行付款操作;因为请求方式是PUT,因此需要在参数中加入X-CSRFToken;
2)在后端进行逻辑处理,构建出跳转的支付链接地址pay_url(并指出用户完成支付后返回的地址return_url)。
支付链接地址经过js回调函数处理,将此pay_url返回给前端页面,此时自动打开手机支付宝app,用户对相应的订单进行支付操作,
支付完成后,跳转return_url相对应的前端页面paycomplete.html,如下图所示。
3)用户在支付完成后,需要修改数据库中订单的相关信息,比如说订单的状态status,以及支付宝的交易号trade_no,此时前端页面中的js向后端发送ajsx异步请求,向后端相应接口save_order_payment_result()发出逻辑处理。
js中alipayData = document.location.search.substr(1),切割URL中的参数信息,并以form格式将参数信息推送给后端接口,后端接收数据;
为安全起见,借助工具校验参数的合法性,确定参数是支付宝真实发出的;
最后修改数据库的订单状态信息。此时,点击前文中支付成功界面中的“回到我的订单”,会发现订单状态已经从“去支付”变为“发表评论”。
2.后端代码
pay.py详细代码如下:
# coding:utf-8
from . import api
from ihome.utils.commons import login_required
from ihome.models import Order
from flask import g, current_app, jsonify, request
from ihome.utils.response_code import RET
from alipay import AliPay
from ihome import constants, db
import os
@api.route("/orders/<int:order_id>/payment", methods=["POST"])
@login_required
def order_pay(order_id):
"""发起支付宝支付"""
user_id = g.user_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(errno=RET.DBERR, errmsg="数据库异常")
if order is None:
return jsonify(errno=RET.NODATA, errmsg="订单数据有误")
# 创建支付宝sdk的工具对象
alipay_client = AliPay(
appid="2016081600258081",
app_notify_url=None, # 默认回调url
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=True # 默认False
)
# 手机网站支付,需要跳转到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://127.0.0.1:5000/payComplete.html", # 返回的连接地址
notify_url=None # 可选, 不填则使用默认notify url
)
# 构建让用户跳转的支付连接地址
pay_url = constants.ALIPAY_URL_PREFIX + order_string
return jsonify(errno=RET.OK, errmsg="OK", data={"pay_url": pay_url})
@api.route("/order/payment", methods=["PUT"])
def save_order_payment_result():
"""保存订单支付结果"""
alipay_dict = request.form.to_dict()
# 对支付宝的数据进行分离 提取出支付宝的签名参数sign 和剩下的其他数据
alipay_sign = alipay_dict.pop("sign")
# 创建支付宝sdk的工具对象
alipay_client = AliPay(
appid="2016081600258081",
app_notify_url=None, # 默认回调url
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=True # 默认False
)
# 借助工具验证参数的合法性
# 如果确定参数是支付宝的,返回True,否则返回false
result = alipay_client.verify(alipay_dict, alipay_sign)
if result:
# 修改数据库的订单状态信息
order_id = alipay_dict.get("out_trade_no")
trade_no = alipay_dict.get("trade_no") # 支付宝的交易号
try:
Order.query.filter_by(id=order_id).update({"status": "WAIT_COMMENT", "trade_no": trade_no})
db.session.commit()
except Exception as e:
current_app.logger.error(e)
db.session.rollback()
return jsonify(errno=RET.OK, errmsg="OK")
3.前端js
order.js中部分代码:
$.get("/api/v1.0/user/orders?role=custom", function(resp){
if ("0" == resp.errno) {
$(".orders-list").html(template("orders-list-tmpl", {orders:resp.data.orders}));
$(".order-pay").on("click", function () {
var orderId = $(this).parents("li").attr("order-id");
$.ajax({
url: "/api/v1.0/orders/" + orderId + "/payment",
type: "post",
dataType: "json",
headers: {
"X-CSRFToken": getCookie("csrf_token"),
},
success: function (resp) {
if ("4101" == resp.errno) {
location.href = "/login.html";
} else if ("0" == resp.errno) {
// 引导用户跳转到支付宝连接
location.href = resp.data.pay_url;
}
}
});
});
4.前端html
order.html中部分代码:
<li order-id={{order.order_id}}>
<div class="order-title">
<h3>订单编号:{{order.order_id}}</h3>
{{ if "WAIT_COMMENT" == order.status }}
<div class="fr order-operate">
<button type="button" class="btn btn-success order-comment" data-toggle="modal" data-target="#comment-modal">发表评价</button>
</div>
{{ else if "WAIT_PAYMENT" == order.status }}
<div class="fr order-operate">
<button type="button" class="btn btn-success order-pay">去支付</button>
</div>
paycomplete.html代码,此代码中有部分js代码,处理上文中的第三部分逻辑。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>爱家-我的订单</title>
<link href="/static/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/plugins/font-awesome/css/font-awesome.min.css" rel="stylesheet">
<link href="/static/css/reset.css" rel="stylesheet">
<link href="/static/plugins/bootstrap-datepicker/css/bootstrap-datepicker.min.css" rel="stylesheet">
<link href="/static/css/ihome/main.css" rel="stylesheet">
<link href="/static/css/ihome/orders.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="top-bar">
<div class="nav-bar">
<h3 class="page-title">支付完成</h3>
<a class="nav-btn fl" href="/my.html"><span><i class="fa fa-angle-left fa-2x"></i></span></a>
</div>
</div>
<div class="orders-con">
<p> 支付已完成 </p>
<a href="/orders.html">回到我的订单</a>
</div>
<div class="footer">
<p><span><i class="fa fa-copyright"></i></span>爱家租房 享受家的温馨</p>
</div>
</div>
<script src="/static/js/jquery.min.js"></script>
<script src="/static/plugins/bootstrap/js/bootstrap.min.js"></script>
<script src="/static/plugins/bootstrap-datepicker/js/bootstrap-datepicker.min.js"></script>
<script src="/static/plugins/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js"></script>
<script src="/static/js/template.js"></script>
<script type="text/javascript">
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[1] : undefined;
}
var alipayData = document.location.search.substr(1);
$.ajax({
url: "/api/v1.0/order/payment",
type: "put",
data: alipayData,
headers: {
"X-CSRFToken": getCookie("csrf_token")
}
})
</script>
</body>
</html>
版权声明:本文为fanxindong0620原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。