Flask项目之手机端租房网站的实战开发(十四)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_41782425/article/details/86699086

说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家!

接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/86660480

目录

一丶支付接口

二丶支付宝支付后端接口编写

三丶支付宝支付测试

四丶获取支付宝支付结果对订单状态进行修改

五丶测试订单支付成功后对订单状态的修改

六丶测试客户评论对订单状态以及订单量的增加

七丶项目优化


一丶支付接口

1.分析:当客户下单后,房东需要进入客户订单中,将该订单进行接单处理后,那么在客户的我的订单功能中,才能对此订单进行支付操作,当客户点击支付后,接入支付宝支付(这里以支付宝为支付方式,微信的话逻辑也是一样)

2.支付宝开发文档

  • step2 然后选择支付应用下的接入文档

  • step3 点击全部文档

  • step4 进入手机网站支付,查看开发说明文档

  

3.支付宝产品流程

  • step1 用户已安装支付宝支付流程:

步骤1:用户在浏览器中访问商家网页应用,选择商品下单、确认购买,进入支付环节,选择支付宝付款,用户点击去支付,如下图1;
步骤2:进入到支付宝支付路由页面,支付宝处理支付请求,并尝试唤起支付宝客户端,如下图2;
步骤3:进入到支付宝页面,调起支付宝支付,出现确认支付界面,如下图3;

图1:

图2:

图3:

步骤4:用户确认收款方和金额,点击立即支付后出现输入密码界面,如下图4;
步骤5:输入正确密码后,支付宝端显示支付结果,如下图5;
步骤6:自动回跳到浏览器中,商家根据付款结果个性化展示订单处理结果,如下图6。
注意:在iOS系统中,唤起支付宝App支付完成后,不会自动回到浏览器或商户App。用户可手工切回到浏览器或商户App。

图4:

图5:

图6:

  • step2 用户未安装支付宝支付流程:

用户未安装支付宝支付流程
步骤1:若用户未安装支付宝客户端,用户进入到支付宝网页收银台,用户登录支付宝账户,如图7和图8;
步骤2:登录成功后,进入付款确认页面,如图9;
步骤3:用户点击确认付款,进入支付密码页面,如图10;
步骤4:用户输入密码,完成支付,展示支付结果,如图11。

图7:

图8:

图9:

图10:

图11:

 

 4.创建应用

  • step1 登录支付宝,进入管理中心

  • step2 填写入住申请

  • step3 提交后进入如下界面

  • step4 进入网页&移动应用列表,进入支付接入

  • step5 创建应用并提交审核,通过后,方可在线上使用 ,需要审核,还有填写营业执照啥的,这样太麻烦,微信有测试环境,那么支付宝同样也有,沙箱就是支付宝测试环境

 

5.沙箱:这是支付宝提供开发人员测试的那么一个环境,这个沙箱环境与线上的环境是一样的,在沙箱环境与线上使用唯一不同的就是APPID,如果需要转换为线上的话,只需要将APPID修改为你的应用的APPID即可,程序代码不需要作任何改变,进入支付宝沙箱,在沙箱账号中分为卖家信息和买家信息,账户余额可以任意充值

6.支付流程图

7.在支付宝开发平台中,只有JAVA PHP 以及.NET三种语言的SDK,在github上也有他人封装好的支付宝支付的SDK

https://github.com/fzlee/alipay/blob/master/README.zh-hans.md

二丶支付宝支付后端接口编写

1.生成秘钥文件

  • step1 在linux系统中有openssl命令,用来生成秘钥的,进入openssl,执行genrsa -out app_private_key.pem 2048命令,生成2048字节的私钥并保存到app_private_key.pem文件中

  • step2 生成完私钥后,执行rsa -in app_private_key.pem -pubout -out app_public_key.pem,对应根据私钥生成公钥

  • step3 在终端中使用cat 查看公钥,复制公钥

  • step4 在沙箱中查看应用公钥点击修改,将刚复制的公钥进行粘贴保存即可

  • step5 在项目文件api_1_0目录下新建keys目录用于保存项目所需的秘钥,再将在linux系统中生成的app_private_key.pem拷贝到keys项目目录下,在keys目录下创建一个alipay_public_key.pem文件,然后在上图中,查看支付宝公钥,复制支付宝公钥,将复制的支付宝公钥粘贴到alipay_public_key.pem文件中

2.逻辑编写 

  • step1 在api_1_0目录下创建一个pay.py模块,用作于支付,然后子啊__init__中导入此模块

  • step2 定义接口路由
@api.route("/orders/<int:order_id>/payment", methods=["POST"])
@login_required
def order_pay(order_id):
    """支付宝支付"""
    pass
  • step3 获取用户id
