day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知

时间:2023-03-09 01:30:18
day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知

目录

1.基于支付宝提供的沙箱测试环境开发支付接口

  1.后端提供创建充值订单接口

  2.前端调用AlipayPlus发起支付

  3.注意:自定义APPLoader完成接下来的开发

  4.下载支付宝沙箱钱包APP

  5.充值成功/失败的提示

2.服务端处理支付结果的同步通知和异步通知

  1.异步通知结果处理

  2.同步通知结果处理

BUG:修复页面底部菜单无法被点击的BUG

MoFang充值流程图

day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知

1.基于支付宝提供的沙箱测试环境开发支付接口

沙箱环境: https://openhome.alipay.com/platform/appDaily.htm?tab=info

1.后端提供创建充值订单接口

服务端提供充值api接口,user/views.py代码:

day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知
from application import jsonrpc
from .models import Recharge
from datetime import datetime
from alipay import AliPay
from alipay.utils import AliPayConfig
import os, json, random
from flask import current_app @jsonrpc.method("Recharge.create")
@jwt_required # 验证jwt
def create_recharge(money=10):
"""创建充值订单"""
current_user_id = get_jwt_identity()
user = User.query.get(current_user_id)
if user is None:
return {
"errno": status.CODE_NO_USER,
"errmsg": message.user_not_exists,
}
order_number = datetime.now().strftime("%y%m%d%H%M%S") + "%08d" % user.id + "%04d" % random.randint(0, 9999) recharge = Recharge(
status=False,
out_trade_number=order_number,
name="账号充值-%s元" % money,
user_id=user.id,
money=money
)
db.session.add(recharge)
db.session.commit() # 创建支付宝sdk对象
app_private_key_string = open(os.path.join(current_app.BASE_DIR, "application/apps/users/keys/app_private_key.pem")).read()
alipay_public_key_string = open(os.path.join(current_app.BASE_DIR, "application/apps/users/keys/app_public_key.pem")).read() alipay = AliPay(
appid= current_app.config.get("ALIPAY_APP_ID"),
app_notify_url=None, # 默认回调url
app_private_key_string=app_private_key_string,
# 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
alipay_public_key_string=alipay_public_key_string,
sign_type=current_app.config.get("ALIPAY_SIGN_TYPE"),
debug = False, # 默认False
config = AliPayConfig(timeout=15) # 可选, 请求超时时间
) order_string = alipay.api_alipay_trade_app_pay(
out_trade_no=recharge.out_trade_number, # 订单号
total_amount=float(recharge.money), # 订单金额
subject=recharge.name, # 订单标题
notify_url=current_app.config.get("ALIPAY_NOTIFY_URL") # 服务端的地址,自定义一个视图函数给alipay
) return {
"errno": status.CODE_OK,
"errmsg": message.ok,
"sandbox": current_app.config.get("ALIPAY_SANDBOX"),
"order_string": order_string,
"order_number": recharge.out_trade_number,
}

后端提供创建充值订单接口

配置文件dev.py,代码:

    # 支付宝配置信息
ALIPAY_APP_ID = "2016091600523592"
ALIPAY_SIGN_TYPE = "RSA2"
ALIPAY_NOTIFY_URL = "https://example.com/notify"
ALIPAY_SANDBOX = True

2.前端调用AlipayPlus发起支付

客户端发起充值请求,并调用alipayplus模块发起支付.orchard.html代码

