PAYATHON 2026

如何在 VueJs 组件中获取 body 传递的参数值?

支付老李

结论

Vue 组件的 created() 无法直接读取第三方支付页面跳转时携带的 HTTP request body。

request body 由接收请求的服务器处理,而 Vue 组件运行在浏览器中。页面跳转完成后,浏览器不会向页面 JavaScript 暴露原始 POST body。

支付网关应先把结果提交到后端回调地址。后端解析请求、验签并保存结果,然后将浏览器重定向到 Vue 页面。Vue 组件再通过订单号或一次性凭证向后端查询支付状态。

推荐处理流程

通常可以按以下流程处理:

  1. 支付网关向后端回调接口发送 POST 请求。
  2. 后端解析请求体,并验证支付平台的签名。
  3. 后端根据商户订单号更新支付状态。
  4. 后端将浏览器重定向到 Vue 支付结果页。
  5. Vue 组件从 URL 中获取订单号或短期凭证。
  6. Vue 调用后端接口查询最终支付结果。

有些支付平台会提供两个不同的地址:

  • 浏览器返回地址,用于将用户带回网站。
  • 服务端通知地址,用于可靠地通知支付结果。

支付结果应以经过验签的服务端通知为准,不能只凭浏览器返回的数据确认支付成功。

后端接收支付结果

下面假设支付平台使用 application/x-www-form-urlencoded 提交数据,后端使用 Express:

import express from 'express';

const app = express();

app.use(express.urlencoded({ extended: false }));
app.use(express.json());

app.post('/api/payment/callback', async (req, res) => {
  const paymentData = req.body;

  // 必须根据支付平台文档验证签名。
  const signatureValid = verifyPaymentSignature(paymentData);

  if (!signatureValid) {
    return res.status(400).send('Invalid signature');
  }

  const orderId = paymentData.order_id;
  const paymentStatus = paymentData.status;

  // 应实现幂等更新,避免重复通知造成重复处理。
  await updatePaymentStatus(orderId, paymentStatus);

  // 只把非敏感标识放入 URL。
  res.redirect(
    303,
    `/payment/result?orderId=${encodeURIComponent(orderId)}`
  );
});

verifyPaymentSignature() 和字段名称需要根据实际支付网关的协议实现。示例中的 order_id 和 status 不能直接当作固定字段使用。

使用 303 See Other 重定向,可以明确让浏览器通过 GET 请求打开结果页面,避免沿用原来的 POST 方法。

在 Vue 组件中查询结果

如果项目使用 Vue Router,可以在 created() 中读取查询参数,再向后端发起请求:

export default {
  data() {
    return {
      loading: true,
      payment: null,
      error: null
    };
  },

  async created() {
    const orderId = this.$route.query.orderId;

    if (!orderId) {
      this.loading = false;
      this.error = '缺少订单号';
      return;
    }

    try {
      const response = await fetch(
        `/api/payments/${encodeURIComponent(orderId)}`,
        {
          credentials: 'same-origin'
        }
      );

      if (!response.ok) {
        throw new Error(`Request failed: ${response.status}`);
      }

      this.payment = await response.json();
    } catch (error) {
      this.error = '暂时无法获取支付结果,请稍后重试';
      console.error(error);
    } finally {
      this.loading = false;
    }
  }
};

后端查询接口还应验证当前用户是否有权查看该订单:

app.get('/api/payments/:orderId', async (req, res) => {
  const order = await findPaymentByOrderId(req.params.orderId);

  if (!order) {
    return res.status(404).json({
      message: 'Payment not found'
    });
  }

  // 还需要校验订单是否属于当前登录用户。
  res.json({
    orderId: order.id,
    status: order.paymentStatus,
    amount: order.amount
  });
});

使用 Vue 3 Composition API 时,也可以在 setup() 或 onMounted() 中执行相同的查询逻辑。问题不在于选择哪个生命周期钩子,而在于支付请求体必须先由服务器接收。

如果数据实际在 URL 中

只有支付平台通过查询字符串返回数据时,前端才能直接读取。例如:

https://example.com/payment/result?orderId=123&status=success

在 Vue Router 中可以这样获取:

created() {
  const orderId = this.$route.query.orderId;
  const status = this.$route.query.status;
}

如果不使用 Vue Router,可以这样读取:

const params = new URLSearchParams(window.location.search);

const orderId = params.get('orderId');
const status = params.get('status');

用户可以自行修改 URL 中的 status=success,所以这个值只能用于页面展示或触发后端查询,不能作为修改订单状态的可信依据。

为什么不能在 created() 中读取 POST body

下面的写法在浏览器端并不存在:

created() {
  console.log(request.body);
}

request 和 request.body 属于服务器端的请求上下文。Vue 生命周期钩子可以访问的内容包括:

  • 组件状态;
  • Vue Router 参数;
  • URL 查询字符串;
  • localStorage 或 sessionStorage;
  • Cookie;
  • 前端主动请求后端后得到的数据。

即使支付平台通过 HTML 表单向 Vue 路由发起 POST 请求,Vue 页面加载后仍然无法取得那次表单提交的原始 body。服务器必须先接收 POST 请求,再通过重定向、服务端会话或数据库把必要的信息传给前端。

注意事项

  • 必须验证支付网关的签名、时间戳、金额、币种、商户订单号和商户身份。
  • 不要把银行卡信息、签名、支付令牌或完整回调内容放进 URL。
  • 支付回调可能重复发送,订单更新逻辑必须具备幂等性。
  • 浏览器返回可能早于或晚于服务端通知,结果页应支持轮询或手动刷新状态。
  • 不要只根据前端传回的 success 字段发货、充值或确认订单。
  • 如果前后端部署在不同域名下,还需要正确配置 CORS、Cookie 的 SameSite 属性和 HTTPS。
  • 如果支付平台要求回调接口返回特定文本或状态码,应严格按照接口文档响应。在这种情况下,服务端通知接口通常不应直接负责页面跳转。

备注:内容仅供参考。