Skip to content

订单支付与问诊室通知(二) ​

本页对应 订单支付与问诊室通知,共 7 段视频,约 1 小时 4 分。

学习目标 ​

  1. 能说明支付流程中用户、前端浏览器、商户后端、支付平台客户端及支付平台后台的角色分工,并指出前端主要参与“调用后端支付接口”和“引导用户支付”两个环节。
  2. 能根据接口文档封装支付请求方法,传入支付方式、订单 ID、支付回调地址等参数,并在用户确认支付时触发调用。
  3. 能根据后端返回的 payURL 使用 window.location.href 完成 H5 支付跳转,并说明支付完成后回调地址的作用及端口配置注意事项。
  4. 能区分 H5 与小程序引导支付方式的差异,使用条件编译分别处理 H5 跳转和小程序支付 API 调用,并对暂不支持的微信支付给出提示。
  5. 能说明支付宝沙箱环境、沙箱账号及 APPID 的用途,指出支付账号与后端对接信息通常由后端申请和维护。
  6. 能使用原生 WebSocket 建立连接,监听 onopen、onmessage、onclose 事件,调用 send 发送消息,并通过浏览器 Network 面板的 WS 请求验证消息收发过程。

知识点 ​

先看支付流程图:五个角色,前端只盯两处 ​

为什么需要:支付看起来复杂,但先分清角色,才能知道前端到底做什么。转写稿里的图要“竖着看列”:用户、浏览器、商户后台、支付平台后台、微信客户端。用户不写代码,真正配合的是浏览器、商户后台、支付平台后台、客户端等角色。
怎么想:不要一上来研究后端和支付平台怎么对接。前端只关心两处:第一步调后端接口拿结果;最后拿到地址或参数后引导用户支付。中间后端与支付平台交互,对前端可视为透明。
怎么做:按流程记:浏览器请求商户后台 → 后端对接支付平台 → 后端把结果返回前端 → 前端引导支付 → 支付完成后回跳目标页。
易错点:把“跳转支付地址”当成支付全部;忽略最终回跳页。转写稿里 payCallback 就是支付成功后回跳地址,端口要填自己的,如 5173/5174 不能写错。

调支付接口:参数、封装与 confirm 回调 ​

为什么需要:前端要知道付多少钱、用什么方式付,必须把订单信息交给后端,由后端生成支付所需结果。
怎么想:接口是 POST,一般传支付方式、orderId、payCallback。orderId 用来确定订单和金额;payCallback 是支付成功后的回跳页。
怎么做:在 service/payment.js 封装接口,在支付组件监听到 confirm 后调用。转写稿中的关键写法如下:

js
// service/payment.js
export const orderPayment = (data) => request.post('/order/payment', data)

// 支付组件
const onPaymentFormConfirm = async (index) => {
  const res = await orderPayment({
    payType: index,
    orderId,
    payCallback: 'http://localhost:5173/pay/result'
  })
  console.log(res.data)
}

易错点:index 要能从组件 confirm 事件解构出来;支付方式 0/1/2 的含义要按接口文档,不能猜。转写稿中微信支付暂不支持,选 0 时要提示“暂不支持微信支付”。

引导支付:网页跳 URL,小程序调 API ​

为什么需要:后端只返回结果,用户还需要被“引导”到支付动作。网页和小程序引导方式不同。
怎么想:H5 中后端返回 payURL,用地址跳转引导;小程序没有 window.location,要调 wx.requestPayment,参数由后端返回。
怎么做:H5 可写:

js
window.location.href = res.data.payURL

小程序则调 wx.requestPayment,四个必传参数由服务端返回。转写稿还要求加条件编译:H5 才跳 URL,小程序才调支付 API;微信支付不支持时先提示。
易错点:把网页跳转硬套到小程序;忘记条件编译;payCallback 端口写错导致支付后回不到本地页面。演示中回跳页暂时空白,是因为页面内容还没补。

支付宝演示与沙箱账号 ​

