手把手实现支付功能的前端技术方案与避坑指南

ლ哲铭 移动 阅读 3,017
赞 16 收藏
二维码
手机扫码查看
反馈

项目初期的技术选型

上个月接了个新需求,给公司一个H5商城加微信支付功能。本来以为就是调个接口的事,结果做起来才发现坑比想象中多得多。

手把手实现支付功能的前端技术方案与避坑指南

技术栈是Vue 2 + Vant UI,移动端为主,用户基本都是用手机微信打开的。一开始我考虑过直接用微信JSSDK的chooseWXPay,但后来发现官方已经不推荐这个了,新项目都建议走H5支付的新流程——也就是后端统一下单,前端跳转微信支付页面那种。

最后定的方案是:前端请求下单接口,后端返回mweb_url,我们再用这个URL跳转到微信内置支付页。看起来挺简单对吧?实际上后面一堆细节要处理。

核心代码就这几行

先说说正常流程怎么走。前端只需要发个请求,拿到链接然后跳转就行:

async function handlePay() {
  try {
    const res = await fetch('https://jztheme.com/api/order/create', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        productId: 123,
        amount: 100
      })
    });

    const data = await res.json();

    if (data.code === 0 && data.mwebUrl) {
      window.location.href = data.mwebUrl;
    } else {
      alert('下单失败:' + data.msg);
    }
  } catch (err) {
    alert('网络错误');
  }
}

HTML部分更简单:

<button @click="handlePay">立即支付</button>

看起来是不是很清爽?但这只是理想情况下的代码。真实环境里,光是“跳转”这一步就能让你折腾半天。

最大的坑:跳转失效和白屏

第一次提测的时候,测试妹子跟我说点了没反应。我本地好好的啊,查了半天才发现她是在某些安卓机上用微信打开的,点完按钮压根没跳转。

后来排查发现,window.location.href在微信浏览器里有时候会被拦截,特别是绑定在非直接用户操作事件上的时候。哪怕你是@click触发的,如果中间套了异步逻辑,也可能被当成非主动行为。

解决办法是必须保证跳转发生在用户手势的上下文中。我把跳转逻辑提前存下来,确保它是在事件流中同步执行的:

let pendingUrl = null;

document.addEventListener('click', () => {
  if (pendingUrl) {
    window.location.href = pendingUrl;
    pendingUrl = null;
  }
}, false);

async function handlePay() {
  try {
    const res = await fetch('https://jztheme.com/api/order/create', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        productId: 123,
        amount: 100
      })
    });

    const data = await res.json();

    if (data.code === 0 && data.mwebUrl) {
      // 不直接跳转,而是暂存
      pendingUrl = data.mwebUrl;
    } else {
      alert('下单失败:' + data.msg);
    }
  } catch (err) {
    alert('网络错误');
  }
}

这里注意我踩过好几次坑:不能用setTimeout延迟跳转,也不能放在Promise.then里面直接跳,否则大概率被拦。亲测有效的做法就是先把url存起来,然后靠下一次点击事件来触发跳转——虽然听起来有点怪,但在微信环境里确实管用。

又出问题:支付完成后回不来

解决了跳转问题,下一个问题是:用户付完钱,回不到我们页面。

理论上,微信支付支持传一个redirect_url参数,告诉它支付完要跳回哪。但现实是,很多机型根本不认这个参数,尤其是iOS微信,经常跳完就停在支付成功页,用户得手动点“返回”才能回来。

后来调整了方案,在下单时让后端把当前页面地址通过scene_info传进去,这样微信那边会自动尝试返回。不过最保险的做法还是提醒用户手动返回,并且在页面加载时检查是否刚从支付回来。

我们在订单详情页加了个检测逻辑:

// 页面加载时检查是否有支付完成标识
if (sessionStorage.getItem('pay_initiated') === '1') {
  sessionStorage.removeItem('pay_initiated');
  
  // 延迟一点查订单状态,避免接口还没更新
  setTimeout(() => {
    checkOrderStatus();
  }, 1500);
}

function handlePay() {
  // 下单前标记已发起支付
  sessionStorage.setItem('pay_initiated', '1');
  // ...后续逻辑
}

虽然不完美,但至少能让用户知道“你刚付完钱”,不至于懵圈。

性能与体验优化

还有一个小细节:下单接口不能太慢。我们最初接口平均响应要800ms,用户点完“支付”要等快一秒才有反应,体验很差。

后来做了两个优化:

  • 前端加了个loading状态,避免用户重复点击
  • 把下单接口拆成两步:先预下单(极快),再后台补全信息

现在首屏响应控制在200ms以内,用户感觉就是“点了立刻跳”,流畅多了。

踩坑提醒:这三点一定注意

总结下我踩过的坑,大家避避雷:

  1. 不要相信微信浏览器的跳转稳定性 —— 即使是标准流程,不同版本、不同系统表现也不一样,一定要真机测试。
  2. sessionStorage比localStorage更适合这种临时状态 —— 用户关掉页面就清掉了,不会留脏数据。
  3. 别忘了处理网络异常和超时 —— 我们现在设置了10秒超时,失败后允许重试,不然用户卡住就跑了。

回顾与反思

做完这个功能回头看,其实逻辑很简单,但各种边界情况加起来工作量不小。最头疼的不是技术实现,而是微信生态的“不确定性”——同样的代码,在iOS和Android上表现可能完全不同。

目前还有个小问题没完全解决:极少数用户支付完成后,返回时页面没刷新,订单状态还是“待支付”。理论上可以通过轮询修复,但我们觉得加轮询太重了,现在只是让用户手动下拉刷新一下,影响不大就没继续折腾。

整体来看,这次改动上线后支付成功率提升了12%,主要是因为loading反馈更及时、跳转更稳定了。虽然过程磕磕绊绊,但结果还行。

以上是我的项目经验,希望对你有帮助

这个功能看着小,真做起来细节一堆。很多东西文档上不写,只有自己踩过了才知道。

如果有更优的实现方式,比如更好的跳转兼容方案或者返回监听机制,欢迎评论区交流。我也在持续找更稳定的解法。

这类移动端支付的坑应该不少团队都遇到过,也许下次可以分享下支付宝H5支付的对接经历,那个 тоже有意思。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论