PAYATHON 2026

POST REFUND 自 2 月 10 日起返回 400 错误

支付小周

结论

请求失败是因为请求体的序列化方式不对。当前的 payload 是对象,不是 JSON 字符串。即使手动设置了 Content-Type: application/json,客户端仍可能将对象编码成表单数据,造成请求头声明的格式与实际请求体不一致。

发送请求前,应使用 JSON.stringify() 序列化 payload,并通过客户端的 contentType 选项指定媒体类型。

解决方法

如果脚本使用 Google Apps Script 的 UrlFetchApp.fetch(),可以这样发送请求:

const url = 'https://connect.squareup.com/v1/me/refunds';

const requestBody = {
  payment_id: paymentId,
  type: 'FULL',
  reason: 'reason'
};

const options = {
  method: 'post',
  headers: {
    Authorization: `Bearer ${personalId}`,
    Accept: 'application/json'
  },
  contentType: 'application/json',
  payload: JSON.stringify(requestBody),
  muteHttpExceptions: true
};

const response = UrlFetchApp.fetch(url, options);

Logger.log(response.getResponseCode());
Logger.log(response.getContentText());

需要把 payload 写成:

payload: JSON.stringify(requestBody)

不要直接传入对象:

payload: requestBody

原因

在 UrlFetchApp 等 HTTP 客户端中,如果 payload 是 JavaScript 对象,客户端通常会将其编码为表单数据,例如:

payment_id=...&type=FULL&reason=reason

但请求头声明的格式却是:

Content-Type: application/json

服务器会按 JSON 解析请求体,但实际收到的内容并不是合法 JSON,因此可能返回:

{
  "type": "bad_request",
  "message": "The Content-Type of this request is not supported. Supported type(s): application/json"
}

配置对象中出现了正确的 Content-Type,不代表实际传输的请求体就是 JSON。请求头声明的类型必须与请求体格式一致。

如果使用其他 HTTP 客户端

处理方式相同,先把对象序列化为 JSON,再发送请求。例如使用 fetch:

const response = await fetch(url, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${personalId}`,
    Accept: 'application/json',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    payment_id: paymentId,
    type: 'FULL',
    reason: 'reason'
  })
});

使用 curl 排查时,可以发送一条等价请求:

curl -X POST 'https://connect.squareup.com/v1/me/refunds' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  --data '{"payment_id":"PAYMENT_ID","type":"FULL","reason":"reason"}'

注意事项

  • 不要在日志中记录完整的访问令牌、付款 ID 或客户信息。
  • JSON 字符串必须使用双引号,也不能包含示例之外的反引号、引号或右花括号。
  • Content-Type 错误不一定表示请求头缺失,也可能是请求体格式与声明的类型不一致。
  • 单凭这条错误信息,无法确认服务器是否在 2 月 10 日调整过校验策略。即使服务端以前允许这种格式不一致的请求,也应按照 API 要求发送真正的 JSON。
  • 如果序列化后仍然报错,请记录实际的 HTTP 状态码和响应体,并检查当前调用的 Square API 端点、认证方式和请求字段是否仍适用于该接口。

备注:内容仅供参考。