为什么需要:开发支付不能总用真钱测试,所以要理解沙箱。
怎么想:沙箱就是测试环境,里面有沙箱应用和沙箱账号,账号里有虚拟金额,只用于测试。
怎么做:演示时选支付宝,点“立即支付”后调接口,拿到 payURL 跳转;手机装了支付宝会唤起客户端,没有则可用浏览器付款。支付完成点“完成”后,会按 payCallback 回跳。
易错点:沙箱账号可能不稳定,失败可重试;不要以为沙箱余额能真实消费;应用 ID 等主要由后端对接,前端了解流程即可。

WebSocket:长连接用于持续通信 ​

为什么需要:普通 HTTP 请求一去一回,连接结束,不适合持续聊天。WebSocket 建立连接后,浏览器和服务器可随时互发消息。
怎么想:把它想成打电话,接通后不挂断;onopen 是连接成功,onmessage 是收到服务器消息,onclose 是断开,send 是浏览器发消息。
怎么做:原生示例:

js
const ws = new WebSocket('wss://example.com')
ws.onopen = () => ws.send('测试')
ws.onmessage = (event) => console.log(event.data)
ws.onclose = () => console.log('连接断开')

调试时在 Network 面板看 WS,绿色箭头是发送,红色箭头是接收。
易错点:协议是 ws/wss,不是普通 HTTP;原生写法只用于理解,实际项目会用 socket.io 这类库。

先看支付流程:前端只做“两步” ​

为什么需要:支付平台很多,但网页端支付的主流程基本一致。先把角色和步骤看清,写代码时就不会把后端对接支付平台的工作误当成前端任务。
怎么想:转写稿中的流程图按列表示角色:用户、浏览器、商户后台、支付平台客户端/后台。真正需要前端关心的是“浏览器—商户后台”这一段;商户后台与支付平台之间的交互对前端透明。
怎么做:前端做两件事:①调用后端支付接口;②拿到返回地址后跳转,引导用户支付。转写稿反复强调:网页端是 window.location.href 跳转;小程序端不是跳网页,而是调用支付 API 拉起支付窗口。
易错点:不要把“后端返回地址”理解成前端自己生成支付页;也不要以为整个支付都由前端完成。支付成功后回到哪个页面,由前端传给后端的回调地址决定。

支付流程角色与步骤示意图

封装支付接口:参数要传对 ​

为什么需要:支付接口和普通接口一样,需要统一封装,避免页面里散落请求代码。
怎么想:转写稿中接口要求 POST,核心参数有支付方式、订单 ID、支付成功后的回调页。支付方式通常用 0/1/2 区分微信、支付宝、云闪付等;订单 ID 用于后端确定金额。
怎么做:在 service 下新建 payment.js,封装类似 orderPayment 的方法;页面点击“立即支付”时调用,并打印结果。
易错点:orderId 不是 orer IDD;payCallback 不是 pa call back;支付方式不能凭感觉写,要按接口文档。转写稿中当前后端只支持支付宝,微信支付只是参数占位。

js
// service/payment.js(按转写稿整理)
export const orderPayment = (data) => request({
  url: '/order/payment',
  method: 'POST',
  data
})

// 页面调用
const res = await orderPayment({
  payType: index,       // 0 微信 1 支付宝 2 云闪付
  orderId,
  payCallback: 'http://localhost:5173/pay/result'
})
console.log(res.data)

点击支付后跳转:拿到 payURL ​

为什么需要:后端与支付宝对接成功后,会把支付地址返回给前端;前端必须跳过去,用户才能付款。
怎么想:转写稿演示中,点击“立即支付”后选择支付宝,接口返回数据里有 payURL。这一步就是“引导用户支付”。
怎么做:监听支付组件的 confirm 事件,在回调里调接口;成功后执行 window.location.href = data.payURL。
易错点:回调地址端口要写自己的。转写稿里演示用 5173,若同学本地是 5174,却写死 5173,付完款就回不到自己的页面;回调页暂时空白是因为页面还没实现,不是支付失败。

