PAYATHON 2026

ProtectPay Hosted Payment Page 支付处理超时如何解决?

支付小周

结论

外部 Hosted Payment Page 显示 timed out 时,修改 MVC 前端通常解决不了问题。应优先检查 ProtectPay 提交支付后访问的回调地址、返回地址或通知接口,确认这些地址可以从公网访问,并且能快速返回成功响应。

如果支付请求已经进入 ProtectPay,但页面无法跳回网站,常见原因有:

  • 回调 URL 只能从本机或内网访问;
  • HTTPS 证书、TLS 协议或域名解析有误;
  • 回调接口要求登录,因此返回 302;
  • 防火墙、WAF、代理或 IP 白名单拦截了 ProtectPay;
  • MVC 防伪验证拒绝了第三方 POST;
  • 回调处理时间过长,超过 ProtectPay 的等待时间;
  • 创建 hosted transaction identifier 时传入的 URL 无效或编码有误;
  • ProtectPay 或其上游支付处理服务发生超时。

具体要使用哪个 URL 字段、超时时间是多少,应以当前 ProtectPay 接口文档和商户配置为准。仅凭浏览器页面上的提示无法确定原因。

先确认支付是否实际成功

“没有跳回网站”不一定表示”支付失败”。Hosted Payment Page 超时后,交易仍有可能已经获得授权。

先在 ProtectPay 商户后台或交易查询接口中,通过以下信息查询交易状态:

  • hosted transaction identifier;
  • merchant transaction ID;
  • ProtectPay transaction ID;
  • 支付发生时间和金额。

确认状态之前,不要让用户直接重新付款,以免造成重复扣款。业务系统应使用唯一订单号,并确保支付结果处理具有幂等性。

检查回调地址是否可访问

回调地址必须是 ProtectPay 服务器能够访问的公网 HTTPS 地址,例如:

https://payments.example.com/payment/protectpay/callback

以下地址通常不能作为生产环境的回调地址:

http://localhost:5000/payment/callback
http://127.0.0.1/payment/callback
http://192.168.1.20/payment/callback

同时检查以下事项:

  • 域名能否通过公共 DNS 正确解析;
  • HTTPS 证书是否有效,证书链是否完整;
  • 接口是否依赖浏览器 Cookie 或用户 Session;
  • 接口是否会跳转到登录页;
  • 接口是否允许 ProtectPay 使用的 HTTP 方法;
  • 反向代理是否把请求转发到了正确的应用;
  • 防火墙或 WAF 是否拦截了请求;
  • URL 中的查询参数是否正确编码。

可以从网站所在网络之外测试接口:

curl -i -X POST "https://payments.example.com/payment/protectpay/callback" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data "test=1"

这个测试只能说明普通公网客户端可以访问该接口,不能完全证明 ProtectPay 服务器也能访问。最终仍要结合服务器访问日志判断。

让回调接口尽快返回

ProtectPay 等待响应期间,不要执行发送邮件、生成报表或调用多个下游系统等耗时操作。回调接口只需完成必要流程:

  1. 接收并解析请求;
  2. 验证来源和签名;
  3. 根据交易 ID 判断是否已经处理;
  4. 保存原始通知和交易状态;
  5. 立即返回 ProtectPay 要求的成功响应;
  6. 将后续业务交给后台队列处理。

下面是一个简化的 ASP.NET MVC 示例。字段名和响应内容仅用于展示结构,必须替换成 ProtectPay 文档规定的实际格式:

[AllowAnonymous]
[HttpPost]
public ActionResult ProtectPayCallback()
{
    string transactionId = Request.Form["transactionId"];
    string status = Request.Form["status"];
    string signature = Request.Form["signature"];

    if (string.IsNullOrWhiteSpace(transactionId))
    {
        return new HttpStatusCodeResult(400);
    }

    if (!VerifyProtectPaySignature(Request.Form, signature))
    {
        return new HttpStatusCodeResult(401);
    }

    // 必须按 transactionId 或 merchant transaction ID 做幂等更新。
    SavePaymentNotification(transactionId, status, Request.Form);

    // 耗时操作应进入后台队列。
    QueuePaymentProcessing(transactionId);

    return new HttpStatusCodeResult(200);
}

如果回调使用 JSON,可以采用类似结构:

[AllowAnonymous]
[HttpPost]
public ActionResult ProtectPayCallback(ProtectPayNotification notification)
{
    if (notification == null ||
        string.IsNullOrWhiteSpace(notification.TransactionId))
    {
        return new HttpStatusCodeResult(400);
    }

    if (!VerifyProtectPayNotification(notification, Request))
    {
        return new HttpStatusCodeResult(401);
    }

    SavePaymentNotification(notification);
    QueuePaymentProcessing(notification.TransactionId);

    return new HttpStatusCodeResult(200);
}

不要直接照搬示例中的字段名或签名算法。不同的 ProtectPay 接口或商户配置可能使用不同的通知格式。

排查 MVC 常见拦截问题

第三方服务器发送的 POST 通常不带网站生成的防伪 Token。如果回调 Action 使用了:

[ValidateAntiForgeryToken]

请求可能还没进入业务代码就被拒绝。第三方回调一般需要单独豁免防伪 Token,但仍须使用 ProtectPay 提供的签名、共享密钥或交易查询机制验证请求,不能直接信任提交的内容。

还要检查回调 Action 是否使用了:

[Authorize]

如果访问回调需要用户登录,ProtectPay 可能会收到 302,随后被重定向到登录页。服务端通知接口应允许匿名请求到达,再通过支付平台提供的安全机制完成认证。

查看日志定位超时发生在哪一段

生成 hosted transaction identifier 时,应记录:

  • 本地订单号;
  • hosted transaction identifier;
  • 配置的返回 URL 和通知 URL;
  • 请求时间;
  • ProtectPay 返回的关联 ID。

回调入口应记录:

  • 收到请求的时间;
  • HTTP 方法和路径;
  • HTTP 状态码;
  • 处理耗时;
  • ProtectPay 提供的交易 ID;
  • 验签结果。

不要记录完整卡号、CVV、磁道数据、密钥或完整的敏感请求体。

可以根据日志做初步判断:

  • 完全没有回调日志:检查 URL、DNS、证书、防火墙和 ProtectPay 配置;
  • 收到请求但返回 401 或 403:检查身份验证、验签和 WAF;
  • 返回 302:检查登录跳转、HTTPS 跳转和 URL 规范化;
  • 返回 404:检查 MVC 路由和部署路径;
  • 返回 500:查看应用异常日志;
  • 请求耗时过长:减少同步处理,将耗时任务放入后台队列;
  • 回调成功但浏览器没有返回:检查 return URL,并确认返回页面是否依赖已经过期的 Session。

无法从本地解决时

如果服务器日志表明 ProtectPay 从未访问回调地址,或者回调已经快速返回成功,但 Hosted Payment Page 仍然提示超时,应向 ProtectPay 支持团队提供以下信息:

  • hosted transaction identifier;
  • merchant transaction ID;
  • ProtectPay transaction ID;
  • 准确时间和时区;
  • 测试或生产环境;
  • 返回 URL 和通知 URL 的域名;
  • 服务端访问日志中的状态码和耗时;
  • 页面错误截图或错误代码。

不要发送完整信用卡号、CVV、API 密钥或其他 PCI 敏感数据。支付平台支持团队可以通过关联 ID 判断超时发生在 Hosted Payment Page、回调请求还是上游支付处理器。

备注:内容仅供参考。