Skip to content
LIQFYdocs
PTENENEN
Go to dashboard

错误

每个 Liqfy 错误都是一个结构稳定的 JSON 对象——只需针对它编写一次处理逻辑,无需再猜测。每个响应(无论成功还是错误)还会携带 X-Request-Id 头(如果你未发送则自动生成,如果你发送了则原样回显)。

#结构

json
{
  "error": {
    "type": "invalid_request_error",
    "code": "invalid_request",
    "message": "Validation failed",
    "details": [
      { "param": "amount", "message": "must be a positive integer" }
    ]
  },
  "request_id": "req_01J..."
}
字段是否总是存在说明
error.type是稳定的类别——取值为 invalid_request_error、authentication_error、permission_error、not_found_error、conflict_error、rate_limit_error、api_error 之一。
error.code是稳定的 snake_case 机器码。你的处理逻辑应根据它进行分支判断,而不是 message。
error.message是人类可读的说明。可安全记录到日志;不作为自动化处理的契约。
error.param恰好一个字段失败时标识该失败字段。若有多个字段失败,则不存在此字段,仅由 error.details 列出它们。
error.details校验失败时用于多字段校验错误的结构化 { param, message }[]。
request_id是联系支持团队时请提供此值——它可追踪每一条日志、span 和 Kafka 消息。也会在 X-Request-Id 响应头中回显。

兼容性别名: 响应体中除 error 对象外,还包含若干扁平的顶层字段——statusCode、message、requestId,以及(存在时)code / fieldErrors——供直接读取这些字段的客户端(包括控制台)使用。请读取嵌套的 error 对象;扁平别名不保证在未来的主版本中保留。

#状态码对照

#结果未知的收款创建

409 idempotency_outcome_unknown 表示原始创建请求可能已到达支付服务商,但其结果尚未确认。请使用相同的 Idempotency-Key 和请求体,并采用退避策略重试。不要为该收款生成新的键:这可能会产生另一笔支付。

即使过了五分钟或 24 小时,原始操作仍会被保留。重试时,如果已保存的结果可用,则返回该结果;否则返回此错误,而不会再次发起创建。查询不到结果并不能证明该收款从未被创建。如果结果仍然未知,请携带 request_id 联系支持团队。已完成的响应至少保留 24 小时。

#HTTP 状态码

HTTPerror.type建议的客户端操作
200——
201—保存返回的 id 并继续。
400invalid_request_error修正请求体/请求头后重试。不要盲目重试。
401authentication_error确认 Authorization: Bearer 头携带的是正确且有效的密钥。
403permission_error被权限范围、反欺诈/频率防护、API 密钥退款尝试(消息标记 REFUND_ADMIN_ONLY)或 IP 白名单拦截。请检查请求或联系支持团队。
404not_found_error检查你提供的 id。
409conflict_error同一个 Idempotency-Key 用于不同的请求体(idempotency_key_reused)、首次请求仍在处理中(idempotency_key_in_flight),或处于不可取消的状态(payment_not_cancellable)。
422invalid_request_error违反业务规则(仍属于请求层面的问题)——阅读 message,修正输入,不要重试。
429rate_limit_error退避。使用指数延迟 + 抖动重试;存在 Retry-After 时请遵循它。
5xxapi_error可以使用相同的 Idempotency-Key 安全重试——幂等性可防止产生重复。

#常见错误

#400 Bad Request

校验失败——error.code 为 invalid_request,error.details 列出所有失败的字段(仅当恰好一个字段失败时才存在 error.param)。

json
{
  "error": {
    "type": "invalid_request_error",
    "code": "invalid_request",
    "message": "Validation failed",
    "details": [
      { "param": "amount", "message": "must be a positive integer" },
      { "param": "payment_method", "message": "Invalid enum value" }
    ]
  },
  "request_id": "req_01J..."
}

当资金类写操作缺少 Idempotency-Key 头时,也会返回 400。在 POST /v1/charges 和 POST /v1/pix/charges 上,请求头是唯一的传递方式。在 POST /payments 上,请求体字段 idempotencyKey 可作为别名使用——如果两者都发送,则必须一致(不一致会返回 400),并以请求头的值为准。POST /payments/{id}/refund 仅限管理后台使用,因此不会接受来自 API 密钥的任何一种形式。

处理方式 修正请求后重试。切勿循环重试。


#401 Unauthorized

json
{
  "error": {
    "type": "authentication_error",
    "code": "unauthorized",
    "message": "Invalid API key"
  },
  "request_id": "req_01J..."
}

可能的原因:

  • 缺少请求头——确保已设置 Authorization: Bearer lq_live_...。
  • 密钥已轮换——请检查控制台。
  • 空白字符——密钥需精确匹配,不能有首尾空格。

处理方式 核实密钥。如果密钥正确,则它可能已被禁用——请联系支持团队。


#403 Forbidden

欺诈/频率防护会在收款到达服务商之前将其拦截——error.code 为 fraud.*:

json
{
  "error": {
    "type": "permission_error",
    "code": "fraud.customer_velocity_exceeded",
    "message": "Too many payment attempts for this customer in a short window"
  },
  "request_id": "req_01J..."
}

另一个 fraud.* 代码对应账户自身的频率限制(消息:"Too many payment attempts from this account in a short window")。

处理方式 审查该客户/放慢重试速度。只有仍然有效的收款才计入限制——已过期或已取消的收款(例如超时未支付的 Pix 二维码)会被忽略,因此重试这些收款是安全的。如果拦截看起来有误,请携带 request_id 发送邮件至 suporte@liqfy.com.br。

#退款——消息标记 REFUND_ADMIN_ONLY

POST /payments/{id}/refund 会以 403 拒绝所有 API 密钥调用方,无论请求体为何:退款只能从 Liqfy 管理后台发起。这不是一个 code 值——error.code 就是普通的 "forbidden",该标记仅作为消息前缀保留,因此请根据代码判断拒绝情况,仅将前缀作为标记进行匹配(例如 message.startsWith("REFUND_ADMIN_ONLY")):

json
{
  "error": {
    "type": "permission_error",
    "code": "forbidden",
    "message": "REFUND_ADMIN_ONLY: reembolsos devem ser solicitados pelo painel/admin, ..."
  },
  "request_id": "req_01J..."
}

参见 API 参考——退款。

#IP 白名单

如果账户配置了 IP 白名单,来自白名单之外地址的请求会以 403 被拒绝——同样的 permission_error 信封结构,error.code "forbidden"。该检查是核心账户路由(钱包、提现、交易、MED 案件、api-keys 及相关路由)上的按账户防护;支付和 webhook 服务上的资金流转路由(/v1/charges、/v1/pix/charges、/v1/payments、/v1/webhooks)不在其覆盖范围内。请将你的集成所使用的所有出口地址(包括 CI 和故障转移主机)加入白名单,否则请求根本无法到达密钥检查环节。详情及如何更新列表:API 密钥。

#account_suspended

账户已被暂停或封禁:在支持团队解除之前,对收款、支付、提现、钱包和交易的 API 密钥请求都会被拒绝。密钥本身仍然有效——轮换密钥无济于事。

json
{
  "error": {
    "type": "permission_error",
    "code": "account_suspended",
    "message": "Conta suspensa: a API não aceita requisições desta conta. Fale com o suporte."
  },
  "request_id": "req_01J..."
}

处理方式 不要重试。请携带 request_id 发送邮件至 suporte@liqfy.com.br。


#404 Not Found

json
{
  "error": {
    "type": "not_found_error",
    "code": "not_found",
    "message": "Charge ch_a1b2c3d4-0000-0000-0000-000000000009 not found"
  },
  "request_id": "req_01J..."
}

处理方式 核实 id。如果你刚刚创建了该资源,请等待 1–2 秒后重试。


#409 Conflict

json
{
  "error": {
    "type": "conflict_error",
    "code": "idempotency_key_reused",
    "message": "Idempotency key 'ORD-7821' already used with a different request body"
  },
  "request_id": "req_01J..."
}

你重放了一个 Idempotency-Key,但请求体与原始请求不同。Liqfy 拒绝静默覆盖。

另外两个 conflict_error 代码也会返回 409:

error.code何时处理方式
idempotency_key_reused相同的键,不同的请求体。使用新的键——常见原因是客户修改购物车后重复使用了同一个订单 id。
idempotency_key_in_flight使用该键的首次请求仍在处理中。等待其完成(或几秒后重试)——不要更换键。
payment_not_cancellable对 PROCESSING 收款执行 POST /payments/{id}/cancel。等待收款结算完成,然后改为从管理后台发起退款。

#业务规则拒绝

请求结构错误(字段缺失/无效)返回 400、error.type: "invalid_request_error"。某些下游配置错误——例如所请求的 payment_method 和 currency 没有配置服务商——会由内部编排器透传,带有其自身的状态码和 UPPER_SNAKE_CASE 机器码,最常见的是 422:

json
{
  "error": {
    "type": "invalid_request_error",
    "code": "NO_PROVIDER_CONFIGURED",
    "message": "No payment provider is configured for this payment method and currency."
  },
  "request_id": "req_01J..."
}

此类别中收款侧的代码:PROVIDER_UNAVAILABLE、PROVIDER_ERROR、PAYMENT_REJECTED、NO_PROVIDER_CONFIGURED、UNKNOWN_METHOD、MOCK_PROVIDER_IN_PRODUCTION、PAYER_IDENTITY_REQUIRED(当 expected_payer_tax_id 锁定收款且付款人证件不匹配时)。

付款/提现侧的代码:WALLET_NOT_FOUND、WALLET_NOT_SPENDABLE、AMOUNT_BELOW_MINIMUM、AMOUNT_ABOVE_MAXIMUM、DAILY_PAYOUT_LIMIT_EXCEEDED(422)、INSUFFICIENT_BALANCE、WITHDRAWAL_NOT_CANCELLABLE。注意提现路径上的例外:INSUFFICIENT_BALANCE 返回的是 400,而不是 422——请根据 error.code 进行分支判断,而不是状态码。