js
const onConfirmPay = async () => {
  const { data } = await orderPayment({
    payType: index,
    orderId,
    payCallback: 'http://localhost:5173/pay/result'
  })
  window.location.href = data.payURL
}

调试过程:接口成功但页面没跳 ​

为什么需要:支付演示受测试账号、网络和本地端口影响,必须会看返回结果和网络面板。
怎么想:转写稿中出现过“接口返回成功、结果也打印了,但跳转没执行”的情况;先把地址手动粘到浏览器验证,再重走流程。还出现测试账号付款失败,刷新后恢复。
怎么做:先看 console.log(data) 是否有 payURL;再看 Network 请求是否成功;跳转不执行时检查代码位置和地址;测试账号不稳定时刷新重试。
易错点:不要看到“支付失败”就改前端逻辑,沙箱账号本身可能不稳定;也不要把本地回调地址写成别人的端口。

小程序与 H5 的“引导方式”不同 ​

为什么需要:前面流程一样,但网页能跳 URL,小程序不能打开任意网页,也不能用 window.location。
怎么想:网页端返回 payURL 并跳转;小程序端由后端返回 wx.requestPayment 所需参数,前端调 API 拉起支付窗口。
怎么做:用条件编译区分:H5 才跳转;微信小程序才调 wx.requestPayment;当前微信支付不支持时,选微信要提示“暂不支持微信支付”。
易错点:不要把 H5 的 window.location.href 写进小程序分支;条件编译别漏,否则一端能跑、另一端报错。

js
// #ifdef H5
window.location.href = data.payURL
// #endif

// #ifdef MP-WEIXIN
wx.requestPayment({ ...data.payParams })
// #endif

沙箱账号:测试支付不花真钱 ​

为什么需要:开发支付不能每次都真付 39 元,支付宝开放平台提供沙箱环境。
怎么想:沙箱就是测试环境,有沙箱应用 ID、商户号、沙箱买家账号和虚拟余额;后端已申请好,前端只需使用后端提供的沙箱能力。
怎么做:按转写稿演示,在支付宝开放平台控制台查看沙箱账号;付款时用讲义提供的测试账号,密码按演示输入。
易错点:沙箱余额只是测试数字;应用 ID 等配置主要由后端对接,前端不要自己乱改。

WebSocket:为持续通信做准备 ​

为什么需要:HTTP 请求通常一去一回,不适合持续聊天;WebSocket 建立连接后可随时收发消息。
怎么想:原生写法是 new WebSocket('wss://...'),连接成功触发 onopen,收到消息触发 onmessage,断开触发 onclose,发送用 send。
怎么做:转写稿用原生示例测试:打开页面看控制台“建立连接”,send 发消息,onmessage 收消息;Network 里看 WS,绿色向上是发送,红色向下是接收。
易错点:原生 WebSocket 只是理解概念,实际项目后面会用 Socket.IO 之类库;测试服务器可能不稳定,地址要确认可用。

js
const ws = new WebSocket('wss://example.com/ws')
ws.onopen = () => ws.send('测试')
ws.onmessage = (e) => console.log('收到消息', e.data)
ws.onclose = () => console.log('连接断开')

支付流程先看角色和边界 ​

为什么需要:支付不是前端单独完成的,先看清“谁和谁交互”,才不会把后端对接支付平台的工作误当成前端任务。怎么想:按流程图竖列看角色——用户、浏览器、商户后台、支付平台后台、支付客户端;真正参与开发的主要是浏览器、商户后台、支付平台后台。怎么做:前端只负责向商户后台发请求,拿到结果后引导用户支付;后端负责与支付宝/微信后台对接。易错点:把“支付成功通知”当成前端判断;把后端与支付平台交互当成前端职责。
支付流程角色与步骤图

前端只做两件事:调接口与引导支付 ​