user_id = g.user_id
  • step4 判断订单的状态,需要满足参数中订单id与客户的订单id一致,当前订单的用户与登录的用户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="数据库异常")
  • step5 判断订单对象是否存在
if not order:
    return jsonify(errno=RET.NODATA, errmsg="订单数据有误")
  • step6 获取应用私钥
app_private_key_path = os.path.join(os.path.dirname(__file__), "keys/app_private_key.pem")
app_private_key_string = open(app_private_key_path).read()
  • step7 获取支付宝公钥
alipay_public_key_path = os.path.join(os.path.dirname(__file__), "keys/alipay_public_key.pem")
alipay_public_key_string = open(alipay_public_key_path).read()
  • step8 创建支付宝sdk工具对象
app_client = AliPay(
    appid="2016092400589177", # 沙箱的appid
    app_notify_url=None,  # 默认回调url
    app_private_key_string=app_private_key_string, # 私钥
    # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
    alipay_public_key_string=alipay_public_key_string,
    sign_type="RSA2", # RSA 或者 RSA2
    debug = True  # 默认False
)
  • step9 手机网站支付,需要跳转到https://openapi.alipaydev.com/gateway.do? + order_string
order_string = app_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/orders.html",
    notify_url=None  # 可选, 不填则使用默认notify url
)
  • step10 构造用户跳转的支付链接地址,并返回正确响应数据
pay_url = constants.ALIPAY_URL_PREFIX + order_string

return jsonify(errno=RET.OK, errmsg="OK", data={"pay_url":pay_url})

三丶支付宝支付测试

1.运行项目,清除浏览器缓存,进入http://127.0.0.1:5000/orders.html我的订单页面

2.在订单编号为5的订单上进行点击去支付, 然后会出现一个检查客户到的页面,如果客户端存在则会在手机上打开支付宝客户端(注:因为这里博主使用的是沙箱进行开发测试,所以在手机端需要安装沙箱版的支付宝,详细说明在开发平台文档中有相关说明,此支付宝只暂不支持IOS)

3.因为博主是在电脑Web上进行测试,所以当环境中没有支付宝客户端,那么就会跳转到支付宝手机网页登录界面,选择支付宝账号登录(左图),输入开发平台沙箱买家账号(右图)进行登录

4.点击下一步后,会出现房屋的编号,以及金额,与我的订单中编号5的的订单信息一致(左图),点击确认付款后,输入支付密码,然后出现(右图)成功界面

 5.点击完成后,根据return_url编写的代码逻辑会跳转到http://127.0.0.1:5000/orders.html订单页面,此时订单5的状态依然显示待支付(左图),那是因为这一块的逻辑还未进行编写,当支付宝支付成功返回正确响应数据给我们时,我们还未在页面中对订单的状态进行修改,如果此时再点击该订单的去支付,则会出现(右图)页面,而不会跳转到订单支付界面,此时如果点击继续支付,则会报错,因为此订单在支付宝后台已经有对应的流水了

四丶获取支付宝支付结果对订单状态进行修改

1.分析:根据代码逻辑当客户进行订单支付后,支付显示成功后,支付宝会携带以下参数跳转到订单页面,正常来说那么在这个页面就应该根据支付宝返回的参数,进行处理,但是放在订单页面进行处理的话就比较麻烦,因为我客户访问我的订单页面时,是不需要携带参数的,而从我的订单页面去访问支付宝支付接口时,成功支付后,返回的我的订单页面是携带了参数的,所以如果要在我的订单页面进行处理的话,需要进行判断到底是客户访问的还是支付宝返回回来的这是其一,其二就是在我的订单页面什么时候让后端去对订单状态进行更改,所以在我的订单页面进行处理的话,逻辑比较复杂,那么最好的方式就是定义一个过渡页面,当支付宝支付成功后,不是直接跳转到我的订单页面,而是跳转到我们定义的过渡页面,在这个过渡页面肯定是有支付宝发送过来的参数,那么就在后端对订单状态进行修改,再跳转到我的订单页面,显示成功支付后的订单状态

前台回跳参数

公共参数:

参数 类型 是否必填 最大长度 描述 示例值
app_id String 32 支付宝分配给开发者的应用ID 2016040501024706
method String 128 接口名称 alipay.trade.wap.pay.return
sign_type String 10 签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 RSA2
sign String 256 支付宝对本次支付结果的签名,开发者必须使用支付宝公钥验证签名 详见示例
charset String 10 编码格式,如utf-8,gbk,gb2312等 utf-8
timestamp String 19 前台回跳的时间,格式"yyyy-MM-dd HH:mm:ss" 2016-08-11 19:36:01
version String 3 调用的接口版本,固定为:1.0 1.0

业务参数:

