Flask项目之手机端租房网站的实战开发(十四)
说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家!
接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/86660480
目录
一丶支付接口
1.分析:当客户下单后,房东需要进入客户订单中,将该订单进行接单处理后,那么在客户的我的订单功能中,才能对此订单进行支付操作,当客户点击支付后,接入支付宝支付(这里以支付宝为支付方式,微信的话逻辑也是一样)
2.支付宝开发文档
- step1 进入支付宝开放平台https://open.alipay.com/platform/home.htm,进入开发接入-----查看详情
- 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>爱家租房 享受家的温馨</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 访问如下地址,查看此过渡页面
- 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
Flask项目之手机端租房网站的实战开发(十四)的更多相关文章
- Flask项目之手机端租房网站的实战开发(四)
说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/8 ...
- Flask项目之手机端租房网站的实战开发(三)
说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/8 ...
- Flask项目之手机端租房网站的实战开发(一)
说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 一丶项目介绍 产品:关于手机移动端的租房网站 角色:在这个产品中用户包括房东与房客 功能:房东可以在这个平台发布自己的房屋,房客可 ...
- Flask项目之手机端租房网站的实战开发(二)
说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/8 ...
- Flask项目之手机端租房网站的实战开发(六)
说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/8 ...
- Flask项目之手机端租房网站的实战开发(十一)
说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/8 ...
- Flask项目之手机端租房网站的实战开发(十)
说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/8 ...
- Flask项目之手机端租房网站的实战开发(九)
说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/8 ...
- Flask项目之手机端租房网站的实战开发(八)
说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/8 ...
随机推荐
- Intent传递对象——Serializable和Parcelable差别
前两篇文章讨论了Serializable和Parcelable实现Intent之间传递对象和对象数组的方式.两种方法实现上相似,效果一致,怎么选择用哪种方法实现呢? Intent在不同的组件中传递对象 ...
- Android资源之图像资源(状态图像资源)
在上一篇博文中.我主要解说了XML图像资源中的图层资源,在此图像资源博文中我会给大家陆续解说XMl图像资源的图像状态资源.图像级别资源.淡入淡出资源.嵌入图像资源.剪切图像资源和外形资源. 1.图像状 ...
- Java中AtomicInteger的使用!!!
今天在看Volley的源码的时候,看到里面使用了AtomicInteger这个类,曾经没用过,今天看了一下API学习了一下: 首先介绍一下这个类的用处,这个类主要是用来替换java中的自增和自减操作, ...
- 比较两个文件是否相同(C/C++语言)
#include <stdio.h> #include <string.h> ; // Calculate the file size void Get_file_size(c ...
- JAVA线程队列BlockingQueue
JAVA线程队列BlockingQueue 介绍 BlockingQueue阻塞队列,顾名思义,首先它是一个队列,通过一个共享的队列,可以使得数据由队列的一端输入,从另外一端输出. 常用的队列主要有以 ...
- 洛谷P3807 【模板】卢卡斯定理exgcd
题目背景 这是一道模板题. 题目描述 给定n,m,p(1\le n,m,p\le 10^51≤n,m,p≤105 ) 求 C_{n+m}^{m}\ mod\ pCn+mm mod p 保证P为pri ...
- Python(三) 变量与运算符
一.什么是变量 变量 = [1,2] 二.变量的命名规则 字母,数字,下划线,首字母不能是数字 系统关键字 不能用在变量名中 保留关键字 区别大小写 a=1, a='1', a=(1,2), ...
- PowerDesigner删除外键关系,而不删除外键列[转] 及编码格式
PowerDesigner删除外键关系,而不删除外键列[转] 数据库 database -> generate database ->format 设置为utf-8 PowerDesi ...
- Android开发当中Parcelable接口的使用
本文转载于:http://www.2cto.com/kf/201205/132814.html 本文稍微做了些修改 android提供了一种新的类型:Parcel.本类被用作封装数据的容器,封装后的数 ...
- js003-4方向8方向函数
1,求四方向或者8方向的周围的棋子. /** * pos 1-4, 1-8 4/8方向的周围 * @param {*} pos * @param {*} dir */ var _nearChess = ...