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

    目录

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

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

      2.前端调用AlipayPlus发起支付

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

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

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

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

      1.异步通知结果处理

      2.同步通知结果处理

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

    MoFang充值流程图

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

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

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

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

    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代码

    <!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进行模块功能使用,则会报错如下:

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

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

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

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

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

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

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

    客户端发起支付代码:

    注意:

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

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

    orchard.html代码

    <!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, 代码:

    <!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, 代码:

    @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,代码:

    <!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;
    }
  • 相关阅读:
    【Vue】Re19 Promise
    【Vue】Re17 Router 第四部分(参数传递,守卫函数)
    【Vue】Re16 Router 第三部分(懒加载、子路由)
    【Vue】Re15 Router 第二部分(缺省路由、动态路由)
    【Vue】Re14 Router 第一部分(入门案例)
    【Vue】Re13 CLI(Command Line Interface)
    【Vue】Re12 Webpack 第三部分(插件、热部署、配置分离)
    【Vue】Re11 Vue 与 Webpack
    【Vue】Re10 Webpack 第二部分(Loader)
    11-26日测试
  • 原文地址:https://www.cnblogs.com/libolun/p/14208445.html
Copyright © 2011-2022 走看看