day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知
<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
<div class="header">
<div class="info" @click="go_home">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<p class="user_name">好听的昵称</p>
</div>
<div class="wallet">
<div class="balance" @click="user_recharge">
<p class="title"><img src="../static/images/money.png" alt="">钱包</p>
<p class="num">99,999.00</p>
</div>
<div class="balance">
<p class="title"><img src="../static/images/integral.png" alt="">果子</p>
<p class="num">99,999.00</p>
</div>
</div>
<div class="menu-list">
<div class="menu">
<img src="../static/images/menu1.png" alt="">
排行榜
</div>
<div class="menu">
<img src="../static/images/menu2.png" alt="">
签到有礼
</div>
<div class="menu" @click="go_orchard_shop">
<img src="../static/images/menu3.png" alt="">
道具商城
</div>
<div class="menu">
<img src="../static/images/menu4.png" alt="">
邮件中心
</div>
</div>
</div>
<div class="footer" >
<ul class="menu-list">
<li class="menu">新手</li>
<li class="menu">背包</li>
<li class="menu-center" @click="go_orchard_shop">商店</li>
<li class="menu">消息</li>
<li class="menu">好友</li>
</ul>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
namespace: '/mofang_orchard',
token:"",
socket: null,
recharge_list: ['10','20','50','100','200','500','1000'],
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.game.goFrame("orchard","my_orchard.html", this.current,{
x: 0,
y: 180,
w: 'auto',
h: 'auto',
},null);
this.checkout();
},
methods:{
user_recharge(){
// 发起充值请求
api.actionSheet({
title: '余额充值',
cancelTitle: '取消',
buttons: this.recharge_list
}, (ret, err)=>{
if( ret ){
if(ret.buttonIndex <= this.recharge_list.length){
// 充值金额
money = this.recharge_list[ret.buttonIndex-1];
// 调用支付宝充值
this.create_recharge(money);
}
}else{ }
}); },
create_recharge(money){
// 获取历史信息记录
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this, token, (new_access_token)=>{
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.create",
"params": {
"money": money,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
this.game.print(response.data);
if(parseInt(response.data.result.errno)==1000){
// ***前往支付宝***
orderInfo = response.data.result.order_string;
var aliPayPlus = api.require('aliPayPlus');
aliPayPlus.payOrder({
orderInfo: orderInfo,
sandbox: true, // 将来APP上线需要修改成false
}, (ret, err)=>{
this.game.print(ret,true);
api.alert({
title: '支付结果',
msg: ret.code,
buttons: ['确定']
});
});
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
})
},
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
});
},
connect(){
// socket连接
this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("开始连接服务端");
});
},
go_index(){
this.game.outWin("orchard");
},
go_friends(){
this.game.goFrame("friends","friends.html",this.current);
this.game.goFrame("friend_list","friend_list.html",this.current,{
x: 0,
y: 190,
w: 'auto',
h: 'auto',
},null,true);
},
go_home(){
this.game.goWin("user","user.html", this.current);
},
go_orchard_shop(){
// 种植园商店
this.game.goFrame("orchard_shop","shop.html", this.current,null,{
type:"push",
subType:"from_top",
duration:300
});
}
}
});
}
</script>
</body>
</html>

前端调用AlipayPlus发起支付

3.注意:自定义APPLoader完成接下来的开发

基于支付宝模块唤醒支付宝APP,实现支付.

因为我们使用的支付宝模块是第三方模块,所以在前面下载安装AppLoader里面是没有对应模块代码的,所以如果继续使用AppLoader进行模块功能使用,则会报错如下:

day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知

所以我们需要进行自定义AppLoader,可以在本地编辑器中项目目录菜单中选择云编译自定义AppLoader,,也可以到官网网站用户后台中心进行编译生成自定义的APPLoader.

方式1: 在本地编辑器中选择云编译自定义AppLoader

day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知

方式2: 在官网的用户后台中心生成自定义AppLoader

day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知

得到自定义AppLoader以后,在测试手机或者安卓模拟器中, 安装自定义AppLoader然后进行功能测试!

4.下载支付宝沙箱钱包APP

同时, 测试之前,因为本次开发的功能是支付宝支付功能,所以我们还需要到支付宝沙箱应用后台下载一个支付宝沙箱钱包App到当前手机或者模拟器中.否则无法完成测试支付过程.