为什么需要:网页支付流程里,前端能控制的环节很少,抓住边界就不会乱。怎么想:流程图第2步是前端调后端接口,第4步拿返回结果,第5步引导用户支付;中间后端与支付平台的交互对前端透明。怎么做:在“立即支付”的确认事件里调用封装好的支付接口,传支付方式、订单ID、回跳地址,并打印结果。易错点:在页面加载时就调支付接口;参数名不按接口文档写;忘记处理接口失败。

js
// 示意:字段名以接口文档为准
async function onConfirmPay(index) {
  const res = await paymentAPI.orderPayment({
    payType: index,       // 0微信 1支付宝 2云闪付(以文档为准)
    orderId,
    payCallback: 'http://localhost:5173/pay/result'
  })
  console.log(res.data)
}

支付完成后的回跳与结果页 ​

为什么需要:用户在支付宝页面付款后,要能回到自己的网站,并看到支付结果。怎么想:payCallback 就是支付成功后跳转的页面地址;本地开发要写自己的端口,5173/5174不能写错。怎么做:调接口时传回跳地址;拿到 payURL 后用 window.location.href 跳转。易错点:端口写错导致回不来;结果页还没添加所以显示空白;字段大小写写错。

js
window.location.href = res.data.payURL

网页、小程序与条件编译 ​

为什么需要:支付流程一致,但“引导用户支付”的方式不同。怎么想:网页用地址跳转;小程序没有 window.location 和 a 链接,要调 wx.requestPayment,参数由服务端返回。怎么做:H5 跳 URL,小程序调支付 API,并用条件编译隔离。易错点:把网页跳转代码放进小程序;当前后端只支持支付宝,选微信支付要提示“暂不支持微信支付”。

js
// #ifdef H5
window.location.href = res.data.payURL
// #endif

// #ifdef MP-WEIXIN
wx.requestPayment(payParams) // payParams 为服务端返回的支付参数
// #endif

支付宝沙箱账号与测试 ​

为什么需要:开发支付不能总用真钱,沙箱就是测试环境。怎么想:后端在支付宝开放平台创建应用、开通沙箱,前端拿后端配置好的沙箱账号测试。怎么做:控制台进入沙箱,查看沙箱应用和沙箱账号;测试账号有虚拟金额,支付失败可重试。易错点:沙箱金额不是真钱;沙箱账号可能不稳定;应用ID等主要是后端对接使用,前端不必自己申请。
支付宝沙箱账号示意图

WebSocket 为什么需要 ​

为什么需要:HTTP 请求一次一来回,结束后连接断开,不适合持续聊天。怎么想:WebSocket 建立持久连接,像打电话不挂断,双方可随时发消息。怎么做:new WebSocket 建连接,监听 onopen、onmessage、onclose,用 send 发消息。易错点:不是所有请求都要用 WebSocket;服务器返回消息才触发 onmessage;调试看 Network 的 WS 面板,不是 XHR。

js
const ws = new WebSocket(wsUrl)
ws.onopen = () => ws.send('测试')
ws.onmessage = (e) => console.log('收到消息', e.data)
ws.onclose = () => console.log('连接关闭')

WebSocket 调试与库的引入 ​

为什么需要:原生写法帮助理解原理,但项目里直接用原生效率低。怎么想:类比 Ajax 原生 XMLHttpRequest 与 axios,WebSocket 实际开发常用 Socket.IO。怎么做:在控制台观察“建立连接”“收到消息”;Network→WS 中绿箭头是发送、红箭头是接收,连续 send 可验证持续通信。易错点:测试服务器可能延迟或不稳定;不要把原生示例当成最终项目写法;先理解概念,再引入库。

代码示例 ​

示例一:支付接口封装 + 组件内调用(Vue3 / JS) ​

js
// service/payment.js —— 与支付相关的接口统一放在这里
import request from '@/utils/request'

// 订单支付
// paymentMethod: 支付方式 0-微信 1-支付宝 2-云闪付
// orderId:       订单ID(后端靠它才知道要付多少钱)
// payCallBack:   支付成功后跳回的页面地址
export const orderPayment = (data) => {
  return request.post('/patient/order/payment', data)
}
html
<!-- 订单详情页中,监听支付弹窗的 confirm 事件 -->
<template>
  <PaymentForm @confirm="onPaymentForm" @close="showPay = false" />
