PAYATHON 2026

支付流程中如何在 Return URL 指定原浏览器

支付小周

结论

普通的 HTTP/HTTPS Return URL 无法指定由哪个浏览器打开,也无法保证返回发起支付的浏览器标签页。

支付完成后,DBS PayLah 会把 Return URL 交给手机操作系统处理。系统通常使用用户设置的默认浏览器,不会记录支付流程是从 Chrome、Safari 还是其他浏览器发起的。具体行为取决于操作系统和 PayLah 应用,网页端无法控制。

Return URL 应填写完整的 HTTPS 地址:

$returnUrl = 'https://www.example.com/payment_success.php';

补全 https:// 只能保证 URL 格式正确,不能指定浏览器。

为什么无法指定原浏览器

下面这样的 URL 只包含访问地址,不包含浏览器或原标签页信息:

https://www.example.com/payment_success.php

PayLah 应用打开这个地址时,移动系统需要重新选择处理该 URL 的应用。对于 HTTPS 地址,通常会有以下情况:

  • Android 使用默认浏览器或已配置的链接处理应用。
  • iOS 通常使用默认浏览器。
  • 原浏览器中的页面、标签页和 JavaScript 上下文不会自动转移到新浏览器。
  • 如果打开了不同的浏览器,Cookie 和 sessionStorage 可能无法共享。

在 Return URL 中加入浏览器名称或自定义查询参数,也不会改变系统的处理方式:

https://www.example.com/payment_success.php?browser=chrome

这里的 browser=chrome 只是传给服务器的普通参数,不能要求操作系统调用 Chrome。

推荐的处理方式

1. 不依赖 Return URL 判断支付成功

Return URL 适合向用户展示支付结果,不应作为确认付款成功的唯一依据。建议按以下流程处理:

  1. 创建支付请求时生成唯一订单号。
  2. 将订单初始状态保存为 pending。
  3. 通过支付平台提供的服务端通知、Webhook 或订单查询 API 获取最终状态。
  4. Return URL 只携带订单标识或不可预测的状态令牌。
  5. 结果页面向自己的服务器查询支付状态。
  6. 用户手动切回原浏览器后,原页面查询同一笔订单的状态。

可用的通知方式和字段名称取决于 DBS PayLah 当前提供的接口,应以其接入文档为准。

例如:

<?php

$orderId = createOrderId();
$state = bin2hex(random_bytes(32));

// 将 orderId、state、用户会话和 pending 状态保存到数据库。
savePaymentState($orderId, $state, 'pending');

$returnUrl = sprintf(
    'https://www.example.com/payment_success.php?order_id=%s&state=%s',
    rawurlencode($orderId),
    rawurlencode($state)
);

不要因为收到以下请求就把订单标记为已支付:

GET /payment_success.php?order_id=123

Return URL 可能被重复访问,也可能被手工构造。服务器必须核对 state,并根据可信的服务端支付结果确认订单状态。

2. 让原页面在恢复显示时查询支付状态

付款完成后,即使结果页在默认浏览器中打开,用户仍可能手动切回原浏览器。原页面可以在重新获得焦点或恢复可见时查询订单状态:

async function refreshPaymentStatus() {
  const response = await fetch(
    `/api/payment-status.php?order_id=${encodeURIComponent(orderId)}`,
    {
      credentials: 'same-origin',
      cache: 'no-store'
    }
  );

  if (!response.ok) {
    return;
  }

  const result = await response.json();

  if (result.status === 'paid') {
    window.location.replace(
      `/payment_success.php?order_id=${encodeURIComponent(orderId)}`
    );
  }
}

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'visible') {
    refreshPaymentStatus();
  }
});

window.addEventListener('focus', refreshPaymentStatus);

支付等待页面也可以在有限时间内轮询状态:

const timer = setInterval(async () => {
  await refreshPaymentStatus();
}, 3000);

setTimeout(() => {
  clearInterval(timer);
}, 5 * 60 * 1000);

轮询间隔和持续时间应根据业务量、接口限制和支付有效期调整。

如果 Return URL 在另一个浏览器中打开,原浏览器里的 Cookie、localStorage 和 sessionStorage 通常无法使用。因此,结果页不能只依靠原浏览器的会话识别订单。

可以采用以下方式:

  • 使用随机且一次性的 state。
  • 使用短期有效的签名令牌。
  • 在服务端绑定订单号与当前用户身份。
  • 用户登录后,通过服务端查询订单。

例如,可以为返回参数添加签名:

<?php

$orderId = 'ORDER-123456';
$expires = time() + 900;
$payload = $orderId . '|' . $expires;
$signature = hash_hmac('sha256', $payload, $serverSecret);

$returnUrl = sprintf(
    'https://www.example.com/payment_success.php?order_id=%s&expires=%d&signature=%s',
    rawurlencode($orderId),
    $expires,
    rawurlencode($signature)
);

服务器收到请求后应检查签名和有效期。签名只能用于识别参数和防止篡改,不能代替实际支付状态的验证。

能否使用 Chrome 专用链接

部分平台支持浏览器专用 URL Scheme 或 Android Intent,例如指定 Chrome 的包名,但不建议把它们用作支付 Return URL,原因包括:

  • iOS 和 Android 使用不同的格式。
  • 用户可能没有安装指定浏览器。
  • 浏览器版本和系统策略可能限制调用。
  • 即使成功打开浏览器,通常也只是打开新页面,未必能恢复原标签页。
  • PayLah 可能只接受 HTTP/HTTPS Return URL。
  • 强制绕过用户的默认浏览器设置,兼容性和使用体验都较差。

除非 DBS PayLah 的官方接入规范明确支持自定义 Scheme、Android Intent 或”返回调用方”机制,否则不要假设这些地址可以用作 Return URL。

如果必须回到固定应用

如果支付流程运行在自有移动应用中,可以考虑:

  • iOS Universal Links。
  • Android App Links。
  • 自有 Custom URL Scheme。
  • 支付 SDK 提供的应用回调机制。

这些方案可以把用户带回自己的 App,但仍无法可靠地指定并恢复某个第三方浏览器中的原标签页。对于纯 Web POS 场景,可靠的做法仍是由服务端确认支付状态,并确保任意浏览器中的结果页都能查询同一笔订单。

注意事项

  • Return URL 必须包含协议,优先使用 https://。
  • 不要在 URL 中传递密钥、银行卡信息或其他敏感数据。
  • 不要因为用户访问了成功页面就把订单更新为 paid。
  • 支付回调需要校验签名、金额、币种、商户号和订单号。
  • 服务端通知应能处理重复回调,订单更新需要具备幂等性。
  • 即使用户关闭 PayLah、网络中断或没有跳转到 Return URL,服务端仍应能够查询订单状态。
  • Webhook、状态查询和调用方回跳是否可用,取决于实际使用的 DBS PayLah 产品,应查阅其最新接入文档。

备注:内容仅供参考。