Assembly Payments make_payment API 生产环境报错
结论
这类错误通常不是 PHP 或 make_payment API 的基本调用方式有问题,而是生产环境中的真实信用卡交易遭到支付网关拒绝,或在处理过程中失败。
Sandbox 使用模拟卡号、模拟账户和测试网关。测试付款成功,只能证明基本接口流程可以运行,不代表生产商户账户已经具备真实扣款条件。错误末尾的 (2) 很可能是支付处理链路返回的代码,但仅凭这段文字无法确认其官方含义,还需要查看完整的 API 响应、HTTP 状态码和 Assembly Payments 后台日志。
建议优先检查:
- 生产商户账户是否已经通过审核,并启用信用卡收款;
- API 凭证、用户、Item 和 Card Account 是否都属于 production;
- 信用卡是否通过生产环境的 tokenization 流程创建;
- 真实卡片是否被发卡行、收单机构或风控系统拒绝;
- 请求方法、请求体格式和请求头是否符合生产 API 的要求;
- 商户币种、金额、国家或地区以及卡片类型是否受支持。
为什么 Sandbox 正常而 Production 失败
Sandbox 和 production 是两套相互隔离的支付环境。
Sandbox 通常不会向发卡行发起真实授权,而是根据测试卡号或预设条件模拟交易结果。Production 则需要经过真实的支付处理流程,包括:
- 检查商户账户状态;
- 校验卡片 token 或 Card Account;
- 通过支付网关风控;
- 由收单机构处理;
- 获取发卡行授权;
- 检查金额、币种和地区限制。
因此,测试环境成功只能说明接口流程基本可用,无法证明真实卡片、生产账户和收单配置都已就绪。
建议的排查步骤
1. 保存完整响应,不要只读取错误消息
当前代码直接对响应进行 JSON 解码,可能会漏掉 HTTP 状态码、cURL 错误和原始响应。排查时至少应记录:
- 请求 URL;
- HTTP 状态码;
- 原始响应正文;
curl_errno();curl_error();- 响应头中的 request ID、correlation ID 或类似的追踪标识;
- Item ID、Payment ID 或 Transaction ID。
日志中不要记录完整卡号、CVV、Authorization 请求头或 API 密钥。
2. 核对生产环境配置
需要逐项确认:
- 当前使用的是 production API 凭证,不是 sandbox 凭证;
- Item 创建于 production 环境;
- 买方和卖方账户都存在于 production;
- 信用卡账户或 token 通过 production 环境创建;
- 商户账户已经完成审核,并启用了相应的付款方式;
- Item 当前状态允许调用
make_payment; - 支付金额、币种和账户余额符合接口约束。
在 Sandbox 中创建的用户、Item、Card Account 或 token,通常不能直接用于 production。
3. 使用可以正常交易的真实卡片验证
生产环境不能继续使用 sandbox 测试卡号。请确认:
- 卡片未过期;
- 卡号、有效期和 CVV 正确;
- 卡片允许线上交易;
- 卡片允许使用对应币种或进行跨境交易;
- 卡片有足够的可用额度;
- 发卡行没有拦截该商户或本次交易;
- 账单地址等风控信息符合要求。
如果换一张真实卡片后可以成功,问题更可能出在原卡片、发卡行或风控环节,而不是接口代码。
4. 修正 cURL 请求配置
原代码同时设置了:
CURLOPT_HTTPGET
和:
CURLOPT_CUSTOMREQUEST, "PATCH"
这两个选项表达的请求方式互相冲突。最终请求虽然仍可能表现为 PATCH,但没有必要再设置 CURLOPT_HTTPGET,而且不同 PHP 或 cURL 版本可能出现难以判断的行为。
可以改为:
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_POSTFIELDS => $accountdata,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_TIMEOUT => 30,
]);
$response = curl_exec($ch);
if ($response === false) {
throw new RuntimeException(
sprintf(
'cURL request failed (%d): %s',
curl_errno($ch),
curl_error($ch)
)
);
}
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$responseHeaders = substr($response, 0, $headerSize);
$responseBody = substr($response, $headerSize);
curl_close($ch);
try {
$data = Zend_Json::decode($responseBody);
} catch (Exception $e) {
throw new RuntimeException(
'Invalid JSON response. HTTP status: ' . $statusCode
);
}
if ($statusCode < 200 || $statusCode >= 300) {
error_log(sprintf(
'make_payment failed: HTTP %d; response=%s',
$statusCode,
$responseBody
));
}
return $data;
如果 $accountdata 是 JSON,必须确认请求体已经过 JSON 编码,并设置与之匹配的请求头:
$accountdata = json_encode($payload);
$headers = [
'Accept: application/json',
'Content-Type: application/json',
'Authorization: Bearer ' . $accessToken,
];
如果接口要求 application/x-www-form-urlencoded,则应使用:
$accountdata = http_build_query($payload);
$headers = [
'Accept: application/json',
'Content-Type: application/x-www-form-urlencoded',
'Authorization: Bearer ' . $accessToken,
];
具体格式应以当前使用的 Assembly Payments API 文档为准。JSON 请求体不能与表单类型的 Content-Type 混用。
不要关闭 TLS 证书校验
原代码包含:
curl_setopt($rest, CURLOPT_SSL_VERIFYPEER, false);
生产环境不应关闭 TLS 证书校验,否则连接无法可靠验证服务端身份,并会带来中间人攻击风险。正确的配置是:
curl_setopt($rest, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($rest, CURLOPT_SSL_VERIFYHOST, 2);
如果服务器报告 CA 证书错误,应更新操作系统或 PHP/cURL 使用的 CA 证书包,不要绕过验证。
关闭证书校验通常不能解释 “purchase transaction” 处理失败,因为这条错误表明请求已经到达支付服务端。不过,这个安全问题仍应立即修正。
如何判断问题出在哪一层
可以根据返回结果缩小排查范围:
401或403:重点检查生产凭证、权限和商户账户状态;404:检查 Item ID 是否属于 production,以及 API 地址是否正确;400或422:检查请求字段、请求体编码、Item 状态和业务规则;5xx:可能是支付平台或上游服务异常,应使用 request ID 联系技术支持;- HTTP 请求成功,但交易状态为 failed:更可能是收单机构、发卡行或风控系统拒绝了交易。
不要只凭错误文本判断原因。支付平台有时会将多个上游拒绝原因统一映射为一条通用消息,以免暴露敏感的风控信息。
联系支付平台支持时应提供什么
如果账户、请求和卡片都已核对,接口仍返回相同错误,可以向 Assembly Payments 支持团队提供:
- 错误发生的准确时间和时区;
- production Item ID;
- Payment ID 或 Transaction ID;
- HTTP 状态码;
- 完整错误响应,其中需删除密钥和卡片敏感信息;
- 响应中的 request ID 或 correlation ID;
- 金额和币种;
- 卡片品牌及后四位;
- 是所有卡片都失败,还是只有某一张卡片失败。
支付平台通常需要查询内部交易日志,才能确认 (2) 指向商户配置、网关拒绝、发卡行拒绝,还是其他处理错误。
注意事项
- 不要使用 sandbox 创建的 token、账户或 Item 调用 production;
- 不要记录完整卡号、CVV、API 密钥或 Authorization 内容;
- 不要反复重试已明确遭到拒绝的交易,以免触发风控或造成重复扣款;
- 再次付款前,应先查询交易状态,并使用平台支持的幂等机制;
- 生产环境应设置合理的连接超时和响应超时;
- 如果 API 域名、认证方式或接口版本已经调整,应以当前官方文档和商户后台配置为准。
排查时,可以先删除 CURLOPT_HTTPGET、恢复 TLS 校验并记录完整响应,再确认生产账户已经启用、所有资源都创建于 production,并使用合法的真实卡片测试。如果接口仍返回通用交易错误,就需要把交易标识提供给支付平台,由其查询生产网关日志中的具体拒绝原因。
备注:内容仅供参考。
