PHP Comtrust SOAP API 返回 Response Code 5000
结论
.pfx 证书不是 SOAP envelope 中的字段。它会在建立 HTTPS 连接时作为客户端证书参与 TLS 双向认证。PHP SoapClient 通常无法直接使用 .pfx/.p12 文件,需要先将其转换为包含客户端证书和私钥的 PEM 文件,再通过 local_cert 和 passphrase 传入。
ResponseCode 5000 也未必是证书格式导致的。证书没有绑定到当前 demo 商户、商户凭据错误、请求来源 IP 未获授权,或者正式环境与 demo 环境的证书混用,都可能返回这个错误。
将 PFX 转换为 PEM
可以先生成一个同时包含客户端证书和私钥的 PEM 文件:
openssl pkcs12 -in merchant.pfx -clcerts -out merchant-client.pem
执行命令时通常需要输入两个密码:
.pfx文件的导入密码;- 为导出的 PEM 私钥设置的新密码。
之后配置 SoapClient 时,passphrase 应填写第 2 步设置的 PEM 私钥密码。它不一定与原来的 PFX 密码相同。
如果旧证书使用了 OpenSSL 3 默认不再启用的加密算法,可能需要加上 -legacy:
openssl pkcs12 -legacy -in merchant.pfx -clcerts -out merchant-client.pem
也可以分别导出证书和私钥:
openssl pkcs12 -in merchant.pfx -clcerts -nokeys -out merchant-cert.pem
openssl pkcs12 -in merchant.pfx -nocerts -out merchant-key.pem
然后将两者合并:
cat merchant-cert.pem merchant-key.pem > merchant-client.pem
merchant-client.pem 包含私钥,属于敏感文件。不要将它提交到 Git,也不要放在可被 Web 直接访问的目录中。
在 SoapClient 中使用证书
<?php
$wsdl = 'https://demo-ipg.comtrust.ae:2443/MerchantAPI.svc?singleWsdl';
$options = [
'local_cert' => __DIR__ . '/certificates/merchant-client.pem',
'passphrase' => 'PEM_PRIVATE_KEY_PASSWORD',
'exceptions' => true,
'trace' => true,
'cache_wsdl' => WSDL_CACHE_NONE,
'connection_timeout' => 30,
];
try {
$client = new SoapClient($wsdl, $options);
$request = [
'request' => [
'Customer' => 'Demo Merchant',
'Channel' => 'Web',
'Address' => 'demo-ipg.comtrust.ae',
'Language' => 'en',
'Password' => 'Comtrust',
'version' => '2.0',
'Amount' => '99.98',
'Currency' => 'AED',
'OrderID' => '123',
'OrderInfo' => 'Test Info',
'OrderName' => 'Test Name',
'ReturnPath' => 'https://example.test/payment/return',
'TransactionHint' => 'CPT:Y',
],
];
$result = $client->Register($request);
print_r($result);
} catch (SoapFault $e) {
echo 'SOAP Fault: ' . $e->getMessage() . PHP_EOL;
if (isset($client)) {
echo "Last request headers:\n";
echo $client->__getLastRequestHeaders() . PHP_EOL;
echo "Last request:\n";
echo $client->__getLastRequest() . PHP_EOL;
echo "Last response headers:\n";
echo $client->__getLastResponseHeaders() . PHP_EOL;
echo "Last response:\n";
echo $client->__getLastResponse() . PHP_EOL;
}
}
证书是在 TLS 层发送的,所以 __getLastRequest() 返回的 SOAP XML 中不会出现证书。这是正常现象。
如果合并后的 PEM 仍不能工作
可以明确指定 SSL stream context:
<?php
$sslContext = stream_context_create([
'ssl' => [
'local_cert' => __DIR__ . '/certificates/merchant-cert.pem',
'local_pk' => __DIR__ . '/certificates/merchant-key.pem',
'passphrase' => 'PRIVATE_KEY_PASSWORD',
'verify_peer' => true,
'verify_peer_name' => true,
],
]);
$client = new SoapClient(
'https://demo-ipg.comtrust.ae:2443/MerchantAPI.svc?singleWsdl',
[
'stream_context' => $sslContext,
'exceptions' => true,
'trace' => true,
'cache_wsdl' => WSDL_CACHE_NONE,
]
);
不同 PHP 和 OpenSSL 版本对分离证书与私钥的兼容性可能不同。实际使用中,包含证书和私钥的单一 PEM 文件通常更稳妥。
不要通过关闭服务器证书验证来绕过问题:
'verify_peer' => false,
'verify_peer_name' => false,
这两项设置只会关闭对服务器身份的验证,无法解决客户端证书未被认可的问题,还会降低连接安全性。
检查证书是否正确
查看 PFX 中的证书信息:
openssl pkcs12 -in merchant.pfx -clcerts -nokeys -info
查看导出的客户端证书:
openssl x509 -in merchant-cert.pem -noout -subject -issuer -serial -dates
检查私钥能否正常读取:
openssl pkey -in merchant-key.pem -check
检查私钥与证书是否匹配:
openssl x509 -in merchant-cert.pem -pubkey -noout | openssl sha256
openssl pkey -in merchant-key.pem -pubout | openssl sha256
两条命令输出的摘要应当一致。
还可以直接测试使用客户端证书建立 TLS 连接:
openssl s_client \
-connect demo-ipg.comtrust.ae:2443 \
-servername demo-ipg.comtrust.ae \
-cert merchant-cert.pem \
-key merchant-key.pem
这个测试只能检查 TLS 连接和证书交换情况,不能证明 Comtrust 已经将该证书正确绑定到商户账号。
继续排查 Response Code 5000
如果已经收到结构化的 RegisterResult,说明请求已到达服务端应用。接下来需要逐项检查:
Customer和Password是否为 Comtrust 分配的真实 demo 凭据,而不是文档中的示例值。- 客户端证书是否属于 demo 环境,以及它是否已绑定到同一个商户账号。
- 证书是否过期、被吊销,或者缺少完整证书链。
- 当前服务器的公网 IP 是否需要加入 Comtrust 白名单。
Address表示的究竟是商户地址、来源地址还是授权主机名,而不是 API 域名。该字段应以当前版本的 Comtrust 文档为准。- API 版本、端点和账号是否属于同一环境。
ReturnPath是否必须是支付平台能够访问的 HTTPS 地址。127.0.0.1只指向访问者自身,远程支付平台无法回调这个地址。- PHP 进程是否有权限读取 PEM 文件及其上级目录。
- PHP 是否缓存了 WSDL。调试时使用
WSDL_CACHE_NONE,以免继续读取旧配置。
如果这些设置都正确,但服务仍然返回 5000,应将响应中的 UniqueID 提交给 Comtrust 支持团队,由他们查询服务端日志。例如:
1942d0d0-37f5-4504-951e-3343f9b86ea7
只有服务端日志才能确认具体原因,例如客户端证书未收到、证书未绑定商户、商户密码错误、IP 未授权,或者其他权限策略。仅凭统一的 5000 响应,无法可靠地区分这些情况。
备注:内容仅供参考。