</template>

<script setup>
import { ref } from 'vue'
import { orderPayment } from '@/service/payment'

const orderId = ref('')      // 订单ID,从路由/详情接口里拿
const showPay = ref(false)

// 用户点了"确认支付"才会进来
// index:子组件 confirm 事件里带出来的支付方式下标
const onPaymentForm = async (index) => {
  // 第二步:前端调后端接口
  const { data } = await orderPayment({
    paymentMethod: index,
    orderId: orderId.value,
    payCallBack: 'http://localhost:5173/#/payment/result'
  })

  // 第四步:拿到后端返回的结果
  console.log(data)

  // 第五步:引导用户支付 —— H5 下用地址跳转
  // #ifdef H5
  window.location.href = data.payURL
  // #endif
}
</script>

关键行与易错点

  • orderPayment 的封装和普通接口一模一样,支付接口本身没有任何特殊性,不要把它想复杂。
  • 真正属于前端的只有两步:调接口(第二步)和 跳转 payURL(第五步);中间后端对接支付平台的部分前端不用管。
  • index 来自子组件 confirm 事件,要记得解构出来,否则拿不到用户选的支付方式。
  • window.location.href = data.payURL 就是在“引导用户支付”,网页端靠地址跳转;小程序里没有 location.href,要换成 wx.requestPayment。
  • 易错点:payCallBack 的端口要和本地实际端口一致(5173 / 5174 要看清),写错会导致支付完回不到自己的页面。

示例二:完整可运行的 H5 支付流程演示(HTML + JS) ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>优医咨询 - 支付流程演示</title>
</head>
<body>
  <h3>选择支付方式</h3>
  <label><input type="radio" name="pay" value="0" /> 微信支付</label>
  <label><input type="radio" name="pay" value="1" checked /> 支付宝支付</label>
  <label><input type="radio" name="pay" value="2" /> 云闪付</label>
  <p><button id="payBtn">立即支付</button></p>

  <script>
    // 模拟后端接口:真实项目中就是 service/payment.js 里的 orderPayment
    function orderPayment(params) {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve({
            code: 10000,
            message: 'success',
            data: {
              // 后端对接支付平台后返回的收银台地址
              payURL: 'https://openapi.alipay.com/gateway.do?mock=1&orderId=' + params.orderId
            }
          })
        }, 300)
      })
    }

    const orderId = '1699999999999' // 真实项目从路由或详情接口里取

    document.getElementById('payBtn').onclick = async () => {
      // 取当前选中的支付方式:0 微信 / 1 支付宝 / 2 云闪付
      const index = Number(document.querySelector('input[name="pay"]:checked').value)

      // 当前后端只支持支付宝,微信支付先提示
      if (index === 0) {
        return alert('暂不支持微信支付')
      }

      // 第二步:调接口,传支付方式、订单ID、回跳地址
      const { data } = await orderPayment({
        paymentMethod: index,
        orderId: orderId,
        payCallBack: location.origin + '/payment-result.html'
      })

      // 第四步:拿结果,结果里一定有 payURL
      console.log(data)

      // 第五步:跳转地址,引导用户去支付
      window.location.href = data.payURL
    }
  </script>
</body>
</html>

关键行与易错点

  • 整个支付前端只做两件事:orderPayment(...) 发请求、window.location.href = data.payURL 跳转,其余都是后端的事。
  • if (index === 0) return alert('暂不支持微信支付') —— 后端目前只支持支付宝,微信必须提前拦掉,否则用户点了没反应。
  • 跳转后用户手机上装有支付宝会直接唤起支付宝客户端;没装则显示“继续浏览器付款”的页面,这是平台行为,前端不用处理。
  • 支付成功后支付宝会跳回 payCallBack 指定的地址,所以这个地址必须是自己项目里真实存在的页面(示例里对应 payment-result.html),端口写错就回不来。
  • 易错点:payCallBack 是支付完成后跳转的“支付结果页”,不是当前页;结果页里要做支付成功/失败的提示。

