PAYATHON 2026

AWS Lambda POST 请求返回 404 Error

支付老李

结论

这个 404 是 USIO 服务器返回的 HTTP 响应,与 Lambda 或 VPC 网络故障无关。Lambda 已成功连接到 api.securepds.com,但服务器没有找到与当前请求匹配的接口。

代码中有两个明确的问题:

  1. Content-Type 被写成了 https://application/json,正确值是 application/json。
  2. obj 已经是 JSON 字符串,doPostRequest() 又对它执行了一次 JSON.stringify(),导致请求体被二次序列化。

当前代码还只记录了状态码,没有读取响应体。服务器解释 404 原因的错误信息很可能就在响应体中。

修正请求头和请求数据

不要手工拼接 JSON 字符串,直接构造 JavaScript 对象:

const obj = {
  MerchantID: merchantId,
  Login: merchantLogin,
  Password: merchantPassword,
  Token: token,
  Amount: result.Items[i].amount
};

只在发送请求时调用一次 JSON.stringify()。

还应删除 Access-Control-Allow-Origin。这是服务器返回给浏览器的 CORS 响应头,Lambda 发起服务器端请求时不需要携带它。

推荐实现

const https = require('https');

const doPostRequest = (data) => {
  return new Promise((resolve, reject) => {
    const body = JSON.stringify(data);

    const options = {
      hostname: 'api.securepds.com',
      port: 443,
      path: '/2.0/payments.svc/JSON/SubmitTokenPayment',
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Content-Length': Buffer.byteLength(body),
        'Accept': 'application/json'
      }
    };

    const req = https.request(options, (res) => {
      res.setEncoding('utf8');

      let responseBody = '';

      res.on('data', (chunk) => {
        responseBody += chunk;
      });

      res.on('end', () => {
        resolve({
          statusCode: res.statusCode,
          headers: res.headers,
          body: responseBody
        });
      });
    });

    req.on('error', (error) => {
      reject(error);
    });

    req.write(body);
    req.end();
  });
};

调用时:

try {
  const response = await doPostRequest(obj);

  console.log('Status code:', response.statusCode);
  console.log('Response headers:', JSON.stringify(response.headers));
  console.log('Response body:', response.body);

  if (response.statusCode < 200 || response.statusCode >= 300) {
    throw new Error(
      `USIO request failed with status ${response.statusCode}: ${response.body}`
    );
  }
} catch (error) {
  console.error('Error sending USIO payment request:', error);
}

为什么原请求体可能不正确

原代码使用模板字符串构造 obj:

const obj = `{"MerchantID":"...",
"Login":"...",
"Password":"...",
"Token":"...",
"Amount":"..."}`;

此时 obj 已经是字符串。如果再次执行:

JSON.stringify(obj)

发送的内容会类似:

"{\"MerchantID\":\"...\",\"Login\":\"...\"}"

外层多了一对引号。服务器收到的是一个包含 JSON 文本的字符串,而不是 JSON object。

如果必须保留字符串形式,发送时就不要再次序列化:

req.write(data);

不过,直接构造对象更安全,也能避免括号遗漏、引号转义错误以及特殊字符破坏 JSON。问题中的示例字符串没有显示结尾的 }。如果实际代码同样缺少这个括号,请求体就是无效 JSON。

如果修正后仍然返回 404

读取完整响应体,并继续检查以下项目:

  • USIO 提供的生产环境或测试环境 hostname 是否确实为 api.securepds.com。
  • 接口路径、大小写和版本号是否完全一致: /2.0/payments.svc/JSON/SubmitTokenPayment。
  • 该 endpoint 是否接受 POST,接口文档是否要求提供其他认证头。
  • 响应中的 Location header 是否指向另一个地址。
  • 使用相同的 hostname、path、headers 和 body 在 Postman 或 curl 中测试,判断问题来自接口配置还是代码。

例如:

curl -i \
  -X POST \
  'https://api.securepds.com/2.0/payments.svc/JSON/SubmitTokenPayment' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  --data '{
    "MerchantID": "REDACTED",
    "Login": "REDACTED",
    "Password": "REDACTED",
    "Token": "REDACTED",
    "Amount": "10.00"
  }'

如果 curl 使用相同内容仍然返回 404,问题出在 endpoint、环境或 USIO 侧的路由要求,而不是 Lambda。

注意事项

不要在日志中打印完整的 payment payload、密码或 payment token。调试时应隐藏敏感字段:

console.log({
  MerchantID: merchantId,
  Login: merchantLogin,
  Password: '[REDACTED]',
  Token: '[REDACTED]',
  Amount: result.Items[i].amount
});

未配置 VPC 的 Lambda 通常可以通过默认公网出口访问外部 HTTPS API。网络不可达时,常见表现是超时、DNS 错误或连接错误,而不是由对方服务器返回有效的 404 响应。

备注:内容仅供参考。