跳到正文
PayHub开发者文档

错误码与 HTTP 状态

HTTP 状态说明请求有没有被执行,业务结果看 status。错误响应结构、retryable 的含义、30 个错误码、按接口查看与处理示例。

最后更新于 2026-10-14 · 契约 v1.0.0

先看结论

  1. HTTP 状态说明请求有没有被执行:4xx 表示本次请求被拒绝、没有产生业务效果(409 表示已有相关对象);2xx 表示已处理。
  2. 业务结果看对象的 status:2xx 不等于扣款成功,支付成功以 status=succeeded 或经验签的 payment.succeeded 通知为准。
  3. 5xx 与超时是“结果未知”:请求可能已经被受理。用原幂等键重试或按商户订单号查询,绝不换新键重扣(503 temporarily_unavailable 除外:本次未受理)。
收到错误后的处理决策
收到错误后的处理决策

HTTP 状态

HTTP含义你的处理是否可能已产生业务效果能否用原幂等键重试
200查询、确认或幂等复用已完成的响应检查业务状态是,以 status 为准不需要
201成功创建业务对象不代表支付必然成功,继续检查 status是不需要
202平台已受理,结果待确认保存对象 ID,查询或等待通知可能,结果未知可以(返回同一对象的最新状态),但应优先等通知或查询
400参数缺失、格式错误等修正参数,不自动重扣否修正后用新键重新提交
401密钥无效或失效检查凭据否修正凭据后可重试
403权限、商户能力或环境不允许检查权限,不绕过限制否先解决权限
404对象不存在或不属于当前商户环境核对 ID 与所用 Key 的环境;平台不暴露其他商户对象是否存在否—
409幂等冲突、订单冲突、在途尝试或状态冲突查询已有对象(related_resource_id)后决定下一步已有相关对象存在视错误码(operation_in_progress 可以;其余不可)
422币种不支持、金额不符、退款资金不足等业务校验失败根据错误码修正业务否修正后重新提交(用新键)
429请求受限,尚未按本次新请求执行按 Retry-After 和原幂等键退避重试否是,等待 Retry-After 秒后
5xx平台或依赖异常可能已受理;同键重试或按业务号查询,不能换键猜测重扣未知(503 temporarily_unavailable 除外:未受理)是;或按商户订单号 / 退款号查询
  • 网络超时、连接中断、拿不到响应,与 5xx 同样处理:结果未知。
  • 503 temporarily_unavailable 是唯一明确表示“未受理”的 5xx:平台暂停了该通道的新交易,本次请求没有执行、没有产生 Payment 或扣款,稍后用原幂等键重试即可;响应不说明内部原因。其他 5xx 一律按结果未知处理。
  • 5xx 响应可能不是 JSON(例如网关返回的 HTML),解析失败也按“结果未知”处理。
  • 平台已经受理支付后,不会因为上游超时把它伪装成 429;已受理的请求返回 202 processing。

错误响应结构

字段类型说明
error.codestring稳定的机器可读错误码,按它编程;须容忍未知错误码
error.messagestring面向开发者的脱敏说明,不含卡数据或密钥;文案可能调整,不要解析它
error.fieldstring,可为空出错的请求字段(如 amount、browser_info.ip_address、Idempotency-Key);与字段无关时为 null
error.related_resource_idstring,可为空相关的已有对象 ID,如冲突的 Payment pay_…;用它查询后决定下一步
error.retryableboolean是否适合以原幂等键重试当前请求(见下一节)
request_idstring本次请求的唯一标识;排查与联系技术支持时提供
ErrorResponse 示例
{
  "error": {
    "code": "payment_in_progress",
    "message": "该订单已有未完成的支付尝试,请查询原支付结果。",
    "field": null,
    "related_resource_id": "pay_demo_001",
    "retryable": false
  },
  "request_id": "req_demo_error"
}

说明:400 / 422 修正后用哪个幂等键

被 400、401、403、422 拒绝的请求没有执行业务,也不占用幂等键;修正参数后使用新的幂等键重新提交。这一规则只适用于 400 / 401 / 403 / 422,409、429、5xx 不适用。

