星际猫汤姆 發表於 2025-9-26 15:18:00

记录---jsApi支付+h5支付

<h1 data-id="heading-0">🧑‍💻 写在开头</h1>
<p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<h3 data-id="heading-0">前言:判断是不是微信内,是微信内就用jsApi支付(屏蔽支付宝),不是微信内就用H5支付</h3>
<blockquote>
<p>此实例为vue2实例,需要vue3的宝子可以自行转换,流程上没啥区别</p>
</blockquote>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">var ua = window.navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) == "micromessenger") {
this.showAliPay = false;
return true;
} else {
this.showAliPay = true;
return false;
}</pre>
</div>
<h3 data-id="heading-1">一、jsApi支付</h3>
<h6 data-id="heading-2">1、跳转到中间页,也可不跳转,在中间页进行一系列的操作</h6>
<h6 data-id="heading-3">2、访问这个地址拿到code授权,并传给后端拿openid,拿到openid存到缓存里,下次进页面优先读取缓存的openid,没有再去找微信拿,完整代码如下</h6>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">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 +
    "&amp;redirect_uri=" +
    this.redirect +
    "&amp;response_type=code&amp;scope=snsapi_base&amp;state=123#wechat_redirect";
} else {
// 获取到了code,找后端拿openid
axios
    .post(
      `http://api.test.chiyanjiasu.com/wx/mp/openId?appid=${appid}&amp;appsecret=${appsecret}&amp;code=${wx_code}`
    )
    .then((res) =&gt; {
      // data是形参名,代表返回的数据
      if (res.errcode) {
      alert(res.errmsg);
      return;
      }
      localStorage.setItem(mpPrefix + appid, res.data.openid);
      this.handleType(res.data.openid); // 找后端拿orderId
    })
    .catch((error) =&gt; {
      console.log("error", error);
    });</pre>
</div>
<h4 data-id="heading-4">3、拿链接里的值,和判断是否是微信方法</h4>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">getUrlParam: function (name) {
var reg = new RegExp("(^|&amp;)" + name + "=([^&amp;]*)(&amp;|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r);
return null;
},
isWeChat() {
let ua = window.navigator.userAgent.toLowerCase();
return ua.match(/MicroMessenger/i) == "micromessenger"; // 判定为true则是微信浏览器,false则不是
},</pre>
</div>
<h4 data-id="heading-5">4、传给后端拿orderId(订单编号)</h4>
<p>&nbsp;</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">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) =&gt; {
      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);
      });
    },</pre>
</div>
<h4 data-id="heading-6">5、循环查询订单是否完成</h4>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// 循环查询订单是否完成
    async getPayResult(orderId) {
      const data = await getResult({ orderId: orderId });
      if (data == "poll") {
      //2秒轮询
      this.timerPay = setTimeout(() =&gt; {
          this.getPayResult(orderId);
      }, 2000);
      } else {
      if (data == "success") {
          this.$router.push({
            name: "oldRecharge",
            query: {
            orderId: orderId,
            },
          });
      }
      }</pre>
</div>
<h3 data-id="heading-7">二、h5支付</h3>
<h4 data-id="heading-8">1、调后端接口拿h5跳转链接,</h4>
<p>1)如果是支付宝,定义returnUrl为重定向通知链接</p>
<p>2)如果是微信,在后面拼接&amp;redirect_url= 为重定向通知链接(需要带上订单编号,因为支付宝重定向链接会自动带上订单编号,但是微信不会带上订单编号)</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">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) =&gt; {
      const weiXinRedirectUrl=encodeURIComponent(location.origin + "/#/public/oldRecharge?charset=UTF-8&amp;out_trade_no="+res.orderId)
      window.location.href=flag?res.redirectUrl:res.redirectUrl+'&amp;redirect_url='+weiXinRedirectUrl
      this.show = false;
      this.getPayResult(res.orderId);
      });
    },</pre>
</div>
<h4 data-id="heading-9">2、跳转回来的时候查询链接上是否有‘out_trade_no’,并查询结果</h4>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">if (location.href.includes("out_trade_no") &amp;&amp; !sessionStorage.getItem('isSearchDingDan')) {
      sessionStorage.setItem('isSearchDingDan',true);
      this.getPayResult(this.getUrlParam("out_trade_no"));
    }
