PAYATHON 2026

PHP 集成 WePay payment gateway 时 checkout 页面空白

支付小周

结论

checkout 页面空白,通常不是 WePay 没有返回页面,而是 PHP 在创建 checkout 时出错,同时服务器关闭了错误显示。正确的处理流程是:调用 /checkout/create``,读取 WePay 返回的 checkout_uri`,然后将浏览器重定向到这个地址。

先查看 PHP 错误日志和 WePay API 的原始响应。不要直接输出可能不存在的 checkout_uri,也不要把 WePay 支付页的源码当作本地页面渲染。

先让错误可见

在测试环境中临时开启错误报告:

<?php

error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('log_errors', '1');

生产环境不要向访客显示错误,只将错误写入日志:

<?php

error_reporting(E_ALL);
ini_set('display_errors', '0');
ini_set('log_errors', '1');

同时检查 Web 服务器和 PHP-FPM 的错误日志。空白页通常由以下问题引起:

  • SDK 文件未正确加载;
  • PHP 命名空间或类名有误;
  • cURL、OpenSSL 等扩展未启用;
  • access_token 或 account_id 无效;
  • 混用了 sandbox 凭据和 production 环境;
  • API 返回了错误对象,但代码仍继续读取 checkout_uri;
  • header() 执行前已经产生输出;
  • 异常未被捕获。

正确创建并跳转到 checkout

下面的示例适用于使用经典 /checkout/create 接口的 WePay PHP SDK。不同版本的 SDK 可能有不同的初始化方法和返回值类型,请以所用 SDK 的代码及配套文档为准。

<?php

require_once __DIR__ . '/wepay.php';

error_reporting(E_ALL);
ini_set('display_errors', '1');

$clientId     = getenv('WEPAY_CLIENT_ID');
$clientSecret = getenv('WEPAY_CLIENT_SECRET');
$accessToken  = getenv('WEPAY_ACCESS_TOKEN');
$accountId    = getenv('WEPAY_ACCOUNT_ID');

try {
    // 测试环境。正式上线时改用对应的 production 初始化方法。
    WePay::useStaging($clientId, $clientSecret);

    $wepay = new WePay($accessToken);

    $response = $wepay->request('/checkout/create', [
        'account_id'        => $accountId,
        'amount'            => 10.00,
        'short_description' => 'Order #1001',
        'type'              => 'GOODS',
        'redirect_uri'      => 'https://example.com/payment/return.php',
    ]);

    // SDK 可能返回对象,也可能返回数组。
    $checkoutUri = is_array($response)
        ? ($response['checkout_uri'] ?? null)
        : ($response->checkout_uri ?? null);

    if (!$checkoutUri) {
        throw new RuntimeException(
            'WePay response does not contain checkout_uri: ' .
            json_encode($response, JSON_UNESCAPED_UNICODE)
        );
    }

    header('Location: ' . $checkoutUri, true, 302);
    exit;
} catch (Throwable $e) {
    error_log('WePay checkout error: ' . $e->getMessage());
    http_response_code(500);

    // 测试环境可以显示详细错误,生产环境应改成通用提示。
    echo '无法创建支付页面:' .
        htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8');
}

如果项目仍使用不支持 Throwable 的旧版 PHP,可以改为:

catch (Exception $e) {
    // ...
}

旧版 PHP 已经存在安全和兼容风险,应优先升级。

检查 API 的实际返回值

调试时不要假设请求一定成功,可以暂时输出返回结果:

echo '<pre>';
var_dump($response);
echo '</pre>';
exit;

成功响应中应包含用于跳转的 checkout_uri。如果 API 返回错误信息,需要检查:

  • error;
  • error_code;
  • error_description;
  • HTTP 状态码;
  • account_id 是否属于当前 access_token;
  • 请求字段是否符合当前 API 版本的要求。

记录响应时,应遮盖 access_token、client_secret、银行卡信息和其他敏感数据。

排查重定向问题

如果已经取得 checkout_uri,浏览器却没有跳转,请检查 header() 之前是否产生了任何输出,包括空格、BOM、调试文本和模板内容。

if (headers_sent($file, $line)) {
    throw new RuntimeException(
        "HTTP headers already sent in {$file} on line {$line}"
    );
}

纯 PHP 文件通常不需要在末尾添加 ?>,这样可以避免文件末尾的空白提前发送响应头。

也可以直接查看跳转地址:

var_dump($checkoutUri);
exit;

如果这个地址可以在浏览器中直接打开,问题通常出在本地重定向逻辑。如果地址为空,则需要继续检查 /checkout/create 的响应。

环境与服务器检查

确认 PHP 已加载必要的扩展:

<?php

var_dump([
    'curl'    => extension_loaded('curl'),
    'openssl' => extension_loaded('openssl'),
    'json'    => extension_loaded('json'),
]);

还需要确认:

  • 服务器可以通过 HTTPS 访问 WePay API;
  • CA 证书有效,不要关闭 SSL 校验来绕过证书错误;
  • staging 使用测试凭据,production 使用正式凭据;
  • redirect_uri 是完整 URL;
  • 服务器时间准确;
  • PHP 和 Web 服务器的日志目录可写。

不要直接嵌入未知的 checkout 页面

应优先将浏览器重定向到 WePay 返回的 checkout_uri。如果使用 iframe 加载,浏览器安全策略或 WePay 页面的响应头可能会阻止页面,导致页面空白或无法加载。

如果当前 WePay 产品使用 JavaScript checkout、合作伙伴接口或其他新版流程,就不能直接照搬旧版 /checkout/create 示例。此时需要根据账户类型、SDK 版本和对应的 API 文档调整请求方式。排查步骤仍然相同:先捕获 PHP 错误,再检查 API 响应,取得有效的支付地址后再执行跳转。

备注:内容仅供参考。