retryable 的含义

  1. retryable=true 只表示“适合用原来的幂等键重新发送同一个请求”,例如 rate_limited、operation_in_progress、internal_error、temporarily_unavailable。
  2. 它不授权换新的幂等键、换新的商户订单号或“再创建一笔支付”。结果未知时换键重试可能造成重复扣款。
  3. retryable=false 不代表“这笔支付失败了”:例如 payment_in_progress 是 false,因为正确做法是查询已有支付,而不是重发。
  4. 上游明确拒绝扣款时不返回错误,而是返回 Payment,status=failed 并带 failure,见支付失败原因与 API 错误的区别。

警告:结果未知时不要换幂等键或订单号重新扣款

超时、5xx、202 都可能已经扣款;用同一个幂等键重试,或按订单号查询。详见结果未知时如何查单。

错误码字典

按 error.code 编程,并容忍将来新增的错误码(按 HTTP 状态处理)。每行可通过 #code-<错误码> 直接链接,例如 #code-payment_in_progress。

错误码HTTP含义常见原因处理建议可用原键重试
invalid_request示例:merchant_order_no 格式不正确400参数缺失或格式错误缺 Idempotency-Key;订单号含非法字符;payment_method_data 与 payment_method_id 同时提交;confirm=false 携带卡信息;confirm=true 缺 return_url;收银会话两组身份参数同时提交或都缺;缺少上游要求的 browser_info / billing_details 字段;游标、时间格式、limit 不合法按 error.field 修正后重新提交,不要自动重扣否
sensitive_data_rejected示例:description 中疑似包含卡号等敏感信息400自由文本中疑似包含卡号等敏感信息description、metadata 中出现卡号样式的数字等删除敏感内容后重新提交;检查你的系统为何把卡数据写进这些字段否
unauthenticated示例:API Key 无效或已失效401API Key 缺失、无效、已撤销或已过期未带 Authorization;Key 复制不完整;轮换后旧 Key 已过期;Key 被撤销检查凭据,在 商户后台 API Key 页面 核对 Key 状态与环境否
forbidden示例:API Key 缺少该操作所需的权限范围403Key 的权限范围不允许该操作只读 Key 创建退款;getEvent 缺少事件所属业务的读权限;请求来源 IP 不在 Key 的白名单时返回更具体的 ip_not_allowed使用具备所需 scope 的 Key;不要绕过限制否
ip_not_allowed示例:请求来源 IP 不在该 API Key 的白名单内403请求来源 IP 不在该 API Key 的 IP 白名单内Key 配置了 IP 白名单,请求来自未登记的服务器 IP在商户后台 API Key 页面核对白名单,或从已登记的服务器发起请求否
capability_disabled示例:商户未开通直接传卡403商户未开通或已关闭对应能力未开通直接传卡;支付或退款申请能力被关闭;该币种未开放改用托管收银台或联系平台运营否
merchant_not_active示例:商户当前不受理新业务403商户不是已启用状态,不受理新业务商户被暂停或关闭;已受理业务的回调、查询、通知不受影响联系平台运营否
not_found示例:对象不存在或不属于当前商户与环境404对象不存在或不属于当前商户与环境ID 错误;用测试 Key 查询生产对象;查询平台异常退款(不通过开放 API 返回)核对 ID 与 Key 的环境;平台不会透露对象是否属于其他商户否
idempotency_conflict示例:相同的 Idempotency-Key 已用于内容不同的请求409同一幂等键被用于安全业务参数不同的请求复用了键但金额、币种、订单号等不同属于集成缺陷:检查幂等键生成逻辑;不要改参数后重放原键;新操作使用新键否
operation_in_progress示例:相同的请求正在处理中,请稍后用原幂等键重试409同一幂等键的请求仍在执行并发发送了同键请求稍后用同一个键重试,或查询 related_resource_id;不要换键是
payment_in_progress示例:该订单已有未完成的支付尝试,请查询原支付结果409该订单已有未完成的支付尝试在途(processing / requires_action)时再次 confirm,或用新键对同一订单重复提交查询 related_resource_id 指向的 Payment,按其状态继续(引导付款人完成认证或等待结果);不要新建订单否
order_no_conflict示例:商户订单号已对应一个 Payment409商户订单号已对应一个 Payment换幂等键但商户订单号相同查询 related_resource_id;确需再次收款且原支付已关闭时使用新订单号否
refund_no_conflict示例:商户退款号已被使用409商户退款号已被使用换幂等键重复提交同一退款号查询 related_resource_id 指向的退款否
invalid_state示例:支付当前状态不允许该操作409对象当前状态不允许该操作取消 processing / succeeded 的支付;对已过期、已取消、已成功的订单 confirm;为不可支付的订单创建收银会话查询对象最新状态后决定否
attempt_limit_reached示例:该订单已达到最多尝试次数409该订单已达到最多尝试次数默认最多 3 次,可被平台收紧不能再对该订单扣款;确认原支付关闭后用新订单号否
refund_amount_exceeds示例:退款金额超过可申请退款本金422退款金额超过可申请退款本金可申请退款本金 = 已确认扣款 − 已成功退款本金 − 未结束退款占用 − 争议独占本金;并发部分退款累计超额查询支付的 captured_amount、refunded_amount、refund_reserved_amount 后按可退金额重新申请否
insufficient_refund_funds示例:可用资金不足以覆盖退款本金与退款手续费,差额 USD 3.50422商户可用资金不足以覆盖退款本金与退款手续费资金尚未结算或已占用打款;例:支付 100.00、手续费 3.50、只有净额 96.50 时全额退款差 3.50message 给出差额,本次不生成退款单;线下补款并由平台财务登记后,或经平台批准动用你的保证金 / 冻结资金后(授权 7 天内有效),用同一商户退款号与金额、新的幂等键重新提交;平台不会自动缩减退款金额,也不替你垫付否
refund_not_allowed示例:该支付当前不能退款422该支付当前不能退款支付未成功;超过上游允许的退款期限;交易处于争议中且上游不允许退款按 message 处理;争议交易在商户后台争议页面处理否
currency_not_supported示例:币种未启用或不在可用范围422币种未启用或不在可用范围商户配置与上游能力的交集不含该币种改用已启用币种或联系平台运营否
amount_out_of_range示例:金额不在允许的范围内422金额不满足限额低于最低 / 高于最高单笔金额,或超过日累计限额;错误中不回传具体阈值调整金额或联系平台否
card_brand_not_supported示例:卡品牌不受支持422卡品牌不受支持商户配置与上游能力的交集不含该品牌提示付款人换卡否
fee_exceeds_amount示例:手续费超过交易金额422手续费超过交易金额金额过小,按费率计算的手续费超过金额提高金额或联系平台调整配置否
order_amount_mismatch示例:订单金额或币种与已有支付不一致422复用已有订单时金额或币种不一致创建收银会话时同一订单号的金额或币种变了金额不可修改:关闭原支付后用新订单号创建否
payment_method_unusable示例:支付凭据不能用于本次支付422支付凭据不能用于本次支付payment_method_id 不属于当前环境、上游账户或本商户重新获取支付凭据否
url_not_allowed示例:return_url 不匹配已登记的回跳地址规则422回跳地址不合法return_url / cancel_url 不匹配已登记的地址规则在 商户后台回跳地址页面 登记后重新提交否
risk_rejected示例:请求未通过风控检查422请求被平台风控拒绝命中风险名单等基础规则;不说明具体规则与阈值不要尝试绕过;有疑问联系平台否
rate_limited示例:请求过于频繁,请稍后用原幂等键重试429请求受限,本次请求未执行商户、Key 维度的频率限制;换 Key 不能绕过商户总限额等待 Retry-After 秒后用原幂等键重试是
internal_error示例:平台处理异常,请用原幂等键重试或按业务号查询500平台异常,请求可能已受理—用原幂等键重试或按商户订单号 / 退款号查询;不能换键重扣是
upstream_unavailable示例:依赖服务暂不可用,请求可能已受理502 / 503依赖异常,请求可能已受理上游或平台依赖故障,平台无法确认本次请求是否已生效同上是
temporarily_unavailable示例:服务暂时不可用,请稍后用原幂等键重试503暂时不可用,本次请求未受理平台暂停了该通道的新交易;响应不说明内部原因稍后用原幂等键重试;本次没有产生 Payment 或扣款;持续出现时联系平台运营是

