Vue——H5微信内支付
业务场景介绍:
Vue2--------H5移动端微信支付 >>>微信内支付(JSAPI支付官方API)
订单生成逻辑:前端判断设备获取code,根据code调用后端接口获取openId, 前端携带相关参数请求后端提交订单,后端去和微信对接生成订单,返回订单支付信息,拉取微信支付
开发前准备:
开通商户接入微信支付当然开通其他微信相关支付这个也是必不可少的比如这些
开通申请公众号,进行商户和公众号关联绑定账号关联
其中还要配置相关,支付域名,回调域名等等一些域名的配置详见微信支付对应的开发文档(实际以开发文档为准),所以提前申请域名也必不可少
获取code,获取openId
参考微信网页开发/网页授权获取code之后,通过code调用后端接口获取openId
插曲:获取code时页面会重定向,读取url获取code,调用openId接口,因为第一次本地是没有存的,所以首次点击可能是进入重定向页面第二次才生效然后进入拉起支付(想要避免根据实际需求进行修改获取openId,比如监听路由、根据携带参数)
其中 scope应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
// common.js // 获取code方法 在js中不建议使用route.query.code方式 获取参数 getUrlCodeParam('code') export function getUrlCodeParam(name) { // 从URL中获取指定参数的解码值 // 构建完整的正则表达式 const regex = new RegExp(`[\\?&]${name}=([^&#;]+)(?:&|#;|$)`); const match = regex.exec(location.href); if (match) { // 解码并替换加号 const paramValue = decodeURIComponent(match[1].replace(/\+/g, " ")); return paramValue; } // 参数不存在时返回 null return null; } export const getOpenId = () => { return new Promise(async (resolve, reject) => { console.log("初始化"); let ua = navigator.userAgent.toLowerCase(); // 判断是否在微信内部调用 :/micromessenger/.test(ua) if (!/micromessenger/.test(ua)) { console.log("不在微信环境内"); return resolve(""); } console.log("微信内部环境"); // 判断本地是否获取过openid:(sessionStorage.getItem("wx_Openid_2024")) let openid = sessionStorage.getItem("wx_Openid_2024"); if (openid) return resolve(openid); // 这里是通过getUrlCodeParam('code')方法处理ulr中的 code const code = getUrlCodeParam("code"); // 这里是有code走这里 if (code) { console.log(code, "code"); try { // 我们这里处理的方法是将code传给接口(具体参数根据业务自定义),接口返回openid const res = await weixinH5Openid({ code, payProject: "icommission" }); console.log(res); openid = res.body ? res.body.openid : ""; // 拿到openid后将openid存到本地 sessionStorage.setItem("wx_Openid_2024", openid); resolve(openid); } catch (error) { console.error("获取openid失败", error); reject(error); } } else { // 如果没用code 走这里 // 回调地址域名与配置的一定要一致 let redirectUri = encodeURIComponent(window.location.href); // let redirectUri = encodeURIComponent("http://**.**.cn"+window.location.pathname+window.location.search) // 这里是定义的appid,可以通过配置获取,也可以写死在这里; const appId = "wx99c163b3b6*****"; // https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_4 这里有详细介绍 const url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirectUri}&forcePopup=true&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect`; window.location.href = url; return resolve(""); // 重定向后立即返回空字符串 } }); };
复制
使用方法
import { getOpenId, wxpay } from '@/utils/common' const isWechatBrowser = /micromessenger/i.test(navigator.userAgent); if (isWechatBrowser) { //微信浏览器 try { // 获取用户的 openid let openid = await getOpenId(); if (openid) { }else { } } catch (error) { console.error('获取 openId 失败:', error); // 可以在此处添加其他错误处理逻辑 } }
复制
提交订单,拉起微信支付
携带参数调取后端接口,生成订单,返回订单信息,然后根据订单信息拉起微信支付
可以先在index.html引用
<script src="http://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
复制
common.js
import { weixinH5Openid } from "@/api/login"; /** * 微信内浏览器支付支付函数 * * @param {Object} params 微信支付参数对象 * @param {Function} callback 回调函数,执行成功或失败后调用 */ export function wxpay(params, callback) { if (typeof WeixinJSBridge == "undefined") { if (document.addEventListener) { document.addEventListener( "WeixinJSBridgeReady", onBridgeReady(params, callback), false ); } else if (document.attachEvent) { document.attachEvent( "WeixinJSBridgeReady", onBridgeReady(params, callback) ); document.attachEvent( "onWeixinJSBridgeReady", onBridgeReady(params, callback) ); } } else { onBridgeReady(params, callback); } } /** * 调用微信JS-SDK的支付功能 * * @param {Object} params 支付参数对象 * @param {string} params.appId 公众号ID,由商户传入 * @param {number} params.timeStamp 时间戳,自1970年以来的秒数 * @param {string} params.nonceStr 随机串 * @param {string} params.prepayId 统一下单接口返回的 prepay_id 参数值,提交支付请求时作为包标识 * @param {string} params.signType 微信签名方式 * @param {string} params.sign 微信签名 * @param {Function} callback 回调函数,接收微信支付响应结果 */ function onBridgeReady(params, callback) { WeixinJSBridge.invoke( "getBrandWCPayRequest", { appId: params.appId, //公众号ID,由商户传入 timeStamp: String(params.timeStamp), //时间戳,自1970年以来的秒数 nonceStr: params.nonceStr, //随机串 package: params.prepayId, signType: params.signType, //微信签名方式: paySign: params.sign, //微信签名 }, function (res) { callback(res); } ); }
复制
在需要使用的地方引入调用
import { getOpenId, wxpay } from '@/utils/common'
复制
// body提交订单接口返回的信息对象 wxpay(body, (payResult) => { if (payResult.err_msg == "get_brand_wcpay_request:ok") { console.log("支付成功"); // 使用以上方式判断前端返回,微信团队郑重提示: //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。 this.$toast({ message: '支付成功', duration: 1000 }); this.payPopup = false this.timer = setTimeout(() => { this.$router.push({ path: '/', }) clearTimeout(this.timer) }, 1000) } else if (payResult.err_msg == "get_brand_wcpay_request:cancel") { this.payPopup = false this.$toast({ message: '支付取消', duration: 1000 }); } else if (payResult.err_msg == "get_brand_wcpay_request:fail") { this.payPopup = false this.$toast({ message: '支付失败', duration: 1000 }); }else { this.payPopup = false this.$toast({ message: '未知错误', duration: 1000 }); } })
复制
beforeDestroy() { clearTimeout(this.timer) },
复制
效果图
拉起微信支付也可以使用
安装依赖
npm install weixin-jsapi --save
复制
在需要调起支付的页面引用
import wx from 'weixin-jsapi' // 这个方法需要在提交订单的时候拼接url 获取签名信息参数 (或者单独请求返回也行) wxPayFc(body) { let _this = this // const url = window.location.href.split('#')[0]; // 获取当前页面的完整 URL // const { sign } = await getSignature(url); // 从后端获取签名信息 wx.config({ debug: false, // 这里一般在测试阶段先用ture,等打包给后台的时候就改回false, "appId": body.appId, //公众号ID,由商户传入 "timeStamp": String(body.timeStamp), //时间戳,自1970年以来的秒数 nonceStr: body.nonceStr, // 必填,生成签名的随机串 signature: sign, // 必填,签名 jsApiList: ['chooseWXPay', 'checkJsApi'] // 必填,需要使用的JS接口列表 }) wx.ready(() => { // wx.checkJsApi({ // jsApiList: ['chooseWXPay'], // success:function(res){}, // fail:function(res){}}) wx.chooseWXPay({ "timeStamp": String(body.timeStamp), //时间戳,自1970年以来的秒数 nonceStr: body.nonceStr, // 支付签名随机串,不长于 32 位 "nonceStr": body.nonceStr, //随机串 "package": body.prepayId, "signType": body.signType, //微信签名方式: "paySign": body.sign, //微信签名 success: function (payRes) { // 支付成功后的回调函数 _this.$toast({ message: '支付成功', duration: 1000 }); }, cancel: function (payRes) { _this.$toast({ message: '取消支付', duration: 1000 }); }, fail: function (payRes) { _this.$toast({ message: '支付失败', duration: 1000 }); } }) }) },
复制