pay支付页面

在这里插入图片描述
虚拟DOM挂载到真实DOM上之后,从路由中获取订单id和商品价格,带着订单id调用后端接口,后端接口(1)查询数据库获取订单信息,(2)计算剩余时间:当前时间-订单信息中的订单创建时间>=15min*50s=900s,说明超时了,将剩余时间置为0,否则设置剩余时间=900-(当前时间-订单信息中的订单创建时间),将剩余时间保存在订单信息中并返回给前端,前端保存并从中取出商家信息、剩余时间,剩余时间为0,将overtime设为true,不为0,使用setInterval每隔1s计算剩余时间:(1)剩余时间-1,(2)计算倒计时(分钟、秒数)

mounted() {
	let _this = this;
    // 从路由中获取订单id和商品价格
    this.order_id = this.$route.query.order_id;
    this.price = this.$route.query.price;
    orderInfo({order_id: this.order_id}).then((response) => {
        this.order_info = response.data.data;// 获取订单信息
        let remain_time = response.data.data.pay_remain_time;//支付剩余时间
        this.restaurant_info = this.order_info.restaurant;//商家信息
        if (remain_time === false) {// 剩余时间为0 到时间了 overtime设为true
          	this.overtime = true;
        }

        this.timer = setInterval(function () {
          	remain_time--;// 每一秒 剩余时间-1s
          	_this.calc_remain_time(remain_time);
        }, 1000)
	})
}
//获取订单信息
orderInfo(data){
  	let req = {url: `v1/order/${data.order_id}`}
  	return _get(req);
}
_get(req){
  return axios.get(req.url, {params: req.data})
}
calc_remain_time(remain_time) {//倒计时
	let minutes = (remain_time / 60 % 60)
    this.minutes = minutes >= 10 ? minutes + '' : '0' + minutes;//计算剩余的分钟
    let seconds = (remain_time % 60);
    this.seconds = seconds >= 10 ? seconds + '' : '0' + seconds;//计算剩余的秒数
    if (!this.minutes && !this.seconds) {
    	clearInterval(this.timer);
        this.overtime = true;//支付超时
    }
}

顶部导航条:复用head组件

在这里插入图片描述

<v-head title="支付订单" goBack="true" bgColor="#f4f4f4"></v-head>

美团外卖图片

在这里插入图片描述

<div class="img">
	<img src="../../assets/pay_adv.png">
</div>

支付剩余时间

在这里插入图片描述

<h3>支付剩余时间</h3>
	<!--没超时 展示剩余时间-->
    <div class="remain-time" v-if="!overtime">
        <span>{{minutes.slice(0, 1)}}</span><span>{{minutes.slice(1, 2)}}</span>
        <span>:</span>
        <span>{{seconds.slice(0, 1)}}</span>
        <span>{{seconds.slice(1, 2)}}</span>
    </div>
<!--超时了 展示“支付超时”-->
<span class="overtime" v-else>支付超时</span>

订单信息

在这里插入图片描述
展示餐厅图片 商品价格 店铺名 订单号

<div class="avatar">
	<img :src="restaurant_info.pic_url">
</div>
<div class="info">
	<span class="price">{{this.price}}</span>
    <p>{{restaurant_info.name}} - {{order_id}}</p>
</div>

支付方式

在这里插入图片描述

<li @click="payType = '1'">
	<!--支付宝图标-->
    <span class="pay-icon"><i class="iconfont" style="color:#3d91e4;">&#xe60f;</i></span>
    <span class="pay-way-name">支付宝</span>
    <!--如果你点了支付宝 此时payType=1 展示√这个iconfont-->
    <span class="selected" v-if="payType === '1'"><i class="iconfont">&#xe6da;</i></span>
    <!--否则展示一个空白圆圈-->
    <span class="select" v-else></span>
</li>
<li @click="payType = '2'">
	<!--微信图标-->
    <span class="pay-icon"><i class="iconfont" style="color:#2aaf90;">&#xe62a;</i></span>
    <span class="pay-way-name">微信支付</span>
    <!--如果你点了微信 此时payType=2 展示√这个iconfont-->
    <span class="selected" v-if="payType === '2'"><i class="iconfont">&#xe6da;</i></span>
    <!--否则展示一个空白圆圈-->
	<span class="select" v-else></span>
</li>

确定支付

在这里插入图片描述
点击确定支付后 判断是否超时 超时了展示“支付超时” 没超时将payWayShow设为true 展示蒙版:“手机扫码支付”、“调起APP支付”

<div class="submit" @click="selectPayType()">确定支付</div>
<script>
	selectPayType() {
        if (this.overtime) {
          	this.alertText = '支付超时';
          	this.showTip = true;
          	return;
        }
        this.payWayShow = true;
	}
</script>

蒙版