按接口查看

所有接口都可能返回 unauthenticated(401)、forbidden(403)、rate_limited(429)、internal_error(500)、upstream_unavailable(502 / 503);有参数或请求体的接口另含 invalid_request(400)。temporarily_unavailable(503)只由发起新交易的 createPayment、confirmPayment、createCheckoutSession 返回。下表只列各接口特有的错误码:

接口特有错误码
createPayment创建支付invalid_request、sensitive_data_rejected、capability_disabled、merchant_not_active、idempotency_conflict、operation_in_progress、payment_in_progress、order_no_conflict、currency_not_supported、amount_out_of_range、card_brand_not_supported、fee_exceeds_amount、payment_method_unusable、url_not_allowed、risk_rejected、temporarily_unavailable
listPayments查询支付列表invalid_request
getPayment查询支付详情not_found
confirmPayment确认支付invalid_request、capability_disabled、merchant_not_active、not_found、idempotency_conflict、operation_in_progress、payment_in_progress、invalid_state、attempt_limit_reached、card_brand_not_supported、payment_method_unusable、url_not_allowed、risk_rejected、temporarily_unavailable
cancelPayment取消支付invalid_request、not_found、operation_in_progress、invalid_state
createCheckoutSession创建托管收银会话invalid_request、sensitive_data_rejected、capability_disabled、merchant_not_active、not_found、idempotency_conflict、operation_in_progress、invalid_state、attempt_limit_reached、order_amount_mismatch、currency_not_supported、amount_out_of_range、url_not_allowed、temporarily_unavailable
getCheckoutSession查询收银会话not_found
createRefund创建退款invalid_request、sensitive_data_rejected、capability_disabled、not_found、idempotency_conflict、operation_in_progress、refund_no_conflict、refund_amount_exceeds、insufficient_refund_funds、refund_not_allowed
listRefunds查询退款列表invalid_request
getRefund查询退款not_found
listBalances查询余额invalid_request
listBalanceTransactions查询账务流水invalid_request
listSettlements查询结算单列表invalid_request
getSettlement查询结算单详情not_found
getPayout查询打款结果not_found
getEvent查询事件not_found、forbidden

