PAYATHON 2026

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 则需要经过真实的支付处理流程,包括:

  1. 检查商户账户状态;
  2. 校验卡片 token 或 Card Account;
  3. 通过支付网关风控;
  4. 由收单机构处理;
  5. 获取发卡行授权;
  6. 检查金额、币种和地区限制。

因此,测试环境成功只能说明接口流程基本可用,无法证明真实卡片、生产账户和收单配置都已就绪。

建议的排查步骤

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,并使用合法的真实卡片测试。如果接口仍返回通用交易错误,就需要把交易标识提供给支付平台,由其查询生产网关日志中的具体拒绝原因。

备注:内容仅供参考。