参数 类型 是否必填 最大长度 描述 示例值
out_trade_no String 64 商户网站唯一订单号 70501111111S001111119
trade_no String 64 该交易在支付宝系统中的交易流水号。最长64位。 2016081121001004630200142207
total_amount Price 9 该笔订单的资金总额,单位为RMB-Yuan。取值范围为[0.01,100000000.00],精确到小数点后两位。 9.00
seller_id String 16 收款支付宝账号对应的支付宝唯一用户号。 以2088开头的纯16位数字 2088111111116894

2.逻辑编写 

  • step1 在static静态文件目录下创建一个payComplete.html订单支付完成页面,将orders.html的代码复制到此文件中,并进行修改
<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 style="font-size: 20px;margin: auto">支付已完成</p>
            <a href="orders.html" style="font-size: 18px">返回到我的订单页</a>
        </div>
        <div class="footer">
            <p><span><i class="fa fa-copyright"></i></span>爱家租房&nbsp;&nbsp;享受家的温馨</p>
        </div>
    </div>
  • step2 在pay.py中将return_url修改为http://127.0.0.1:5000/payComplete.html过渡页面
return_url="http://127.0.0.1:5000/payComplete.html"
  • step3 访问如下地址,查看此过渡页面

http://127.0.0.1:5000/payComplete.html?charset=utf-8&out_trade_no=5&method=alipay.trade.wap.pay.return&total_amount=2544.00&sign=U%2Fl06mjzgZwNEdXo0ePmldLIE6wuMZ4qo2PgyDf4Q%2BRZh1b0KdBqaRkckEBZ136Zyr283FzBlNOMprucQcjH6E9i1Df0ZVRx9HJAm54yN6n%2F1ENFpSOG9znILGbGj%2BV9v%2F0efrcUIvBnYgMIHvp2rBJ8ygRjnhQlTC0q5TVafJQ9OHt%2BX9Ae5F7yiJsb%2B8MVoaG6CDSxqy6MpT2JG7h14alKF6At%2BiJVCN6lbs9RCHZgABabTLDgHbkjJWusElCbi6NEzR%2FHvu9ANSz9onb0RmiyfZ35hwVNw1EATKTsdM47TwYxHxHv%2BFS0gFXdDsQnCYpsH%2F0SCL6aqjK%2Fj1r9eg%3D%3D&trade_no=2019013122001459890500811871&auth_app_id=xxx&version=1.0&app_id=xxx&sign_type=RSA2&seller_id=xxx&timestamp=2019-01-31+10%3A47%3A16

  • step4  在pay.py模块中定义接口路由,用于保存订单支付结果
# /api_v1.0/order/payment
@api.route("/order/payment", methods=["PUT"])
def save_order_payment_result():
    """保存订单支付结果"""
    pass
  • step5 获取支付宝支付成功返回的响应参数并转换为字典格式数据
alipay_dict = request.form.to_dict()
  • step6 对支付宝的数据进行分离 将签名参数sign提取出去并获取值
alipay_sign = alipay_dict.pop("sign")
  • step7 获取应用私钥和支付宝公钥
# 获取应用私钥
app_private_key_path = os.path.join(os.path.dirname(__file__), "keys/app_private_key.pem")
app_private_key_string = open(app_private_key_path).read()
# 获取支付宝公钥
alipay_public_key_path = os.path.join(os.path.dirname(__file__), "keys/alipay_public_key.pem")
alipay_public_key_string = open(alipay_public_key_path).read()
  • step8 创建支付宝sdk工具对象
app_client = AliPay(
    appid="2016092400589177",  # 沙箱的appid
    app_notify_url=None,  # 默认回调url
    app_private_key_string=app_private_key_string,  # 应用私钥
    # 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
    alipay_public_key_string=alipay_public_key_string, # 支付宝公钥
    sign_type="RSA2",  # RSA 或者 RSA2
    debug=True  # 默认False
)
  • step9 通过支付宝sdk工具对象中的verify方法验证支付宝返回的响应参数中剔除sign参数后,剩余参数与响应参数中的sign值是否一致,如果确定参数是支付宝的,返回True,否则返回false
result = app_client.verify(alipay_dict, alipay_sign)
  • step10 验证正确则修改数据库的订单状态信息,最后返回正确响应
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.更新数据库ih_order_info表字段,增加trade_no字段

说明:因为当初在创建数据库时,未设置订单对应的支付宝交易流水号的字段,所以这里进行一个更新

  • step1 在models.py中找到Order类,添加此字段
trade_no = db.Column(db.String(128)) # 支付宝交易流水号
  • step2 生成迁移文件并对数据库进行更新操作

  • step3 查看数据库ih_order_info表结构,添加trade_no字段成功