</pre>
</div>
<p>&nbsp;</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// 循环查询订单是否完成
    async getPayResult(orderId) {
      const data = await getResult({ orderId });
      if (data == "poll") {
      //2秒轮询
      this.timerPay = setTimeout(() =&gt; {
          this.getPayResult(orderId);
      }, 2000);
      } else {
      if (data == "success") {
          // this.$toast({ msg: "恭喜您支付成功!", type: "success" ,time: 3000});
          this.showMiddle=true;
          setTimeout(() =&gt; {
            this.showMiddle=false;
            history.pushState({},'','/#/public/oldRecharge');
            sessionStorage.removeItem('isSearchDingDan')
          }, 3000);
      }
      }
    },</pre>
</div>
<h3 data-id="heading-10">完整代码</h3>
<h4 data-id="heading-11">1、主页面oldRecharge页面</h4>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">&lt;template&gt;
&lt;div class="recharge-index"&gt;
    &lt;Header /&gt;
    &lt;van-swipe :autoplay="3000" lazy-render class="my-swipe"&gt;
      &lt;van-swipe-item v-for="image in images" :key="image"&gt;
      &lt;img :src="image" /&gt;
      &lt;/van-swipe-item&gt;
    &lt;/van-swipe&gt;
    &lt;ul class="recharge-list clearfix"&gt;
      &lt;li
      v-for="(item, index) in list"
      :key="index"
      @click="handleChecked(index, item)"
      :class="{ active: activeIndex == index }"
      &gt;
      &lt;div class="useableTime"&gt;
          {{
            item.name.split("-").indexOf("缤纷夏日活动") != -1
            ? item.buyTime / (3600 * 1000) + "小时"
            : item.name + "小时"
          }}
      &lt;/div&gt;
      &lt;div class="unitMoney"&gt;
          约&lt;span&gt;{{ (item.price / (item.buyTime / (3600 * 1000))).toFixed(2) }}&lt;/span
          &gt;元/小时
      &lt;/div&gt;
      &lt;div class="payMoney"&gt;
          优惠价:{{ item.price }}&lt;span&gt;¥{{ item.originPrice }}&lt;/span&gt;
      &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
    &lt;div class="rechargeRule"&gt;
      &lt;p&gt;温馨提示&lt;/p&gt;
      &lt;span&gt;1.所有充值的套餐时长均为可暂停且永久有效的。&lt;/span&gt;
      &lt;span&gt;2.公众号套餐与PC客户端及官网保持一致,该页面充值后时&lt;/span&gt;
      &lt;span&gt;&nbsp;&nbsp;长即刻到账,在公众号及客户端可查看时长余额等。&lt;/span&gt;
    &lt;/div&gt;
    &lt;p class="rechargeRead"&gt;
      &lt;span @click="checkBoolen = !checkBoolen"
      &gt;&lt;img
          :src="
            checkBoolen ? require('./images/icon6.png') : require('./images/icon5.png')
          "
      /&gt;阅读并同意&lt;/span
      &gt;&lt;label @click="hanldGoRouter"&gt;《服务条款》&lt;/label&gt;
    &lt;/p&gt;
    &lt;div class="rechargeFooter"&gt;
      &lt;p&gt;
      金额:&lt;label&gt;¥&lt;/label&gt;&lt;span&gt;{{ activeItem.price }}&lt;/span&gt;
      &lt;/p&gt;
      &lt;div @click="handleBuy()"&gt;支付&lt;/div&gt;
    &lt;/div&gt;
    &lt;Service /&gt;
    &lt;van-popup class="popup-style" v-model="show" overlay-class="pay" position="bottom"&gt;
      &lt;h3&gt;确认订单&lt;/h3&gt;
      &lt;p&gt;
      充值时长:&lt;span&gt;{{ activeItem.buyTime/(3600*1000) + "小时" }}&lt;/span&gt;
      &lt;/p&gt;
      &lt;p&gt;
      应付金额:&lt;span&gt;¥{{ activeItem.price }}&lt;/span&gt;
      &lt;/p&gt;
      &lt;div class="payDivBox"&gt;
      &lt;div @click="handleWeXinOrAliPay('alipay', 1)" v-if="showAliPay"&gt;
          &lt;img src="./images/icon09.png" alt="" /&gt;
          &lt;div&gt;支付宝支付&lt;/div&gt;
      &lt;/div&gt;
      &lt;div @click="handleWeXinOrAliPay('weixin', 2)"&gt;
          &lt;img src="./images/icon10.png" alt="" /&gt;
          &lt;div&gt;微信支付&lt;/div&gt;
      &lt;/div&gt;
      &lt;/div&gt;
    &lt;/van-popup&gt;
    &lt;div class="dialogVisual" v-show="showMiddle"&gt;
      &lt;div class="maskLayer"&gt;&lt;/div&gt;
      &lt;div class="dialogContent"&gt;
      &lt;img src="./images/xiaoyan.png" /&gt;
      &lt;p&gt;充值成功!&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
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: ,
      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") &amp;&amp; !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 || {};
      this.loading = false;
    },
    // 循环查询订单是否完成
    async getPayResult(orderId) {
      const data = await getResult({ orderId });
      if (data == "poll") {
      //2秒轮询
      this.timerPay = setTimeout(() =&gt; {
          this.getPayResult(orderId);
      }, 2000);
      } else {
      if (data == "success") {
          // this.$toast({ msg: "恭喜您支付成功!", type: "success" ,time: 3000});
          this.showMiddle=true;
          setTimeout(() =&gt; {
            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: "请阅读并同意&lt;p&gt;&lt;&lt;服务条款&gt;&gt;&lt;/p&gt;", type: "warning"});
      return
      }
      const token = localStorage.getItem("token");
      if (token &amp;&amp; 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("(^|&amp;)" + name + "=([^&amp;]*)(&amp;|$)");
      var r = (window.location.search||window.location.hash).substr(1).match(reg);
      if (r != null) return unescape(r);
      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) =&gt; {
      const weiXinRedirectUrl=encodeURIComponent(location.origin + "/#/public/oldRecharge?charset=UTF-8&amp;out_trade_no="+res.orderId)
      window.location.href=flag?res.redirectUrl:res.redirectUrl+'&amp;redirect_url='+weiXinRedirectUrl
      this.show = false;
      this.getPayResult(res.orderId);
      });
    },
    handleChecked(index, item) {
      this.activeIndex = index;
      this.activeItem = item;
    },
},
};
&lt;/script&gt;

