实现 2Checkout payment API 后支付调用失败
结论
这两类错误的原因不同:
curl call failed通常说明请求还没有被 2Checkout 正常处理,应先检查网络、DNS、TLS、代理、防火墙和 cURL 配置。Payment Authorization Failed通常说明请求已经到达支付系统,但授权遭到拒绝。需要检查请求参数、测试与生产环境、商户配置、3D Secure、风控规则及发卡行返回结果。
不要把两者统一显示为”支付失败”。传输层错误和 API 响应应分开记录,但日志中不能保存完整卡号、CVV、密钥或访问令牌。
排查 curl call failed
先输出 cURL 错误编号、错误信息、HTTP 状态码和响应内容。只记录 curl_exec() 失败,通常无法定位具体原因。
<?php
$ch = curl_init($apiUrl);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Accept: application/json',
'Authorization: ' . $authorizationHeader,
],
]);
$response = curl_exec($ch);
$curlErrno = curl_errno($ch);
$curlError = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($response === false) {
error_log(sprintf(
'2Checkout transport error: errno=%d, error=%s',
$curlErrno,
$curlError
));
throw new RuntimeException('Payment service is temporarily unavailable.');
}
error_log(sprintf(
'2Checkout response: http_code=%d, body=%s',
$httpCode,
redactPaymentData($response)
));
实际使用时需要实现 redactPaymentData(),对卡号、CVV、令牌、邮箱和地址等敏感信息进行脱敏。
常见检查项如下:
-
确认服务器能够访问 API 域名
在应用所在的服务器上检查 DNS、HTTPS 连通性和证书验证。开发电脑能访问,并不代表生产服务器也能访问。
-
检查 TLS 和 CA 证书
PHP、libcurl、OpenSSL 或系统 CA 证书过旧时,可能导致 TLS 握手失败。不要长期使用以下配置绕过证书验证:
CURLOPT_SSL_VERIFYPEER => false应更新 CA 证书和运行环境,并保持
CURLOPT_SSL_VERIFYPEER开启。 -
检查超时设置
偶发失败可能与网络波动或超时时间过短有关。连接超时和请求总超时应分别设置。对于创建支付或扣款的请求,不要无条件直接重试,否则可能产生重复交易。
-
检查代理、防火墙和出口限制
确认服务器是否需要配置代理,并检查防火墙、安全组或主机服务商是否限制外部 HTTPS 请求。
-
确认 URL 和请求格式
核对 API 地址、HTTP 方法、
Content-Type、认证头及 JSON 编码结果,同时处理json_encode()失败的情况:$json = json_encode($payload, JSON_THROW_ON_ERROR);
排查支付授权失败
Payment Authorization Failed 是业务层拒绝,原因可能是参数错误、环境混用、商户配置、风控或发卡行拒绝。仅凭这一条通用提示,无法判断具体原因。
1. 检查测试与生产环境
以下内容必须属于同一环境:
- API endpoint
- 商户账号或 seller/merchant 标识
- API 用户名和密码
- 私钥、密钥或访问令牌
- 测试卡与真实银行卡
- Webhook 和返回地址配置
使用测试凭证请求生产接口,或用测试卡调用生产环境,通常都会失败。不同 2Checkout API 产品和版本使用的字段名称可能不同,应以对应版本的接口文档为准。
2. 记录完整的结构化错误
不要只读取顶层错误消息,还需要记录:
- HTTP 状态码
- API 错误代码
- 错误字段或参数名
- 交易号、订单号或请求 ID
- 3D Secure 状态
- 网关或发卡行返回的拒绝类别
下面是一个通用的响应处理示例。具体字段需要根据实际 API 响应调整:
<?php
$data = json_decode($response, true, 512, JSON_THROW_ON_ERROR);
if ($httpCode < 200 || $httpCode >= 300) {
error_log(json_encode([
'http_code' => $httpCode,
'error_code' => $data['error_code'] ?? null,
'message' => $data['message'] ?? null,
'request_id' => $data['request_id'] ?? null,
], JSON_UNESCAPED_UNICODE));
throw new RuntimeException(
'Payment request was rejected by the payment service.'
);
}
示例中的 error_code 和 request_id 不一定存在。使用前应先核对当前接口的响应结构。
3. 验证支付参数
重点检查以下内容:
- 金额是否使用接口要求的格式和最小货币单位
- 商户账号是否支持当前币种
- 账单姓名、地址、国家和邮政编码是否完整
- 国家代码和币种代码是否符合接口格式
- 卡片令牌是否有效、是否过期或已经使用
- 商户订单号是否重复
- 签名、哈希或认证字符串是否按要求生成
- 客户端生成的 token 是否提交到了正确的环境
如果使用托管组件或 tokenization,应尽量让敏感卡片信息直接提交给支付服务,服务器只接收 token。不要自行记录或持久化 CVV。
4. 检查 3D Secure 和前端流程
部分交易需要完成 3D Secure 验证。如果后端直接发起扣款,却没有正确处理跳转、challenge、回调或验证结果,最终也可能显示授权失败。
检查浏览器控制台和网络请求,确认:
- 支付组件已经成功加载
- token 已成功创建
- 3D Secure 页面没有被 CSP、iframe 或弹窗策略拦截
- 验证完成后,最终状态已经提交到后端
- 返回地址和 webhook 使用的是可公开访问的 HTTPS 地址
5. 区分参数拒绝与银行拒绝
即使请求参数正确,交易仍可能因为以下原因失败:
- 余额不足
- 卡片过期或被冻结
- 没有开通网上支付、跨境支付或特定币种支付
- 发卡行风控
- CVV 或账单地址校验失败
- 支付平台或商户账号的风控规则
- 同一银行卡在短时间内重复尝试
这些问题通常无法通过修改 PHP 代码解决。前端可以向用户显示适度的通用提示,并允许用户更换支付方式。后台则应保留脱敏后的错误代码和交易标识,方便后续查询。
建议的错误处理方式
传输失败、服务端错误和明确的授权拒绝需要分别处理:
<?php
if ($response === false) {
// 网络或 TLS 错误。交易状态可能未知,不要立即重复扣款。
markOrderAsPaymentUnknown($orderId);
} elseif ($httpCode >= 500) {
// 支付服务暂时异常。查询原交易状态后再决定是否重试。
markOrderAsPaymentPending($orderId);
} elseif ($httpCode >= 400) {
// 参数、认证或支付授权被拒绝。
markOrderAsPaymentFailed($orderId);
} else {
// 仍应解析响应中的最终交易状态,不能仅凭 HTTP 200 判定支付成功。
handlePaymentResponse($response);
}
对于可能创建交易的请求,建议使用 API 提供的幂等机制。如果当前接口不支持幂等键,可以使用唯一订单号,并在重试前查询原交易状态。
仍然无法定位时
可以准备以下信息并提交给 2Checkout 支持团队,但不要发送完整卡号、CVV、密码或密钥:
- 错误发生时间及服务器时区
- 使用的是测试环境还是生产环境
- HTTP 状态码和 API 错误代码
- 脱敏后的请求与响应
- 商户订单号
- 交易号或请求 ID
- cURL 错误编号
- 是否完成 3D Secure 验证
排查时,先确认请求是没有送达,还是已经送达但授权被拒。前者主要检查网络环境和 cURL 配置,后者则检查支付参数、商户配置、3D Secure、风控及发卡行返回结果。
备注:内容仅供参考。