4.在payComplete.html中编写js代码,向后端/api/v1.0/order/payment接口发送请求,并携带支付宝支付成功返回的参数

<script type="text/javascript">
    function getCookie(name) {
        var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
        return r ? r[1] : undefined;
    }
    // 获取支付宝返回的url中的参数,并通过substr方法从去掉第一个元素?,因为返回的url参数是以键值对方式进行构造的,所以这里是以请求体数据进行发送给后端接口
    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>

 五丶测试订单支付成功后对订单状态的修改

1.运行项目,清除页面缓存,刷新网页,进入我的订单(左图),因为前面测试支付时,对订单编号为5的订单已经支付成功,所以对应的在支付宝后台中已经形成了此订单编号为5的交易号,所以这里需从数据库中将该订单的编号进行修改,这里修改为100(右图)

update ih_order_info set id= 100 where id = 5;

2.然后将订单编号为100的订单进行支付(左图),点击确认付款后,支付宝显示成功后,则跳转到payComplete.html页面,在此页面中点击返回我的订单页,此订单状态为发表评价(右图),按理说当订单支付成功后,该订单状态显示为已支付,等待用户退房或者完成住宿后,才会出现待评价,这个逻辑被直接省掉了,在此项目中用户一旦支付成功则该订单显示发表评价

 3.第一次使用支付宝支付接口的朋友们,一般不知道支付宝会收取手续费的,那么博主就带大家看看支付宝的手续费是多少

  • step1 修改我的订单中订单标号为1的订单,将编号改为101,来进行测试,这个房屋之前博主测试过电脑网页支付接口,所以是需要进行编号修改的

  • step2 查看蚂蚁金服开放平台---沙箱账号中商家的账户余额为2009144.81,测试订单的金额为4112,没有手续费的情况下,客户支付此单后,商家的账户余额就应该为2013256.81

  • step3  支付编号为101的订单,下面展示支付流程图

 

  • step4 点击返回到我的订单页后,订单标号为101的订单显示为可以发表评价

  •  step5 在step2时,如果没有支付宝不收取手续费的情况下,客户在支付4112元订单后,商家的账户余额应该为2013256.81元,那么此时在商家信息看看商家的账户余额到底是不是这么多钱,结果发现商家的余额是2013232.14元

  • step6 计算下支付宝的收费需收取比例是多少,原来是千分之6,还是比较多的

六丶测试客户评论对订单状态以及订单量的增加

1.对订单编号为100的订单(左图),点击发表评论按钮,弹出评论框(右图)

 

2.点击确定按钮后,该订单的状态变成已完成,显示客户评论

 

3.查看数据库房屋信息表ih_user_info,订单量order_count值从0变成1,代码逻辑当订单状态变成已完成则order_count加1

 

七丶项目优化

1.解决csrf_token缺失的bug,INFO csrf.py:251 The CSRF token is missing.,在用户进行退出时,不将session数据全部清除,需要将csrf_token的数据保存到session中

# 在清除session数据时,先从session中获取csrf_token的值
csrf_token = session.get("csrf_token")
session.clear()
# 当session数据清除完后 再设置session中的csrf_token的值,这样可以解决csrf_token缺失的bug
session["csrf_token"] = csrf_token

2.数据库优化:

a. 表结构设计  扩展  查询的快慢
    三范式  https://www.zhihu.com/question/24696366
    设计的时候就考虑可能会用到的查询,为方便查询而设计,比如用空间换时间,适当增加冗余字段节省查询开销

b. 建索引   主键  唯一(unique)  索引(key   index)  (外键)
    http://www.jianshu.com/p/2b541c028157
    提升 查询速度  复合索引  where k1     k2   k3  k4
    降低   增删改

c. sql语句优化
    使用索引 注意关键字顺序 最左原则  where

    不要select *

    能使用联合查询,不使用嵌套(子查询)

    select  from tbl_a a inner join tbl_b b on a.field=b.filed where b.=

    select from tbl_a where filed=(select field from tbl_b where b.=)

    能不使用联合查询,尽量不用联合查询

    外键 cascade 级联 (维护外键有额外开销,影响性能)  数据量大的时候,不再使用外键

    使用分析工具分析效率低的sql语句   慢查询工具
    https://flyerboy.github.io/2016/12/23/mysql_slow/
    https://yemengying.com/2016/05/24/mysql-tuning/

d. 缓存
    redis memcached

e. 读写分离
    主从热备份       主(写   增删改)  从(查)

f. 分库分表  水平分库分表
    http://www.infoq.com/cn/articles/key-steps-and-likely-problems-of-split-table
 

猜你喜欢

转载自blog.csdn.net/qq_41782425/article/details/86699086