示例三:原生 WebSocket 基础用法(HTML + JS) ​

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>WebSocket 原生用法</title>
</head>
<body>
  <button id="sendBtn">发送消息</button>
  <ul id="log"></ul>

  <script>
    const logEl = document.getElementById('log')
    const log = (msg) => {
      const li = document.createElement('li')
      li.textContent = msg
      logEl.appendChild(li)
    }

    // 注意协议是 ws / wss,不是 http / https
    // 这里用线上测试地址,真实项目换成后端提供的地址
    const ws = new WebSocket('wss://echo.websocket.events')

    // 连接建立成功
    ws.onopen = () => {
      log('建立连接')
      ws.send('测试')            // 浏览器 -> 服务器
    }

    // 收到服务器发来的消息(服务器每发一次就触发一次)
    ws.onmessage = (event) => {
      log('收到消息:' + event.data)
    }

    // 连接断开
    ws.onclose = () => {
      log('连接已断开')
    }

    ws.onerror = () => {
      log('连接出错')
    }

    // 连接不断开,就可以随时发消息
    document.getElementById('sendBtn').onclick = () => {
      ws.send('测试呀')
    }
  </script>
</body>
</html>

关键行与易错点

  • new WebSocket('ws://...') 一执行,浏览器和服务器之间就“搭好了桥”,连接会一直保持,这正是和 HTTP 一次性请求最大的区别(HTTP 一次请求一次响应后就断开,做不了持续聊天)。
  • ws.send(...) 是浏览器发给服务器;ws.onmessage 是服务器发回给浏览器时触发,两个方向不要搞混。
  • 回调里的数据在 event.data 上,不是直接一个字符串。
  • 协议头必须是 ws:// 或 wss://,写成 http:// 会直接报错;线上一般用 wss。
  • 调试时在浏览器 Network 面板选 WS 标签看,绿箭头是发送、红箭头是接收;这与之前看 XHR 请求的地方不一样。
  • 这只是原生写法,实际项目里会用 socket.io 这类库(类比:原生 XMLHttpRequest 之于 axios)。

常见错误 ​

1. 接口返回成功,但页面停在原地不跳转

  • 现象:点"立即支付"后 Network 里接口 200,控制台也打印了结果,但页面毫无反应。
  • 原因:取值的层级写错(写成 res.payURL,实际在 res.data.payURL),或者跳转语句写在了 await 之外/错误的分支里,拿到的是 undefined。
  • 修法:先 console.log(res) 看清结构再取字段;跳转前加一层判空 if (payUrl) window.location.href = payUrl,并在失败分支给出提示。

2. 支付完成后回到空白页或 404

  • 现象:支付宝里付完款点"完成",跳回来的页面一片空白。
  • 原因:payCallback 抄了讲师的 http://localhost:5173/...,而自己实际跑在 5174;或者目标页根本没建。
  • 修法:用 location.origin 动态拼回跳地址,或改成自己实际的端口;同时先把结果页建出来(哪怕只有一行文字),别让回跳地址指向不存在的路由。

3. 选了微信支付仍然发起支付请求

  • 现象:下拉里选"微信支付"点确认,接口报错或跳到一个打不开的地址。
  • 原因:接口约定 0/1/2 对应微信/支付宝/云闪付,但后端当前只接了支付宝;前端没做拦截。
  • 修法:在 confirm 回调里先判断 index === 0 → uni.showToast({ title: '暂不支持微信支付', icon: 'none' }) 并 return,再走正常支付逻辑。

4. H5 的跳转代码跑到了小程序里(或反之)

  • 现象:小程序端报 window is not defined / location is not defined,H5 端报 requestPayment is not a function。
  • 原因:跨端项目里直接写了平台专有 API,没有做隔离。
  • 修法:用条件编译把 window.location.href = payUrl 包进 #ifdef H5,把 uni.requestPayment({...四个参数}) 包进 #ifdef MP-WEIXIN;两边参数都来自同一个下单接口的返回。