支付失败原因与 API 错误的区别

对比项API 错误(error.code)支付失败原因(failure.code)
出现在哪里4xx / 5xx 响应的 error 对象2xx 响应或 Webhook 中 Payment 的 failure 对象
含义本次请求被拒绝、冲突或结果未知请求已受理,某一次支付尝试被明确拒绝(如发卡行拒绝)
有没有 Payment通常没有新对象(409 时 related_resource_id 指向已有对象)有,status=failed,failure.attempt_id 指向失败的尝试
会不会有 Webhook不会会,payment.failed(含尝试号与当前 version)
怎么重试修正请求;429 / 5xx 用原幂等键付款人换卡,用新幂等键调用 confirm(有效期与尝试次数内)
能否展示给付款人一般不展示(集成或系统问题),可显示通用提示可展示脱敏的 failure.message
例子payment_in_progress、idempotency_conflict、insufficient_refund_fundscard_declined

failed 不是终态:收到 payment.failed 后付款人仍可能重试成功,所以按 version 处理事件。3DS 认证成功但发卡行拒绝扣款时,支付为 failed + failure,不是 API 错误。

失败原因码

支付失败原因

由上游结果映射;客户端须容忍未知的 failure.code,按“支付失败、可提示付款人换卡”处理。

failure.code类别含义付款人能否换卡重试
card_declined发卡行拒绝发卡行拒绝了交易(通用原因)能
expired_card发卡行拒绝卡已过期能
incorrect_card_data发卡行拒绝卡号、有效期或 CVC 有误能(核对后重新输入)
card_not_supported发卡行拒绝该卡类型不被接受能(换另一张卡)
authentication_failed认证失败3DS 认证未通过能
authentication_expired付款人放弃认证在有效期内未完成,且已确认未扣款能
risk_declined风控拒绝平台或上游风控拒绝,不说明具体规则视情况
processing_error技术错误上游处理失败,且已确认未扣款能
upstream_amount_mismatch技术错误上游实际扣款的金额或币种与订单不一致,平台已把实扣金额原路退还持卡人;这笔退还不出现在退款接口中,也不影响你的余额与可退金额视情况(订单仍在有效期与尝试次数内时可以)