&lt;style scoped lang="less"&gt;
...

&lt;/style&gt;</pre>
</div>
<h3 data-id="heading-12">2、重定向页面</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">&lt;template&gt;
&lt;div&gt;
    &lt;img src="../images/xiaoyan2.png"/&gt;
    &lt;p&gt;加载中,请稍后...&lt;/p&gt;
&lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
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 +
      "&amp;redirect_uri=" +
      this.redirect +
      "&amp;response_type=code&amp;scope=snsapi_base&amp;state=123#wechat_redirect";
    } else {
      // 获取到了code,找后端拿openid
      axios
      .post(
          `http://api.test.chiyanjiasu.com/wx/mp/openId?appid=${appid}&amp;appsecret=${appsecret}&amp;code=${wx_code}`
      )
      .then((res) =&gt; {
          // data是形参名,代表返回的数据
          if (res.errcode) {
            alert(res.errmsg);
            return;
          }
          localStorage.setItem(mpPrefix + appid, res.data.openid);
          this.handleType(res.data.openid);
      })
      .catch((error) =&gt; {
          console.log("error", error);
      });
    }
},
destroyed() {
    clearTimeout(this.timerPay)
},
methods: {
    getUrlParam: function (name) {
      var reg = new RegExp("(^|&amp;)" + name + "=([^&amp;]*)(&amp;|$)");
      var r = window.location.search.substr(1).match(reg);
      if (r != null) return unescape(r);
      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) =&gt; {
      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(() =&gt; {
          this.getPayResult(orderId);
      }, 2000);
      } else {
      if (data == "success") {
          this.$router.push({
            name: "oldRecharge",
            query: {
            orderId: orderId,
            },
          });
      }
      }
    },
},
};
&lt;/script&gt;</pre>
</div>
<div>
<h2>本文转载于:https://juejin.cn/post/7550619805817913379</h2>
</div>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
<p><em><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></em></p><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/19113555
頁: [1]
查看完整版本: 记录---jsApi支付+h5支付