Flask爱家租房--订单支付(支付过程)

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>爱家租房&nbsp;&nbsp;享受家的温馨</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版权协议,转载请附上原文出处链接和本声明。