app内嵌h5支付功能,跳转支付宝&微信,vue组件

一、功能梳理

app内前h5涉及到支付的功能,ios非实物商品实付需要使用ios原生支付方式,实物商品则可以三方支付,主要的实现思路为后端返回跳转支付宝或微信的支付scheme链接,前端进行跳转支付,同时需要实时查询用户的支付状态。

整个过程中复杂的部分在于查询用户支付状态的体验方面,需要保证用户在支付成功、支付失败、跳转支付宝、微信回来或者没有跳转支付宝微信等未知的行为下的用户体验。

组件内容为底部升起的支付选择弹窗,可以选择支付宝或者微信。

二、实现步骤

  1. 用户进行下单操作,前端调用下单接口,成功则返回三方支付app跳转链接,前端进行跳转

  1. 跳转三方app同时进行查询订单接口轮训,实时获取订单状态,轮训时间定位30s,依据需求调整

  1. 用户如果支付时间超过了30s未返回我们app的情况下,需要针对此种情况进行处理,捕捉用户返回我们app的情况

三、组件可支配参数&事件设置

设置支持参数2个、事件4个,分别为

参数:

successStatus: {
  default: '1'
}, // 支付成功的状态码,默认1
errorStatus: {
  default: '2'
} // 支付失败的状态码,默认2

事件:

@payMoney="payMoney" // 下单事件
@payStatus="payStatus" //查询支付状态事件
@succcessFunction="succcessFunction" // 成功支付操作
@errorFunction="errorFunction" // 失败实付操作

四、从支付app返回,事件捕获方法

为了防止用户在支付时间超过我们设置轮训时长情况,需要监听用户从支付app返回当前页面,来进行查询支付状态操作。方法为监听页面显示or隐藏事件

reloadState () {
  // 添加监听器
  if (typeof document.hidden !== 'undefined') {
    this.hidden = 'hidden';
    this.visibilityChange = 'visibilitychange';
  } else if (typeof document.mozHidden !== 'undefined') {
    this.hidden = 'mozHidden';
    this.visibilityChange = 'mozvisibilitychange';
  } else if (typeof document.msHidden !== 'undefined') {
    this.hidden = 'msHidden';
    this.visibilityChange = 'msvisibilitychange';
  } else if (typeof document.webkitHidden !== 'undefined') {
    this.hidden = 'webkitHidden';
    this.visibilityChange = 'webkitvisibilitychange';
  }
  document.addEventListener(this.visibilityChange, this.forceUpdate);
},
forceUpdate () {
  if (document.visibilityChange === this.hidden) {
  } else {
    this.searchTimer();
  }
},

五、组件源码

<template>
    <div class="pay-info">
        <div class="pay-content">
            <span class="pay-left"><img class="pay-icon" src="../assets/zfb.png" alt=""> 支付宝支付</span>
            <img class="radio-icon" src="../assets/radio2.svg" alt="">
        </div>
        <div class="pay-content">
            <span class="pay-left"><img class="pay-icon" src="../assets/wx.png" alt=""> 微信支付</span>
            <img class="radio-icon" src="../assets/radio2.svg" alt="">
        </div>
        <div class="pay-button" @click="payFun"><span>支付</span></div>
        <div class="loading-dom" v-if="loadingPay">
          支付中
          <van-loading color="#ffffff" style="margin-left: 5px"/> 
        </div>
    </div>
</template>

