PHP 测试模式集成 ACH 报错 INVALID MERCH OR PASSWD
结论
INVALID MERCH OR PASSWD 表示当前网关端点不接受请求中提供的商户认证组合。最常见的情况是误将 Virtual Terminal 的网页登录密码填入 API 请求。
Virtual Terminal 登录密码不能直接用作 Const_AuthParam::PASSWORD。这段代码混用了两套不同的认证方式:
createClient、createPaymentMethod等 SOAP Web Service 请求使用:APILoginIDSecureTransactionKeyUTCTimeTSHash
ExecuteSocketQuery/AGI 交易请求通常使用:pg_merchant_idpg_password- 对应的 AGI/API transaction password
pg_password 必须填写 sandbox 为 API 或交易处理提供的密码,不能使用 Virtual Terminal 用户的登录密码。除非接口文档明确说明,否则也不能直接把 SecureTransactionKey 当作 pg_password。
正确配置认证信息
登录 sandbox Virtual Terminal,在类似 Gateway Settings、API Settings 或 Web Services 的页面中确认并生成以下信息:
class Const_AuthParam
{
// SOAP Web Services 身份认证
const APILoginID = 'sandbox_api_login_id';
const SecureTransactionKey = 'sandbox_secure_transaction_key';
// AGI/ExecuteSocketQuery 身份认证
const MERCHANT_ID = 'sandbox_merchant_id';
const PASSWORD = 'sandbox_api_or_transaction_password';
const ENV_MODE = 'test';
}
这几项凭据不一定对应同一个账户概念:
| 配置项 | 用途 |
|---|---|
APILoginID | 标识 SOAP API 调用者 |
SecureTransactionKey | 计算 TSHash,不随请求直接发送 |
MERCHANT_ID | 标识处理交易的商户 |
PASSWORD | AGI 请求的 API/transaction password |
| Virtual Terminal 登录密码 | 仅用于网页登录,不应作为 API 密码 |
Forte 的旧版接口在不同商户后台中可能使用不同的字段名称。如果 Gateway Settings 只显示 APILoginID 和 SecureTransactionKey,没有 AGI/transaction password,需要向 Forte sandbox 支持确认该测试商户是否启用了 AGI,以及 pg_password 应使用哪项凭据。
检查 AGI 请求是否真的发送了认证字段
Authentication 对象只传入了 createClient 和 createPaymentMethod 的 ticket 参数。调用 agiDoTransaction() 时,代码发送的内容只有:
$agiTrxParamsArr = get_object_vars($agiTrxParams);
$nuSoapClient->call('ExecuteSocketQuery', $agiTrxParamsArr);
因此,AGI_Transaction 中必须包含并初始化 pg_merchant_id 和 pg_password。否则,即使 Const_AuthParam 配置正确,实际请求中也可能没有这两个字段。
例如:
class AGI_Transaction
{
public $pg_merchant_id;
public $pg_password;
public $pg_client_id;
public $pg_payment_method_id;
public $pg_transaction_type;
public $pg_total_amount;
public $ecom_billto_postal_name_first;
public $ecom_billto_postal_name_last;
public function __construct()
{
$this->pg_merchant_id = Const_AuthParam::MERCHANT_ID;
$this->pg_password = Const_AuthParam::PASSWORD;
}
}
排查时也可以在调用前显式赋值:
$agiTrxObj = new AGI_Transaction();
$agiTrxObj->pg_merchant_id = Const_AuthParam::MERCHANT_ID;
$agiTrxObj->pg_password = Const_AuthParam::PASSWORD;
$agiTrxObj->pg_client_id = $client_id;
$agiTrxObj->pg_payment_method_id = $paymentMethod_id;
$agiTrxObj->pg_transaction_type = EFT_TRXType::SALE;
$agiTrxObj->pg_total_amount = 0.10;
$agiTrxObj->ecom_billto_postal_name_first = 'Avinash';
$agiTrxObj->ecom_billto_postal_name_last = 'Kshirsagar';
$response = $webServ->agiDoTransaction($agiTrxObj);
字段名必须与 AGI 接口的要求完全一致,包括大小写和下划线。
TSHash 的生成方式
SOAP Web Service 的认证摘要应基于:
HMAC-MD5(APILoginID + "|" + UTCTime, SecureTransactionKey)
现有实现的基本结构没有问题:
private function setTsHash()
{
$strToHash = $this->APILoginID . '|' . $this->UTCTime;
$this->TSHash = hash_hmac(
'md5',
$strToHash,
Const_AuthParam::SecureTransactionKey,
false
);
}
还需要逐项检查:
APILoginID和SecureTransactionKey是否来自同一个 sandbox 商户配置。UTCTime是否采用接口要求的 UTC/.NET ticks 格式。- 服务器时间是否存在明显偏差。
- 如果接口文档要求使用大写十六进制摘要,应改为:
$this->TSHash = strtoupper(
hash_hmac(
'md5',
$strToHash,
Const_AuthParam::SecureTransactionKey,
false
)
);
是否需要大写,应以当前接口版本的 WSDL 或文档为准,不能只根据错误信息判断。
TSHash 错误通常会影响携带 Authentication ticket 的 SOAP 调用。如果错误只出现在 ExecuteSocketQuery,应先检查 pg_merchant_id 和 pg_password。
确认所有凭据属于同一环境
测试和生产凭据不能混用。以下各项必须全部属于 sandbox:
APILoginIDSecureTransactionKeyMERCHANT_ID- AGI/API transaction password
- SOAP WSDL 地址
ExecuteSocketQuery地址
即使 merchant ID 正确,只要密码来自生产环境、其他 sandbox 商户或 Virtual Terminal 用户账户,网关仍可能返回:
INVALID MERCH OR PASSWD
还要检查 merchant ID 的格式。某些接口要求使用完整的商户标识,其中可能包含前缀或固定格式。不要自行删除前导字符、空格或其他组成部分。
修正重复创建客户的问题
当前代码调用了两次 insertNewClient():
$webServ->insertNewClient($client);
$client_id = $webServ->insertNewClient($client);
这样可能会创建两条客户记录,也可能导致第一次调用产生的错误被第二次调用覆盖。应改为:
$client_id = $webServ->insertNewClient($client);
if ($client_id === -1 || !is_numeric($client_id)) {
print_r($webServ->getError());
exit;
}
客户创建成功后,再继续创建付款方式:
$paymentMethod->ClientID = $client_id;
$paymentMethod_id = $webServ->insertNewPaymentMethod($paymentMethod);
if ($paymentMethod_id === -1 || !is_numeric($paymentMethod_id)) {
print_r($webServ->getError());
exit;
}
这样才能判断错误来自 createClient、createPaymentMethod,还是最后的 ExecuteSocketQuery。
建议的排查顺序
- 停止使用 Virtual Terminal 登录密码作为
Const_AuthParam::PASSWORD。 - 在 sandbox 的 Gateway Settings 中重新确认或生成
APILoginID和SecureTransactionKey。 - 确认 AGI 要求的 API/transaction password。如果后台没有提供,请联系 Forte sandbox 支持。
- 检查
AGI_Transaction是否包含并实际发送了pg_merchant_id和pg_password。 - 确认所有认证信息和请求端点都属于 sandbox。
- 分别打印每一步的错误,判断失败发生在 SOAP Web Service 还是 AGI。
- 调试日志只记录字段是否存在,不要写入完整密码、银行账号或
SecureTransactionKey。
仅凭 merchant ID 看起来正确,无法排除认证错误。当前需要先理清两套认证方式:SOAP 调用使用 APILoginID + SecureTransactionKey + TSHash,AGI 调用使用接口规定的 pg_merchant_id + pg_password。Virtual Terminal 的网页登录密码不属于这两套 API 凭据。
备注:内容仅供参考。
