前言:判断是不是微信内,是微信内就用jsApi支付(屏蔽支付宝),不是微信内就用H5支付
此实例为vue2实例,需要vue3的宝子可以自行转换,流程上没啥区别
var ua = window.navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) == "micromessenger") {
this.showAliPay = false;
return true;
} else {
this.showAliPay = true;
return false;
}
一、jsApi支付
1、跳转到中间页,也可不跳转,在中间页进行一系列的操作
2、访问这个地址拿到code授权,并传给后端拿openid,拿到openid存到缓存里,下次进页面优先读取缓存的openid,没有再去找微信拿,完整代码如下
const localOpenId = localStorage.getItem("wexin-openid-wxc951e84c27099161");
if (localOpenId) {
this.handleType(localOpenId);
return;
}
var appid = "wxc951e84c27099161"; //个人公众号appid
var appsecret = "778ef263f6e1d648d50daa1c5147884b";
var mpPrefix = "wexin-openid-";
this.redirect = encodeURIComponent(window.location.href); //重定向回来的地址
// 判断是否是ios微信浏览器
var wx_code = this.getUrlParam("code"); // 截取url中的code
//获取code的地址。获取成功重定向后地址栏中将会带有code,判断没有code的话,就跳转到微信官方链接上获取,获取成功后会再重定向回来,注意url是需要使用encodeURIComponent处理一下编码的
if (!wx_code) {
// scope: 必传;应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
// 静默授权
window.location.href =
"https://open.weixin.qq.com/connect/oauth2/authorize?appid=" +
appid +
"&redirect_uri=" +
this.redirect +
"&response_type=code&scope=snsapi_base&state=123#wechat_redirect";
} else {
// 获取到了code,找后端拿openid
axios
.post(
`http://api.test.chiyanjiasu.com/wx/mp/openId?appid=${appid}&appsecret=${appsecret}&code=${wx_code}`
)
.then((res) => {
// data是形参名,代表返回的数据
if (res.errcode) {
alert(res.errmsg);
return;
}
localStorage.setItem(mpPrefix + appid, res.data.openid);
this.handleType(res.data.openid); // 找后端拿orderId
})
.catch((error) => {
console.log("error", error);
});
3、拿链接里的值,和判断是否是微信方法
getUrlParam: function (name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]);
return null;
},
isWeChat() {
let ua = window.navigator.userAgent.toLowerCase();
return ua.match(/MicroMessenger/i) == "micromessenger"; // 判定为true则是微信浏览器,false则不是
},
4、传给后端拿orderId(订单编号)
handleType(openId) {
const info = JSON.parse(localStorage.getItem("productInfo"));
const obj = {
amount: 1,
rtype: 2,
productId: info.id,
price: info.price,
totalPrice: info.price * 1,
buyTime: info.buyTime,
openId,
jsApi: 1,
};
userPayType(obj).then((result) => {
window.WeixinJSBridge.invoke(
"getBrandWCPayRequest",
{
appId: result.jsApiData.appId, //公众号ID,由商户传入
timeStamp: result.jsApiData.timeStamp, //时间戳,自1970年以来的秒数
nonceStr: result.jsApiData.nonceStr, //随机串
package: result.jsApiData.package,
signType: result.jsApiData.signType, //微信签名方式:
paySign: result.jsApiData.sign, //微信签名
},
function (res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
// 使用以上方式判断前端返回,微信团队郑重提示:
//res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
}
}
);
this.getPayResult(result.orderId);
});
},
5、循环查询订单是否完成
// 循环查询订单是否完成
async getPayResult(orderId) {
const data = await getResult({ orderId: orderId });
if (data == "poll") {
//2秒轮询
this.timerPay = setTimeout(() => {
this.getPayResult(orderId);
}, 2000);
} else {
if (data == "success") {
this.$router.push({
name: "oldRecharge",
query: {
orderId: orderId,
},
});
}
}
二、h5支付
1、调后端接口拿h5跳转链接,
1)如果是支付宝,定义returnUrl为重定向通知链接
2)如果是微信,在后面拼接&redirect_url= 为重定向通知链接(需要带上订单编号,因为支付宝重定向链接会自动带上订单编号,但是微信不会带上订单编号)
hanldType(n, flag) {
// h5支付
const obj = {
amount: 1,
rtype: n,
productId: this.activeItem.id,
price: this.activeItem.price,
totalPrice: this.activeItem.price * 1,
buyTime: this.activeItem.buyTime,
};
if (flag) { // 带flag代表支付宝
obj.returnUrl = location.origin + "/#/public/oldRecharge"
}
userPayType(obj).then((res) => {
const weiXinRedirectUrl=encodeURIComponent(location.origin + "/#/public/oldRecharge?charset=UTF-8&out_trade_no="+res.orderId)
window.location.href=flag?res.redirectUrl:res.redirectUrl+'&redirect_url='+weiXinRedirectUrl
this.show = false;
this.getPayResult(res.orderId);
});
},
2、跳转回来的时候
2、跳转回来的时候查询链接上是否有‘out_trade_no’,并查询结果
if (location.href.includes("out_trade_no") && !sessionStorage.getItem('isSearchDingDan')) {
sessionStorage.setItem('isSearchDingDan',true);
this.getPayResult(this.getUrlParam("out_trade_no"));
}
// 循环查询订单是否完成
async getPayResult(orderId) {
const data = await getResult({ orderId });
if (data == "poll") {
//2秒轮询
this.timerPay = setTimeout(() => {
this.getPayResult(orderId);
}, 2000);
} else {
if (data == "success") {
// this.$toast({ msg: "恭喜您支付成功!", type: "success" ,time: 3000});
this.showMiddle=true;
setTimeout(() => {
this.showMiddle=false;
history.pushState({},'','/#/public/oldRecharge');
sessionStorage.removeItem('isSearchDingDan')
}, 3000);
}
}
},
完整代码
1、主页面oldRecharge页面
<template>
<div class="recharge-index">
<Header />
<van-swipe :autoplay="3000" lazy-render class="my-swipe">
<van-swipe-item v-for="image in images" :key="image">
<img :src="image" />
</van-swipe-item>
</van-swipe>
<ul class="recharge-list clearfix">
<li
v-for="(item, index) in list"
:key="index"
@click="handleChecked(index, item)"
:class="{ active: activeIndex == index }"
>
<div class="useableTime">
{{
item.name.split("-")[0].indexOf("缤纷夏日活动") != -1
? item.buyTime / (3600 * 1000) + "小时"
: item.name + "小时"
}}
</div>
<div class="unitMoney">
约<span>{{ (item.price / (item.buyTime / (3600 * 1000))).toFixed(2) }}</span
>元/小时
</div>
<div class="payMoney">
优惠价:{{ item.price }}<span>¥{{ item.originPrice }}</span>
</div>
</li>
</ul>
<div class="rechargeRule">
<p>温馨提示</p>
<span>1.所有充值的套餐时长均为可暂停且永久有效的。</span>
<span>2.公众号套餐与PC客户端及官网保持一致,该页面充值后时</span>
<span> 长即刻到账,在公众号及客户端可查看时长余额等。</span>
</div>
<p class="rechargeRead">
<span @click="checkBoolen = !checkBoolen"
><img
:src="
checkBoolen ? require('./images/icon6.png') : require('./images/icon5.png')
"
/>阅读并同意</span
><label @click="hanldGoRouter">《服务条款》</label>
</p>
<div class="rechargeFooter">
<p>
金额:<label>¥</label><span>{{ activeItem.price }}</span>
</p>
<div @click="handleBuy()">支付</div>
</div>
<Service />
<van-popup class="popup-style" v-model="show" overlay-class="pay" position="bottom">
<h3>确认订单</h3>
<p>
充值时长:<span>{{ activeItem.buyTime/(3600*1000) + "小时" }}</span>
</p>
<p>
应付金额:<span>¥{{ activeItem.price }}</span>
</p>
<div class="payDivBox">
<div @click="handleWeXinOrAliPay('alipay', 1)" v-if="showAliPay">
<img src="./images/icon09.png" alt="" />
<div>支付宝支付</div>
</div>
<div @click="handleWeXinOrAliPay('weixin', 2)">
<img src="./images/icon10.png" alt="" />
<div>微信支付</div>
</div>
</div>
</van-popup>
<div class="dialogVisual" v-show="showMiddle">
<div class="maskLayer"></div>
<div class="dialogContent">
<img src="./images/xiaoyan.png" />
<p>充值成功!</p>
</div>
</div>
</div>
</template>
<script>
import Header from "./components/header";
import { Swipe, SwipeItem } from "vant";
import { rechargeList, userPayType, getResult } from "@/api/user";
import Service from "./components/fwtk.vue";
import Vue from "vue";
import { Popup } from "vant";
Vue.use(Popup);
Vue.use(Swipe);
Vue.use(SwipeItem);
export default {
components: {
Header,
Service,
},
data() {
return {
list: [],
myswiper: null,
listIndex: {},
show: false,
images: [require("./images/banner.png")],
checkBoolen: false,
activeIndex: 0,
loading: false,
activeItem: {},
showAliPay: false,
showMiddle:false
};
},
computed: {
logoShow() {
return this.$store.state.logoShow;
},
userInfo() {
return this.$store.state.userInfo;
},
},
mounted() {
this.isWeiXin();
if (this.$route.query.orderId) {
this.show = false;
this.$toast({ msg: "恭喜您支付成功!", type: "success" });
}
this.getList();
if (location.href.includes("out_trade_no") && !sessionStorage.getItem('isSearchDingDan')) {
sessionStorage.setItem('isSearchDingDan',true);
this.getPayResult(this.getUrlParam("out_trade_no"));
}
},
methods: {
async getList() {
const data = await rechargeList();
if (!data) {
return;
}
this.list = data.list;
this.activeItem = this.list[0] || {};
this.loading = false;
},
// 循环查询订单是否完成
async getPayResult(orderId) {
const data = await getResult({ orderId });
if (data == "poll") {
//2秒轮询
this.timerPay = setTimeout(() => {
this.getPayResult(orderId);
}, 2000);
} else {
if (data == "success") {
// this.$toast({ msg: "恭喜您支付成功!", type: "success" ,time: 3000});
this.showMiddle=true;
setTimeout(() => {
this.showMiddle=false;
history.pushState({},'','/#/public/oldRecharge'); // 不刷新页面把url截取
sessionStorage.removeItem('isSearchDingDan')
}, 3000);
}
}
},
isWeiXin() {
var ua = window.navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) == "micromessenger") {
this.showAliPay = false;
return true;
} else {
this.showAliPay = true;
return false;
}
},
handleBuy(n) {
if(!this.checkBoolen){
this.$toast({ msg: "请阅读并同意<p><<服务条款>></p>", type: "warning"});
return
}
const token = localStorage.getItem("token");
if (token && this.userInfo) {
this.show = true;
} else {
this.$store.dispatch("setLogoShow", true);
}
},
hanldGoRouter() {
this.$store.commit("setXieyiShowModal", true);
},
handleWeXinOrAliPay(typeName, n) {
if (typeName === "weixin") {
// 微信支付
if (this.isWeiXin()) {
// // 微信内jsApi支付
localStorage.setItem("productInfo", JSON.stringify(this.activeItem));
this.$router.push("/public/rechargeRedirect");
} else {
// 微信外h5支付
this.hanldType(n);
}
} else {
// 支付宝h5支付
this.hanldType(n, true);
}
},
getUrlParam(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = (window.location.search||window.location.hash).substr(1).match(reg);
if (r != null) return unescape(r[2]);
return null;
},
hanldType(n, flag) {
// h5支付
const obj = {
amount: 1,
rtype: n,
productId: this.activeItem.id,
price: this.activeItem.price,
totalPrice: this.activeItem.price * 1,
buyTime: this.activeItem.buyTime,
};
if (flag) {
obj.returnUrl = location.origin + "/#/public/oldRecharge"
}
userPayType(obj).then((res) => {
const weiXinRedirectUrl=encodeURIComponent(location.origin + "/#/public/oldRecharge?charset=UTF-8&out_trade_no="+res.orderId)
window.location.href=flag?res.redirectUrl:res.redirectUrl+'&redirect_url='+weiXinRedirectUrl
this.show = false;
this.getPayResult(res.orderId);
});
},
handleChecked(index, item) {
this.activeIndex = index;
this.activeItem = item;
},
},
};
</script>
<style scoped lang="less">
...
</style>
2、重定向页面
<template>
<div>
<img src="../images/xiaoyan2.png"/>
<p>加载中,请稍后...</p>
</div>
</template>
<script>
import { userPayType, getResult } from "@/api/user";
import axios from "axios";
export default {
data() {
return {
redirect: "",
timerPay: null,
};
},
mounted() {
const localOpenId = localStorage.getItem("wexin-openid-wxc951e84c27099161");
if (localOpenId) {
this.handleType(localOpenId);
return;
}
var appid = "wxc951e84c27099161"; //个人公众号appid
var appsecret = "778ef263f6e1d648d50daa1c5147884b";
var mpPrefix = "wexin-openid-";
this.redirect = encodeURIComponent(window.location.href); //重定向回来的地址
// 判断是否是ios微信浏览器
var wx_code = this.getUrlParam("code"); // 截取url中的code
//获取code的地址。获取成功重定向后地址栏中将会带有code,判断没有code的话,就跳转到微信官方链接上获取,获取成功后会再重定向回来,注意url是需要使用encodeURIComponent处理一下编码的
if (!wx_code) {
// scope: 必传;应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
// 静默授权
window.location.href =
"https://open.weixin.qq.com/connect/oauth2/authorize?appid=" +
appid +
"&redirect_uri=" +
this.redirect +
"&response_type=code&scope=snsapi_base&state=123#wechat_redirect";
} else {
// 获取到了code,找后端拿openid
axios
.post(
`http://api.test.chiyanjiasu.com/wx/mp/openId?appid=${appid}&appsecret=${appsecret}&code=${wx_code}`
)
.then((res) => {
// data是形参名,代表返回的数据
if (res.errcode) {
alert(res.errmsg);
return;
}
localStorage.setItem(mpPrefix + appid, res.data.openid);
this.handleType(res.data.openid);
})
.catch((error) => {
console.log("error", error);
});
}
},
destroyed() {
clearTimeout(this.timerPay)
},
methods: {
getUrlParam: function (name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]);
return null;
},
isWeChat() {
let ua = window.navigator.userAgent.toLowerCase();
return ua.match(/MicroMessenger/i) == "micromessenger"; // 判定为true则是微信浏览器,false则不是
},
handleType(openId) {
const info = JSON.parse(localStorage.getItem("productInfo"));
const obj = {
amount: 1,
rtype: 2,
productId: info.id,
price: info.price,
totalPrice: info.price * 1,
buyTime: info.buyTime,
openId,
jsApi: 1,
};
userPayType(obj).then((result) => {
window.WeixinJSBridge.invoke(
"getBrandWCPayRequest",
{
appId: result.jsApiData.appId, //公众号ID,由商户传入
timeStamp: result.jsApiData.timeStamp, //时间戳,自1970年以来的秒数
nonceStr: result.jsApiData.nonceStr, //随机串
package: result.jsApiData.package,
signType: result.jsApiData.signType, //微信签名方式:
paySign: result.jsApiData.sign, //微信签名
},
function (res) {
if (res.err_msg == "get_brand_wcpay_request:ok") {
// 使用以上方式判断前端返回,微信团队郑重提示:
//res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
}
}
);
this.getPayResult(result.orderId);
});
},
// 循环查询订单是否完成
async getPayResult(orderId) {
const data = await getResult({ orderId: orderId });
if (data == "poll") {
//2秒轮询
this.timerPay = setTimeout(() => {
this.getPayResult(orderId);
}, 2000);
} else {
if (data == "success") {
this.$router.push({
name: "oldRecharge",
query: {
orderId: orderId,
},
});
}
}
},
},
};
</script>