支付流程中如何在 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 适合向用户展示支付结果,不应作为确认付款成功的唯一依据。建议按以下流程处理:
- 创建支付请求时生成唯一订单号。
- 将订单初始状态保存为
pending。 - 通过支付平台提供的服务端通知、Webhook 或订单查询 API 获取最终状态。
- Return URL 只携带订单标识或不可预测的状态令牌。
- 结果页面向自己的服务器查询支付状态。
- 用户手动切回原浏览器后,原页面查询同一笔订单的状态。
可用的通知方式和字段名称取决于 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);
轮询间隔和持续时间应根据业务量、接口限制和支付有效期调整。
3. 避免依赖特定浏览器的 Cookie
如果 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 产品,应查阅其最新接入文档。
备注:内容仅供参考。
