PAYATHON 2026

TwoCheckout 抛出 System.ArgumentNullException:Value cannot be

支付阿杰

结论

System.ArgumentNullException: Value cannot be null. Parameter name: s 表明 SDK 把一个 null 字符串传给了不接受空值的 .NET 方法,例如 Encoding.GetBytes(string s)、DateTime.Parse(string s) 或类似的解析函数。

这个异常通常与 IIS 的写入权限无关。给 IIS_IUSRS 增加 solution 目录权限一般解决不了问题。这里运行的是 NUnit 测试,执行进程很可能也不是 IIS。

仅凭异常消息无法确定哪个字符串是空值,需要查看完整堆栈。结合代码,建议优先检查:

  • API 用户名和密码是否为真实有效的 API 凭据;
  • sale_id 是否存在于当前 Sandbox 环境;
  • 旧版 SDK 是否收到了空响应,或无法解析服务器返回的错误响应;
  • TLS、代理、网络连接或接口地址变化是否导致请求未获得正常响应;
  • SDK 是否假定响应中一定包含某个字段,而服务器实际没有返回该字段。

先获取完整异常堆栈

当前代码只捕获 TwoCheckoutException,实际抛出的却是 ArgumentNullException,所以程序不会进入这个 catch。

调试时不要把异常当作测试成功。可以暂时改为记录完整异常信息:

[Test]
public void _004_TestSaleStop()
{
    TwoCheckoutConfig.ApiUsername = "actual-api-username";
    TwoCheckoutConfig.ApiPassword = "actual-api-password";
    TwoCheckoutConfig.Sandbox = true;

    var service = new SaleService();
    var options = new SaleStopServiceOptions
    {
        sale_id = 9093729066354
    };

    try
    {
        var result = service.Stop(options);

        Assert.That(result, Is.Not.Null);
        Assert.That(result, Is.InstanceOf<TwoCheckoutResponse>());
    }
    catch (Exception ex)
    {
        TestContext.WriteLine(ex.ToString());
        throw;
    }
}

ex.ToString() 会包含:

  • 实际异常类型;
  • 完整调用栈;
  • InnerException;
  • SDK 中触发异常的类、方法以及可能的行号。

调用栈中第一个属于 TwoCheckout SDK 的位置最值得关注。例如,出现下面的内容:

System.Text.Encoding.GetBytes(String s)
TwoCheckout....(...)

说明 SDK 正在编码一个空字符串。如果出现 DateTime.Parse(String s),则表示服务器响应中的某个日期字段缺失。Parameter name: s 只是底层方法的形参名称,并不是名为 s 的 TwoCheckout 配置项。

检查配置和环境是否匹配

如果示例中的 "username" 和 "password" 只是占位符,需要将它们替换为 TwoCheckout 后台提供的 API 凭据。普通登录邮箱或账户密码通常不能代替 API 凭据。

还要确认下面的配置与数据来源一致:

TwoCheckoutConfig.Sandbox = true;

具体来说:

  • Sandbox = true 时,sale_id 必须属于 Sandbox;
  • 正式环境中的 sale ID 不能直接用于 Sandbox;
  • Sandbox 凭据和正式环境凭据不能混用。

凭据无效或 sale ID 不存在时,服务器可能返回错误响应。旧版 SDK 对某些错误响应处理不完整,可能不会抛出清晰的 TwoCheckoutException,而是在解析缺失字段时触发 ArgumentNullException。

可以在调用前检查必要数据:

if (string.IsNullOrWhiteSpace(TwoCheckoutConfig.ApiUsername))
{
    throw new InvalidOperationException("TwoCheckout API username is missing.");
}

if (string.IsNullOrWhiteSpace(TwoCheckoutConfig.ApiPassword))
{
    throw new InvalidOperationException("TwoCheckout API password is missing.");
}

if (options.sale_id <= 0)
{
    throw new ArgumentOutOfRangeException(
        nameof(options.sale_id),
        "A valid sale ID is required.");
}

检查 SDK 收到的原始响应

如果配置无误,可以在 SDK 发起 HTTP 请求和反序列化响应的位置设置断点,查看:

  • HTTP 状态码;
  • 响应正文是否为 null 或空字符串;
  • 服务器是否返回了 HTML 错误页,而不是 SDK 预期的 JSON/XML;
  • 错误响应是否缺少 SDK 强制解析的字段;
  • 请求是否因 TLS、证书、代理或连接问题提前失败。

日志中不要输出 Authorization、API 密码或其他敏感请求头。

如果确认服务器返回了空内容,而 SDK 随后直接尝试解析,可以在本地维护的 SDK 代码中加入明确的检查:

if (string.IsNullOrWhiteSpace(responseBody))
{
    throw new TwoCheckoutException(
        "TwoCheckout returned an empty response.");
}

如果响应不为空,应记录脱敏后的响应内容,再结合调用栈查找缺失的字段。不要仅凭 Parameter name: s 猜测字段名称。

检查旧版 SDK 与运行环境

2checkout-dotnet 是较早期的 SDK。如果项目使用较新的 .NET 运行环境,或者服务端接口、TLS 要求和响应格式发生了变化,旧 SDK 可能遇到兼容性问题。

可以按以下顺序检查:

  1. 项目引用的 SDK 具体版本和提交版本;
  2. SDK 中 SaleService.Stop 最终访问的接口地址;
  3. 当前运行环境是否支持服务端要求的 TLS 版本;
  4. 是否有更新版本、维护分支或针对空响应的修复;
  5. 使用相同凭据和 sale ID 通过官方接口工具直接请求,能否得到正常响应。

在旧版 .NET Framework 环境中,如果调用栈或网络日志明确显示 TLS 协商失败,可以在应用启动阶段验证 TLS 1.2 设置:

ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

只有确认问题来自 TLS 后才需要这样处理。单凭 ArgumentNullException 不能断定 TLS 是根因。

为什么修改目录权限无效

目录权限问题通常会引发以下异常:

  • UnauthorizedAccessException
  • SecurityException
  • IOException

一般不会只出现:

ArgumentNullException: Value cannot be null. Parameter name: s

NUnit 测试通常由测试运行器进程执行,而不是 IIS Application Pool。即使确实存在文件访问问题,也应给实际运行测试的账户授予目标目录所需的最小权限,而不是让 IIS_IUSRS 对整个 solution 目录拥有完全控制权限。

排查时应重点查看完整调用栈、真实 API 响应,以及凭据和运行环境是否匹配。如果调用栈最终证明 SDK 在处理空响应或缺失字段时传入了 null,可以升级 SDK,或在相应的解析位置加入空值检查。同时还需要继续查明响应为空的原因,例如认证失败、数据不匹配或网络异常。

备注:内容仅供参考。