在这里插入图片描述
点击“确定支付”后 前端先判断订单是否正在提交 防止用户重复提交若没有 则继续判断订单是否超时 若超时 显示“支付超时” 否则前端调用自己的initPay方法 initPay会调用后端接口pay 执行initPay方法:

  1. 如果前端传来的order_id为空 后端返回给前端status为-1 错误信息:初始化支付失败参数有误
  2. 后端查询数据库 若order_id已经存在且在数据库中的状态为支付成功 则后端返回前端status为302 信息:该订单已完成支付!
  3. 若order_id已经存在且在数据库中的状态为未支付 则删除数据库中的order_id 所以前端拿到后端返回的信息首先判断状态码status 接下来 后端将订单数据 如订单编号、价格、支付方式等等信息使用md5加密
  4. 若是扫码支付 后端访问另一个接口 获取二维码 若该接口的返回的code为0000 获取成功 后端修改二维码为:扫码直接请求payNotice 将二维码返回给前端 前端将二维码传给scan组件并展示scan组件
  5. 若是调用APP支付 后端返回客户端同步跳转地址 前端会将这个地址放在表单中 然后调用app 提交表单
<transition name="fade">
	<div class="pay-channel" v-show="payWayShow">
        <div class="channel-select-container">
          	<div class="scan" @click="method = 'trpay.trade.create.scan'">
            	<!--如果这个div被点击 则method = 'trpay.trade.create.scan' 展示√这个iconfont-->
            	<i class="iconfont selected" v-if="method === 'trpay.trade.create.scan'">&#xe6da;</i>
            	<!--否则展示空圆圈-->
            	<i class="select" v-else>&#xe6da;</i>
            	<span>手机扫码支付</span>
          	</div>
          	<div class="wap" @click="method='trpay.trade.create.wap'">
            	<!--如果这个div被点击 则method = 'trpay.trade.create.wap' 展示√这个iconfont-->
            	<i class="iconfont selected" v-if="method === 'trpay.trade.create.wap'">&#xe6da;</i>
            	<!--否则展示空圆圈-->
            	<i class="select" v-else></i>
            	<span>调起app支付</span>
          	</div>
          	<!--preventRepeat初始值为false 点击提交后 会置true 防止重复提交 提交完会重置false-->
          	<div class="submit" :class="{disabled:preventRepeat}" @click="submit()">确定支付</div>
		</div>
        <!--×这个iconfont 点击后关闭蒙版-->
        <div class="close" @click="close();">
          	<i class="iconfont icon-close">&#xe625;</i>
        </div>
	</div>
</transition>
<!--扫码支付-->
<scan :payType="payType" :orderData="orderData" @close="scanShow = false;" v-show="scanShow"></scan>
<!--调用app支付-->
<form action="http://pay.trsoft.xin/order/trpayGetWay" method="post" id="form" ref="form">
	<input type="hidden" name="amount" v-model=form_data.amount>
    <input type="hidden" name="outTradeNo" v-model="form_data.outTradeNo">
    <input type="hidden" name="payType" v-model="payType">
    <input type="hidden" name="tradeName" v-model="form_data.tradeName">
    <input type="hidden" name="notifyUrl" v-model="form_data.notifyUrl">
    <input type="hidden" name="synNotifyUrl" v-model="form_data.synNotifyUrl">
    <input type="hidden" name="payuserid" v-model="form_data.payuserid">
    <input type="hidden" name="appkey" v-model="form_data.appkey">
    <input type="hidden" name="method" v-model="method">
    <input type="hidden" name="sign" v-model="form_data.sign">
    <input type="hidden" name="timestamp" v-model="form_data.timestamp">
    <input type="hidden" name="version" v-model="form_data.version">
</form>
<script>
	submit() {//提交支付
        if (this.preventRepeat) return;
        if (this.overtime) {
          	this.alertText = '支付超时';
          	this.showTip = true;
          	return;
        }
        this.preventRepeat = true;//防止多次点击
        initPay({order_id: this.order_id, payType: this.payType, method: this.method}).then((response) => {
          	let res = response.data;// 支付前的准备工作所返回的数据
          	this.preventRepeat = false;
          	if (res.status === -1) {//支付接口出错
            	this.alertText = res.message;//提示
            	this.showTip = true;
            	return;
          	}
          	if (res.status === 302) {//该订单是否已经支付完成
            	let _this = this;
            	this.alertText = res.message;//提示
            	this.showTip = true;
            	setTimeout(() => {//订单支付成功 1s后跳转到订单页面
              		_this.$router.push('/order');
            	}, 1000)
            	return;
          	}

          	if (this.method === 'trpay.trade.create.scan') {//扫码支付方式
            	this.orderData = response.data.data;
            	this.scanShow = true;// 展示二维码
          	} else {//调起APP支付 提交form表单
            	this.form_data = response.data.data
            	this.$nextTick(() => {
              		this.$refs['form'].submit();
            	})
          	}
        })
	}
    initPay = (data) => {
  		let req = {data,url: 'v1/pay'}
  		return _post(req);
	}
	_post(req){
  		return axios({method: 'post', url: `/${req.url}`, data: req.data})
	}