下载: https://sandbox.alipaydev.com/user/downloadApp.htm

客户端发起支付代码:

注意:

在实际过程中, 我们使用的正式的支付宝APP,所以在客户端中sandbox参数的值必须作为配置,引入.

5.充值成功/失败的提示

orchard.html代码

day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知
<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
<div class="header">
<div class="info" @click="go_home">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<p class="user_name">好听的昵称</p>
</div>
<div class="wallet">
<div class="balance" @click="user_recharge">
<p class="title"><img src="../static/images/money.png" alt="">钱包</p>
<p class="num">99,999.00</p>
</div>
<div class="balance">
<p class="title"><img src="../static/images/integral.png" alt="">果子</p>
<p class="num">99,999.00</p>
</div>
</div>
<div class="menu-list">
<div class="menu">
<img src="../static/images/menu1.png" alt="">
排行榜
</div>
<div class="menu">
<img src="../static/images/menu2.png" alt="">
签到有礼
</div>
<div class="menu" @click="go_orchard_shop">
<img src="../static/images/menu3.png" alt="">
道具商城
</div>
<div class="menu">
<img src="../static/images/menu4.png" alt="">
邮件中心
</div>
</div>
</div>
<div class="footer" >
<ul class="menu-list">
<li class="menu">新手</li>
<li class="menu">背包</li>
<li class="menu-center" @click="go_orchard_shop">商店</li>
<li class="menu">消息</li>
<li class="menu">好友</li>
</ul>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
namespace: '/mofang_orchard',
token:"",
socket: null,
recharge_list: ['10','20','50','100','200','500','1000'],
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.game.goFrame("orchard","my_orchard.html", this.current,{
x: 0,
y: 180,
w: 'auto',
h: 'auto',
},null);
this.checkout();
},
methods:{
user_recharge(){
// 发起充值请求
api.actionSheet({
title: '余额充值',
cancelTitle: '取消',
buttons: this.recharge_list
}, (ret, err)=>{
if( ret ){
if(ret.buttonIndex <= this.recharge_list.length){
// 充值金额
money = this.recharge_list[ret.buttonIndex-1];
// 调用支付宝充值
this.create_recharge(money);
}
}else{ }
}); },
create_recharge(money){
// 获取历史信息记录
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this, token, (new_access_token)=>{
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.create",
"params": {
"money": money,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
this.game.print(response.data);
if(parseInt(response.data.result.errno)==1000){
// 前往支付宝
var aliPayPlus = api.require('aliPayPlus');
aliPayPlus.payOrder({
orderInfo: response.data.result.order_string,
sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
}, (ret, err)=>{
// ***增加充值成功失败的提醒***
pay_result = {
9000:"支付成功",
8000:"正在处理中",
4000:"订单支付失败",
5000:"重复请求",
6001:"取消支付",
6002:"网络连接出错",
6004:"支付结果未知",
}
api.alert({
title: '支付结果',
msg: pay_result[ret.code],
buttons: ['确定']
});
// 通知服务端, 修改充值结果
});
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
})
},
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
});
},
connect(){
// socket连接
this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("开始连接服务端");
});
},
go_index(){
this.game.outWin("orchard");
},
go_friends(){
this.game.goFrame("friends","friends.html",this.current);
this.game.goFrame("friend_list","friend_list.html",this.current,{
x: 0,
y: 190,
w: 'auto',
h: 'auto',
},null,true);
},
go_home(){
this.game.goWin("user","user.html", this.current);
},
go_orchard_shop(){
// 种植园商店
this.game.goFrame("orchard_shop","shop.html", this.current,null,{
type:"push",
subType:"from_top",
duration:300
});
}
}
});
}
</script>
</body>
</html>

前端增加充值成功/失败的提醒

2.服务端处理支付结果的同步通知和异步通知

1.异步通知结果处理

users/views.py,代码:

from flask import request
def notify_response():
"""支付宝支付结果的异步通知处理"""
data = request.form.to_dict()
# sign 不能参与签名验证
signature = data.pop("sign") app_private_key_string = open(os.path.join(current_app.BASE_DIR, "application/apps/users/keys/app_private_key.pem")).read()
alipay_public_key_string = open(os.path.join(current_app.BASE_DIR, "application/apps/users/keys/app_public_key.pem")).read() alipay = AliPay(
appid= current_app.config.get("ALIPAY_APP_ID"),
app_notify_url=None, # 默认回调url
app_private_key_string=app_private_key_string,
# 支付宝的公钥,验证支付宝回传消息使用,不是你自己的公钥,
alipay_public_key_string=alipay_public_key_string,
sign_type=current_app.config.get("ALIPAY_SIGN_TYPE"),
debug = False, # 默认False
config = AliPayConfig(timeout=15) # 可选, 请求超时时间
) # verify
success = alipay.verify(data, signature)
if success and data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED" ):
"""充值成功"""
out_trade_number = data["out_trade_no"]
recharge = Recharge.query.filter(Recharge.out_trade_number==out_trade_number).first()
if recharge is None:
return "fail"
recharge.status=True
user = User.query.get(recharge.user_id)
if user is None:
return "fail"
user.money+=recharge.money
db.session.commit()
return "success" # 必须只能是success

绑定路由, users/urls.py,代码:

from . import views
from application.utils import path
urlpatterns = [
....
path("/alipay/notify", views.notify_response),
]

tip:改写路由绑定的path方法

上面代码,我们已经完成了异步处理,但是我们要在前期的项目代码处理中, 没有实现视图方法的绑定操作,所以上面的代码视图方法只能被GET请求.

所以需要改在utils/__init__.py中路由绑定的path方法,代码:

def path(rule,func_view,**kwargs):
# 把蓝图下视图和路由之间的映射关系处理成字典结构,方便后面注册蓝图的时候,直接传参
return {"rule":rule,"view_func":func_view,**kwargs}

users/urls.py代码:

from . import views
from application.utils import path
urlpatterns = [
....
path("/alipay/notify", views.notify_response,methods=["POST","GET"]),
]

完成上面的操作以后, 异步处理的视图方法就可以被外界使用POST请求访问了.

2.同步通知结果处理

1.同步通知结果处理后端接口

users/views.py,代码:

@jsonrpc.method("Recharge.return")
@jwt_required # 验证jwt
def return_recharge(out_trade_number):
"""同步通知处理"""
current_user_id = get_jwt_identity()
user = User.query.get(current_user_id)
if user is None:
return {
"errno": status.CODE_NO_USER,
"errmsg": message.user_not_exists,
} recharge = Recharge.query.filter(Recharge.out_trade_number==out_trade_number).first()
if recharge is None:
return {
"errno": status.CODE_RECHARGE_ERROR,
"errmsg": message.recharge_not_exists,
} recharge.status=True
user.money+=recharge.money
db.session.commit()
return {
"errno": status.CODE_OK,
"errmsg": message.ok,
"money": user.money,
}

2.前端支付完成后要通知后端修改充值结果(同步结果通知)

客户端, orchard.html, 代码:

day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知
<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
<div class="header">
<div class="info" @click="go_home">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<p class="user_name">好听的昵称</p>
</div>
<div class="wallet">
<div class="balance" @click="user_recharge">
<p class="title"><img src="../static/images/money.png" alt="">钱包</p>
<p class="num">{{money}}</p>
</div>
<div class="balance">
<p class="title"><img src="../static/images/integral.png" alt="">果子</p>
<p class="num">99,999.00</p>
</div>
</div>
<div class="menu-list">
<div class="menu">
<img src="../static/images/menu1.png" alt="">
排行榜
</div>
<div class="menu">
<img src="../static/images/menu2.png" alt="">
签到有礼
</div>
<div class="menu" @click="go_orchard_shop">
<img src="../static/images/menu3.png" alt="">
道具商城
</div>
<div class="menu">
<img src="../static/images/menu4.png" alt="">
邮件中心
</div>
</div>
</div>
<div class="footer" >
<ul class="menu-list">
<li class="menu">新手</li>
<li class="menu">背包</li>
<li class="menu-center" @click="go_orchard_shop">商店</li>
<li class="menu">消息</li>
<li class="menu">好友</li>
</ul>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
namespace: '/mofang_orchard',
token:"",
money:"",
socket: null,
recharge_list: ['10','20','50','100','200','500','1000'],
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.game.goFrame("orchard","my_orchard.html", this.current,{
x: 0,
y: 180,
w: 'auto',
h: 'auto',
},null);
this.checkout();
this.money = this.game.fget("money")
},
methods:{
user_recharge(){
// 发起充值请求
api.actionSheet({
title: '余额充值',
cancelTitle: '取消',
buttons: this.recharge_list
}, (ret, err)=>{
if( ret ){
if(ret.buttonIndex <= this.recharge_list.length){
// 充值金额
money = this.recharge_list[ret.buttonIndex-1];
// 调用支付宝充值
this.create_recharge(money);
}
}else{ }
}); },
create_recharge(money){
// 获取历史信息记录
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this, token, (new_access_token)=>{
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.create",
"params": {
"money": money,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
// 前往支付宝
var aliPayPlus = api.require('aliPayPlus');
aliPayPlus.payOrder({
orderInfo: response.data.result.order_string,
sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
}, (ret, err)=>{
pay_result = {
9000:"支付成功",
8000:"正在处理中",
4000:"订单支付失败",
5000:"重复请求",
6001:"取消支付",
6002:"网络连接出错",
6004:"支付结果未知",
}
api.alert({
title: '支付结果',
msg: pay_result[ret.code],
buttons: ['确定']
});
// ***通知服务端, 修改充值结果***
this.return_recharge(response.data.result.order_number,token);
});
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
})
},
return_recharge(out_trade_number,token){
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.return",
"params": {
"out_trade_number": out_trade_number,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
this.money = response.data.result.money.toFixed(2);
}
})
},
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
});
},
connect(){
// socket连接
this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("开始连接服务端");
});
},
go_index(){
this.game.outWin("orchard");
},
go_friends(){
this.game.goFrame("friends","friends.html",this.current);
this.game.goFrame("friend_list","friend_list.html",this.current,{
x: 0,
y: 190,
w: 'auto',
h: 'auto',
},null,true);
},
go_home(){
this.game.goWin("user","user.html", this.current);
},
go_orchard_shop(){
// 种植园商店
this.game.goFrame("orchard_shop","shop.html", this.current,null,{
type:"push",
subType:"from_top",
duration:300
});
}
}
});
}
</script>
</body>
</html>

前端支付完成后要通知后端修改充值结果(同步结果通知)

tip:登录成功后将用户的积分和余额也返回给前端

完善下前面登陆代码中的信息补充,users/views.py, 代码:

day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知
@jsonrpc.method("User.login")
def login(ticket,randstr,account,password):
"""根据用户登录信息生成token"""
# 校验防水墙验证码
params = {
"aid": current_app.config.get("CAPTCHA_APP_ID"),
"AppSecretKey": current_app.config.get("CAPTCHA_APP_SECRET_KEY"),
"Ticket": ticket,
"Randstr": randstr,
"UserIP": request.remote_addr
}
# 把字典数据转换成地址栏的查询字符串格式
# aid=xxx&AppSecretKey=xxx&xxxxx
params = urlencode(params)
url = current_app.config.get("CAPTCHA_GATEWAY")
# 发送http的get请求
f = urlopen("%s?%s" % (url, params))
# https://ssl.captcha.qq.com/ticket/verify?aid=xxx&AppSecretKey=xxx&xxxxx content = f.read()
res = json.loads(content)
print(res) if int(res.get("response")) != 1:
# 验证失败
return {"errno": status.CODE_CAPTCHA_ERROR, "errmsg": message.captcaht_no_match} # 1. 根据账户信息和密码获取用户
if len(account) < 1:
return {"errno":status.CODE_NO_ACCOUNT,"errmsg":message.account_no_data}
user = User.query.filter(or_(
User.mobile==account,
User.email==account,
User.name==account
)).first() if user is None:
return {"errno": status.CODE_NO_USER,"errmsg":message.user_not_exists} # 验证密码
if not user.check_password(password):
return {"errno": status.CODE_PASSWORD_ERROR, "errmsg":message.password_error} # 2. 生成jwt token
access_token = create_access_token(identity=user.id)
refresh_token = create_refresh_token(identity=user.id)
print(access_token)
print(refresh_token)
return {
"errno": status.CODE_OK,
"errmsg": message.ok,
"id": user.id,
"nickname": user.nickname if user.nickname else account,
"avatar": user.avatar if user.avatar else current_app.config["DEFAULT_AVATAR"],
"money": float(user.money),
"credit": float(user.credit),
"access_token": access_token,
"refresh_token":refresh_token
}

登录成功后将用户的余额和果子积分返回给前端

BUG:修复页面底部菜单无法被点击的BUG

出现bug的原因: my_orchard页面帧的高度挡住了主页面的中的底部菜单.

客户端页面代码, orchard.html,代码:

day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知day114:MoFang:基于支付宝沙箱测试环境完成创建充值订单接口&服务端处理支付结果的同步通知和异步通知
<!DOCTYPE html>
<html>
<head>
<title>用户中心</title>
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta charset="utf-8">
<link rel="stylesheet" href="../static/css/main.css">
<script src="../static/js/vue.js"></script>
<script src="../static/js/axios.js"></script>
<script src="../static/js/main.js"></script>
<script src="../static/js/uuid.js"></script>
<script src="../static/js/settings.js"></script>
<script src="../static/js/socket.io.js"></script>
</head>
<body>
<div class="app orchard" id="app">
<img class="music" :class="music_play?'music2':''" @click="music_play=!music_play" src="../static/images/player.png">
<div class="orchard-bg">
<img src="../static/images/bg2.png">
<img class="board_bg2" src="../static/images/board_bg2.png">
</div>
<img class="back" @click="go_index" src="../static/images/user_back.png" alt="">
<div class="header">
<div class="info" @click="go_home">
<div class="avatar">
<img class="avatar_bf" src="../static/images/avatar_bf.png" alt="">
<img class="user_avatar" src="../static/images/avatar.png" alt="">
<img class="avatar_border" src="../static/images/avatar_border.png" alt="">
</div>
<p class="user_name">好听的昵称</p>
</div>
<div class="wallet">
<div class="balance" @click="user_recharge">
<p class="title"><img src="../static/images/money.png" alt="">钱包</p>
<p class="num">{{money}}</p>
</div>
<div class="balance">
<p class="title"><img src="../static/images/integral.png" alt="">果子</p>
<p class="num">99,999.00</p>
</div>
</div>
<div class="menu-list">
<div class="menu">
<img src="../static/images/menu1.png" alt="">
排行榜
</div>
<div class="menu">
<img src="../static/images/menu2.png" alt="">
签到有礼
</div>
<div class="menu" @click="go_orchard_shop">
<img src="../static/images/menu3.png" alt="">
道具商城
</div>
<div class="menu">
<img src="../static/images/menu4.png" alt="">
邮件中心
</div>
</div>
</div>
<div class="footer" >
<ul class="menu-list">
<li class="menu">新手</li>
<li class="menu">背包</li>
<li class="menu-center" @click="go_orchard_shop">商店</li>
<li class="menu">消息</li>
<li class="menu">好友</li>
</ul>
</div>
</div>
<script>
apiready = function(){
init();
new Vue({
el:"#app",
data(){
return {
music_play:true,
namespace: '/mofang_orchard',
token:"",
money:"",
socket: null,
recharge_list: ['10','20','50','100','200','500','1000'],
timeout: 0,
prev:{name:"",url:"",params:{}},
current:{name:"orchard",url:"orchard.html",params:{}},
}
},
created(){
this.game.goFrame("orchard","my_orchard.html", this.current,{
x: 0,
y: 180,
w: 'auto',
h: 410,
},null);
this.checkout();
this.money = this.game.fget("money")
},
methods:{
user_recharge(){
// 发起充值请求
api.actionSheet({
title: '余额充值',
cancelTitle: '取消',
buttons: this.recharge_list
}, (ret, err)=>{
if( ret ){
if(ret.buttonIndex <= this.recharge_list.length){
// 充值金额
money = this.recharge_list[ret.buttonIndex-1];
// 调用支付宝充值
this.create_recharge(money);
}
}else{ }
}); },
create_recharge(money){
// 获取历史信息记录
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this, token, (new_access_token)=>{
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.create",
"params": {
"money": money,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
// 前往支付宝
var aliPayPlus = api.require('aliPayPlus');
aliPayPlus.payOrder({
orderInfo: response.data.result.order_string,
sandbox: response.data.result.sandbox, // 将来APP上线需要修改成false
}, (ret, err)=>{
pay_result = {
9000:"支付成功",
8000:"正在处理中",
4000:"订单支付失败",
5000:"重复请求",
6001:"取消支付",
6002:"网络连接出错",
6004:"支付结果未知",
}
api.alert({
title: '支付结果',
msg: pay_result[ret.code],
buttons: ['确定']
});
// 通知服务端, 修改充值结果
this.return_recharge(response.data.result.order_number,token);
});
}else{
this.game.print(response.data);
}
}).catch(error=>{
// 网络等异常
this.game.print(error);
});
})
},
return_recharge(out_trade_number,token){
this.axios.post("",{
"jsonrpc": "2.0",
"id": this.uuid(),
"method": "Recharge.return",
"params": {
"out_trade_number": out_trade_number,
}
},{
headers:{
Authorization: "jwt " + token,
}
}).then(response=>{
if(parseInt(response.data.result.errno)==1000){
this.money = response.data.result.money.toFixed(2);
}
})
},
checkout(){
var token = this.game.get("access_token") || this.game.fget("access_token");
this.game.checkout(this,token,(new_access_token)=>{
this.connect();
});
},
connect(){
// socket连接
this.socket = io.connect(this.settings.socket_server + this.namespace, {transports: ['websocket']});
this.socket.on('connect', ()=>{
this.game.print("开始连接服务端");
});
},
go_index(){
this.game.outWin("orchard");
},
go_friends(){
this.game.goFrame("friends","friends.html",this.current);
this.game.goFrame("friend_list","friend_list.html",this.current,{
x: 0,
y: 190,
w: 'auto',
h: 'auto',
},null,true);
},
go_home(){
this.game.goWin("user","user.html", this.current);
},
go_orchard_shop(){
// 种植园商店
this.game.goFrame("orchard_shop","shop.html", this.current,null,{
type:"push",
subType:"from_top",
duration:300
});
}
}
});
}
</script>
</body>
</html>

修复页面底部菜单无法被点击的BUG

main.css中修改样式选择符对应的css样式

.orchard-frame .prop-list{
position: absolute;
bottom: 1rem; /*bottom: 6rem 修改成 bottom: 1rem*/
width: 100%;
}
.orchard-frame .pet-hp-list{
position: absolute;
right: 0;
bottom: 3rem;/*bottom: 8rem 修改成 bottom: 3rem*/
width: 11rem;
height: 4rem;
}