<script type="text/javascript">
export default {
  data () {
    return {
      isApp: !!browers.appUA,
      timer: null,
      loadingPay: false,
      orderNo: '', // 订单号
      payStatus: '' // 订单支付状态
    };
  },
  props: {
    successStatus: {
      default: '1'
    }, // 支付成功的状态码,默认1
    errorStatus: {
      default: '2'
    } // 支付失败的状态码,默认2
  },
  mounted () {
    this.$once('hook:beforeDestroy', () => {
      clearInterval(this.timer);
      this.timer = null;
    });
  },
  methods: {
    reloadState () {
      // 添加监听器
      if (typeof document.hidden !== 'undefined') {
        this.hidden = 'hidden';
        this.visibilityChange = 'visibilitychange';
      } else if (typeof document.mozHidden !== 'undefined') {
        this.hidden = 'mozHidden';
        this.visibilityChange = 'mozvisibilitychange';
      } else if (typeof document.msHidden !== 'undefined') {
        this.hidden = 'msHidden';
        this.visibilityChange = 'msvisibilitychange';
      } else if (typeof document.webkitHidden !== 'undefined') {
        this.hidden = 'webkitHidden';
        this.visibilityChange = 'webkitvisibilitychange';
      }
      document.addEventListener(this.visibilityChange, this.forceUpdate);
    },
    forceUpdate () {
      if (document.visibilityChange === this.hidden) {
      } else {
        this.searchTimer();
      }
    },
    payFun () {
      this.$emit('payMoney', (res) => {
        this.reloadState();
        // 下单接口的回调,执行轮训结果
        this.orderNo = res.order_no;
        this.loadingPay = true;
        this.searchPay();// 监听从其他app返回,为了解决从支付宝回来不轮训的问题
        setTimeout(() => {
          location.href = res.pay_address;
        }, 200);
      });
    },
    searchPay () {
      this.$emit('payStatus', (res) => {
        if (this.payStatus === '') {
          this.searchTimer();
        }
        // 下单接口的回调,执行轮训结果
        this.payStatus = res;
      });
    },
    searchTimer() {
      if (this.timer) {
        return;
      }
      if (this.orderNo) {
        let times = 0;
        this.searchPay();
        if (this.payStatus === '') {
          this.timer = setInterval(res => {
            times++;
            this.searchPay();
            if (this.payStatus === this.$props.successStatus) {
              clearInterval(this.timer);
              this.timer = null;
              this.loadingPay = false;
              document.removeEventListener(this.visibilityChange, this.forceUpdate);
              // 支付成功事件
              this.$emit('succcessFunction', (res) => {
              });
            }
            if (this.payStatus === this.$props.errorStatus) {
              clearInterval(this.timer);
              this.timer = null;
              this.loadingPay = false;
              document.removeEventListener(this.visibilityChange, this.forceUpdate);
              // 支付失败事件
              this.$emit('errorFunction', (res) => {
              });
            }
            if (times > 30) {
              this.$toast('未查询到支付状态,请重新支付');
              clearInterval(this.timer);
              this.timer = null;
              this.loadingPay = false;
              document.removeEventListener(this.visibilityChange, this.forceUpdate);
            }
          }, 1000);
        }
      }
    }
  }
};
</script>

<style lang="less" scoped>
.pay-info {
    .pay-content {
        background: #fff;
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 8px 0;
        font-size: 28px;
        color: #222;
        .pay-left {
            display: flex;
            align-items: center;
        }
        .pay-icon {
            width: 60px;
            height: 60px;
            margin-right: 10px
        }
        .radio-icon {
            width: 34px;
            height: 34px;
        }
    }
    .pay-button {
        width: 100%;
        text-align: center;
        margin-top: 30px;
        span {
            display: inline-block;
            background: #4e88f6;
            color: #fff;
            width: 100%;
            font-size: 32px;
            height: 88px;
            line-height: 88px;
            border-radius: 50px;
            display: inline-block;
        }
    }
    .loading-dom {
      width: 100%;
      height: 100vh;
      background: rgba(0,0,0,.5);
      color: #fff;
      text-align: center;
      font-size: 34px;
      font-weight: 500;
      position: fixed;
      display: flex;
      align-items: center;
      justify-content: center;
      top: 0;
      left: 0
    }
}
</style>

六、父组件调用

<Pay 
    @payMoney="payMoney"
    @payStatus="payStatus"
    @succcessFunction="succcessFunction"
    @errorFunction="errorFunction"
    :success-status='1'
    :error-status='2'
></Pay>
// 下单事件
    payMoney (callback) {
      this.$axios.post(`url`, params).then((res) => {
        let data = res.data;
        if (Number(data.code) === 0) {
            // 执行支付操作,跳转url,结果回调给收银台组件
          this.currentOrder = data.data.order_no;
          callback(data.data);
        } else {
          this.$toast(data.message);
        }
      }).catch((e) => {
      });
    },
    // 轮训状态接口
    payStatus (callback) {
      this.$axios.get(`url`).then((res) => {
        let data = res.data;
        if (Number(data.code) === 0) {
            // 执行支付结果查询,结果回调给收银台组件
          callback(data.data.pay_result);
          if (data.data.list && data.data.list.length) {
            this.resultData = data.data.list;
          }
        } else {
          this.$toast(data.message);
        }
      }).catch((e) => {
      });
    },
    succcessFunction () {
      // 支付成功父组件操作事件
      this.$toast('支付成功');
      // 先弹出支付成功提示,延时1秒出结果弹窗
      setTimeout(() => {
        // 支付成功后刷新一下接口
      }, 1000);
    },
    errorFunction () {
      // 支付失败父组件操作事件
      this.$toast('支付失败,请重新发起支付');
    },

猜你喜欢

转载自blog.csdn.net/qq_38068508/article/details/129621317
今日推荐