5. 连点"立即支付"生成多笔订单 / 多次跳转

  • 现象:手抖点两下,后台出现两条订单记录,页面连跳两次。
  • 原因:回调里没有防重,请求是异步的,按钮在等待期间仍可点。
  • 修法:进入回调立刻置 paying = true(配合按钮 :disabled 或 loading),在 finally 里复位。

6. WebSocket 刚 new 完就 send,报 InvalidStateError

  • 现象:控制台抛 Failed to execute 'send' on 'WebSocket': Still in CONNECTING state。
  • 原因:连接是异步建立的,new 之后立刻 send 时握手还没完成。
  • 修法:把首次 send 放进 onopen 回调;或在发送前判断 ws.readyState === WebSocket.OPEN。另外别把 onmessage(收)和 send(发)用反。

练习 ​

练习 1|补全支付回调 在 confirm 事件的回调中:解构出 index,index === 0 时提示"暂不支持微信支付"并终止;否则调用已封装的 orderPayment,传入订单 ID、支付方式、回跳地址,成功后按平台引导用户支付(H5 跳地址 / 小程序调起支付面板)。要求代码能在 H5 与微信小程序两端都不报错。

练习 2|干掉硬编码 把 payCallback 从写死的 http://localhost:5173/#/pages/pay/result 改成运行时计算,保证换端口、换域名、打包上线都不用改代码。写出你的实现,并说明为什么不能只写 /#/pages/pay/result。

练习 3|防重复提交 给"立即支付"加上防重逻辑,要求:请求期间按钮不可再触发、请求失败后能恢复可点、控制台不出现重复请求。写出关键代码片段。

练习 4|支付结果页 新建支付结果页,onLoad 里从路由参数取出订单 ID,调用订单详情接口,根据返回的支付状态展示"支付成功 / 待支付 / 支付失败"三种文案。思考:这个页面能不能只靠 URL 参数判断成功,为什么?

练习 5|WebSocket 最小闭环 用原生 API 写一个页面:连接成功后自动发一条消息、收到消息打印到页面、断开时提示"连接已关闭",并提供一个输入框 + 按钮手动发消息。额外要求:连接未建立时点击发送不能报错。

原视频对照 ​

#视频时长
1优医咨询-支付流程说明11:03
2优医咨询-支付宝支付-109:47
3优医咨询-支付宝支付-211:14
4支付流程补充说明08:57
5支付宝支付账号管理07:17
6websocket介绍10:58
7websocket测试05:03

小结 ​

  1. 前端在支付链路里只负责三件事:调下单接口 → 拿结果 → 引导用户支付,中间的商户对接、签名、验签全在后端,不要越界。
  2. 各端支付流程完全一致,唯一的分叉点在"引导"这一步:H5 靠地址跳转,小程序靠 requestPayment 传服务端给的四个参数,APP 靠 SDK。写代码时把这一层单独抽出来,后面接微信支付几乎零改动。
  3. 小程序端的支付参数不是前端拼的,是下单接口返回的;前端只做搬运工。
  4. payCallback 是"付完钱回到哪"的地址,必须是自己项目里真实可访问的地址,端口/域名错了就是空白页——这是最常见的坑。
  5. 跨端项目里,凡是平台专有 API(window.location、requestPayment)都必须用条件编译隔离,否则一端必崩。
  6. 商户号、APPID、沙箱账号属于后端资产;沙箱测试账号余额是假的、偶尔不稳定,遇到失败先重试或换账号,别急着怀疑自己的代码。
  7. WebSocket 解决的是"一次连接、双向持续通信",原生 API 只需理解 onopen / onmessage / onclose / send 四件套,真实项目用 socket.io 这类库封装。
  8. 排错顺序固定:先 console.log 看返回结构 → 再看 Network(普通接口看 XHR,长连接看 WS)→ 最后怀疑代码逻辑。