</script>

scan扫描

在这里插入图片描述
用户扫码会去调用notifyUrl
在这里插入图片描述
虚拟DOM挂载到真实DOM上面之后监听orderData数据的变化 最初orderData为null 父组件给scan组件传入orderData 此时会执行下面的方法 若已有QRCode对象 重新生成一个二维码 否则创建QRCode对象并生成一个二维码 然后监听订单状态

watch: {
	orderData(val) {
        this.orderData = val;
        if (this.qrcode) {
          	this.qrcode.makeCode(val.data.qrcode);// 已有QRCode对象 重新生成一个二维码
        } else {
          	this.qrcode = new QRCode(this.$refs['qrcode'], {// 创建QRCode对象 生成一个二维码
            	text: val.data.qrcode,
            	width: 200,
            	height: 200,
          	});
        }
        this.listenStatus(val.outTradeNo);// orderData中的outTradeNo:商户自主生成的订单号
	}
}

监听订单的状态:每隔3s执行listenStatus方法 listenStatus会调用后端接口listen_status 实时监听支付状态 listen_status会使用订单编号去数据库查询订单的状态 若状态为200代表支付成功 后端向前端返回状态码200及“支付完成” 清空定时器 并在1s后跳转到订单详情页 否则返回状态码-1及“未支付”

listenStatus(outTradeNo) {
	clearInterval(this.timer);
    let _this = this;
    this.timer = setInterval(() => {
    	listenStatus({outTradeNo}).then((response) => {
            if (response.data.status === 200) {
              	clearInterval(this.timer);
              	this.alertText = '支付成功,准备跳转';
              	this.showTip = true;
              	setTimeout(()=>{// 1s后跳转到订单详情页
                	_this.$router.push({path: '/order_detail', query: {id: _this.orderData.order_id}})
              	},1000);
            }
		})
	}, 3000);
}

界面展示:

<div id="scan-container">
    <header>
      	<!--展示支付宝或微信图标 根据payType判断-->
      	<i class="iconfont pay-icon" :style="{color:payTypeObj[payType]['color']}"
         	v-html="payTypeObj[payType]['icon']"></i>
      	<!--展示支付方式:支付宝/微信-->
      	<span class="pay-way-name">{{payTypeObj[payType]['name']}}</span>
    </header>
    <!--展示二维码-->
    <div class="qrcode-container">
      	<div id="qrcode" ref="qrcode"></div>
    </div>
    <!--展示订单信息:产品名称 订单编号 订单金额 实付金额-->
    <div class="info-container">
      	<ul>
        	<li><span>产品名称:{{orderData.tradeName}}</span></li>
        	<li><span>订单编号:{{orderData.outTradeNo}}</span></li>
        	<li><span>订单金额:{{orderData.amount / 100}}</span></li>
        	<li><span>实付金额:{{orderData.amount / 100}}</span></li>
      	</ul>
    </div>
    <!--×这个iconfont 点击后关闭蒙版-->
    <div class="close" @click="close();">
      	<i class="iconfont icon-close">&#xe625;</i>
    </div>
    <alert-tip :text="alertText" :showTip.sync="showTip"></alert-tip>
</div>
<script>
	close() {
        clearInterval(this.timer)
        this.$emit('close');
      }
</script>

QrCode在vue中的使用

QRCode.js 是一个用于生成二维码的 JavaScript 库。主要是通过获取 DOM 的标签,再通过 HTML5 Canvas 绘制而成,不依赖任何库。

  1. 安装:npm install qrcodejs2 --save
  2. 引入:import QRCode from 'qrcodejs2'
  3. 需要一个生成二维码的div:<div id="qrcode" ref="qrcode"></div>
  4. 生成二维码
let qrcode = new QRCode('qrcode', {
    width: 132, //图像宽度
    height: 132, //图像高度
    text: code, // 二维码地址
    colorDark: "#000", //前景色
    colorLight: "#fff", //背景色
  })
  1. 清除二维码
qrcode.clear(); 
  1. 生成另外一个二维码
qrcode.makeCode("http://www.w3cschool.cc");

QrCode的使用

  1. 在html中定义:<div id="qrcode"></div>
  2. 在js中使用:new QRCode(document.getElementById("qrcode"), "http://www.runoob.com"); // 设置要生成二维码的链接
  3. 其他可选参数:
var qrcode = new QRCode(document.getElementById("qrcode"), {
    text: "http://www.runoob.com",
    width: 128,
    height: 128,
    colorDark : "#000000",
    colorLight : "#ffffff",
    correctLevel : QRCode.CorrectLevel.H
});
qrcode.clear(); // 清除二维码
qrcode.makeCode("http://www.w3cschool.cc"); // 生成另外一个二维码
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值