付款人关闭认证页时平台不推断结果,此时不会立即出现失败原因;只有上游确认未扣款后才出现 authentication_expired 等原因。

退款失败原因

只在退款 failed、rejected 时出现:

failure.code含义
refund_rejected退款审核驳回
refund_failed上游明确未执行退款

处理示例

下面的函数把一次调用的结果归类为“下一步怎么做”。未知错误码走 default 分支,按 HTTP 状态处理。

Node.js
const API_BASE = 'https://api.payhub.example';

// 返回"下一步怎么做";未知错误码按 HTTP 状态处理
export async function callPayHub(method, path, { body, idempotencyKey } = {}) {
  let res;
  try {
    res = await fetch(`${API_BASE}${path}`, {
      method,
      headers: {
        Authorization: `Bearer ${process.env.PAYHUB_API_KEY}`,
        ...(body ? { 'Content-Type': 'application/json' } : {}),
        ...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {})
      },
      body: body ? JSON.stringify(body) : undefined,
      signal: AbortSignal.timeout(20_000)
    });
  } catch (cause) {
    return { next: 'unknown', cause }; // 超时或网络错误:结果未知,同键重试或按业务号查询
  }
  const data = await res.json().catch(() => null); // 5xx 可能不是 JSON
  const requestId = data?.request_id;
  if (res.ok) return { next: 'check_status', httpStatus: res.status, data, requestId };
  if (res.status === 429) {
    const raw = res.headers.get('Retry-After');
    const after = raw === null ? NaN : Number(raw);
    return { next: 'retry_same_key', afterSeconds: Number.isFinite(after) ? after : 1, requestId };
  }
  if (res.status >= 500) {
    // 503 temporarily_unavailable:本次请求未受理,稍后用原幂等键重试;其他 5xx 结果未知
    if (data?.error?.code === 'temporarily_unavailable')
      return { next: 'retry_same_key_later', requestId };
    return { next: 'unknown', requestId };
  }
  const error = data?.error ?? {};
  switch (error.code) {
    case 'payment_in_progress':
    case 'order_no_conflict':
    case 'refund_no_conflict':
      return { next: 'query_existing', id: error.related_resource_id, requestId };
    case 'operation_in_progress':
      return { next: 'retry_same_key', afterSeconds: 2, requestId };
    case 'idempotency_conflict':
      return { next: 'fix_integration', requestId }; // 同键不同参数:修复幂等键生成并告警
    default:
      return {
        next: 'rejected',
        httpStatus: res.status,
        code: error.code,
        field: error.field,
        message: error.message,
        requestId
      };
  }
}

联系技术支持

  • 先在商户后台按 request_id 自查调用记录(方法、路径、耗时、HTTP 状态、脱敏错误摘要):API 调用记录(在新标签页打开商户后台)(需要所有者、管理员或开发者)。
  • 联系时提供:request_id、发生时间(带时区)、环境(测试 / 生产)、接口、商户订单号或平台对象 ID。
  • 绝不要提供:API Key、Webhook 签名密钥、完整卡号、CVC、带令牌的认证或收银链接。
这页有帮助吗?