处理方式 阅读 error.code 和 message。不要重试——修正输入;如果账户本应配置服务商,请联系支持团队。


#429 Too Many Requests

json
{
  "error": {
    "type": "rate_limit_error",
    "code": "rate_limited",
    "message": "Rate limit exceeded"
  },
  "request_id": "req_01J..."
}

此响应上的请求头(来自 Kong 的限流插件,按 API 密钥计算——当前阈值参见 快速入门 §4):

text
Retry-After: 12
X-RateLimit-Limit-Minute: 5000
X-RateLimit-Remaining-Minute: 0

处理方式 退避——如果存在 Retry-After 则遵循它,否则使用带抖动的指数退避。

js
async function withRetry(fn, attempts = 5) {
  for (let i = 0; i < attempts; i++) {
    try { return await fn(); }
    catch (err) {
      if (err.status !== 429 && err.status < 500) throw err;
      const base = Math.min(1000 * 2 ** i, 30_000);
      const jitter = Math.random() * 0.3 * base;
      await new Promise(r => setTimeout(r, base + jitter));
    }
  }
  throw new Error('exhausted retries');
}

#5xx ——服务端

json
{
  "error": {
    "type": "api_error",
    "code": "service_unavailable",
    "message": "Upstream unavailable"
  },
  "request_id": "req_01J..."
}

可以安全重试。 使用相同的 Idempotency-Key,这样即使处理实际已成功但响应丢失,也不会产生重复收款。

#Webhook 投递错误(服务器 → 你的端点)

当 Liqfy 无法访问你的 webhook 端点时,失败会显示在 GET /v1/webhooks/deliveries 中:

json
{
  "id": "b2c3d4e5-f6a7-4890-9abc-def012345678",
  "status": "FAILED",
  "attempts": 3,
  "maxAttempts": 15,
  "lastStatusCode": 502,
  "lastError": "HTTP 502",
  "lastResponseBody": "Bad Gateway",
  "nextRetryAt": "2026-07-23T16:08:00.000Z"
}
lastStatusCode / lastError含义
2xx已投递。不会重试。
4xx你的端点拒绝了该负载。Liqfy 仍会继续重试,上限为 maxAttempts——如有需要,请修复后重放。
5xx你的端点已宕机。Liqfy 会以指数退避方式重试。
429 / Retry-AfterLiqfy 会遵循你的 Retry-After 并重新安排投递。
Connection timeout / ETIMEDOUT你的端点响应时间超过 30 秒。务必快速返回 200 OK——改为异步排队处理。
getaddrinfo ENOTFOUND你的域名无法解析。请更新已注册的 URL。
self signed certificateTLS 错误。Liqfy 要求有效的公共证书。

达到 maxAttempts(默认 15)后,该投递会被发送到 DLQ,并可手动重放。完整的重试/DLQ 语义参见 Webhooks。

#Webhook 管理错误

/v1/webhooks 管理路由上的代码(根据代码不同,为 permission_error / conflict_error / rate_limit_error):

error.code何时
WEBHOOK_ENDPOINT_DUPLICATE该 URL 已在此账户上注册(409)。
WEBHOOK_ENDPOINT_LIMIT账户已达到活跃端点数量上限。
WEBHOOK_HTTPS_REQUIRED在 sandbox/staging 之外使用了非 https:// URL。
WEBHOOK_OWN_DOMAIN该 URL 指向 Liqfy 域名。
WEBHOOK_TEST_RATE_LIMIT一小时内 POST /endpoints/{id}/test 调用次数过多(429)。
WEBHOOK_ROLE_REQUIRED调用方的角色无权管理端点(access.canManage: false)。
IMPERSONATION_VIEW_ONLY当前会话处于模拟登录状态,只能读取。
TENANT_NOT_ACTIVE账户未激活。

#解读 error.details

error.details 是一个由 { param, message } 组成的扁平数组——可直接渲染:

jsx
{error.details.map(({ param, message }) => (
  <li key={param}>{param}: {message}</li>
))}

fieldErrors 别名包含相同的信息,只是重组为 { field: reasons[] } 的形式:

json
{
  "fieldErrors": {
    "amount": ["must be a positive integer"]
  }
}

#何时联系支持团队

在以下情况下,请联系 suporte@liqfy.com.br(或提交工单):

  • 5xx 错误持续超过 5 分钟
  • 在 maxAttempts 之后,webhook 投递仍卡在 FAILED 状态
  • 意外收到 403 Forbidden
  • 相同的 Idempotency-Key 在不同调用中返回不同的收款(这是一个 bug——绝不应发生)

务必提供 request_id——它让我们能通过一次查询追踪每一条日志、span 和 Kafka 消息。

#实体读取限制

HTTP 429,error.type: rate_limit_error,error.code: entity_rate_limited。再次读取该实体前,请等待 Retry-After 指示的秒数。参见 轮询与限制。