概述
Dodo Payments API 使用标准 HTTP 状态代码和自定义错误代码来指示 API 请求的成功或失败。当发生错误时,API 会返回适当的 HTTP 状态代码和包含错误详细信息的 JSON 响应。 每个错误响应包括:- 指示错误一般类别的 HTTP 状态代码
- 识别错误确切性质的特定错误代码
- 解释出错原因的人类可读错误消息
- 适用时关于错误的附加详细信息
- 调试集成问题
- 在您的应用程序中实现适当的错误处理
- 向最终用户提供有意义的反馈
- 维护强大的支付处理系统
INSUFFICIENT_FUNDS 或 CARD_DECLINED),请参阅 交易失败 参考。
标准 API 错误代码
错误响应格式
当发生错误时,API 返回具有以下结构的 JSON 响应:错误代码参考
下面的错误代码按与 API 的相关领域分组。每个条目列出了触发条件和 API 返回的消息。认证与账户
-
UNAUTHORIZED- 触发条件: 无 API 密钥或无效令牌/范围
- 消息: 您无权限执行此操作
-
MERCHANT_NOT_LIVE- 触发条件: 商家仍处于测试模式
- 消息: Merchant 尚未上线
-
BUSINESS_ARCHIVED- Trigger: 对已归档企业发起任何面向客户的请求(checkout、payment link、storefront、Customer Portal 或 license key)
- Message: 此企业已归档,不再接受请求
Payments & Checkout
-
CHECKOUT_SESSION_CONSUMED- Trigger: Checkout session 已生成 payment
- Message: Checkout session 已被使用
-
NO_ELIGIBLE_PAYMENT_METHODS- Trigger: 过滤后没有剩余内容
- Message: 未找到符合条件的 payment methods
-
PAYMENT_NOT_SUCCEEDED- Trigger: 尝试对未成功的 payment 进行 refund/process
- Message: 提供的 payment 尚未成功
-
PREVIOUS_PAYMENT_PENDING- Trigger: 在上一个 payment 处于非终止状态时尝试创建 charge
- Message: 上一个 payment 尚未成功,无法创建新的 charge
-
UNSUCCESSFUL_PAYMENT_ID- Trigger: Payment ID 引用未成功的 payment
- Message: Payment ID 的状态未成功。
Connectors & BYOP
这些错误与商户自有的 payment connectors(Bring Your Own Processor)相关。-
BYOP_CONNECTOR_DISABLED- Trigger: 更新通过已禁用 BYOP connector 路由的 subscription 上的 payment method
- Message: 此 subscription 通过商户自有的(BYOP)connector 路由,而该 connector 当前已禁用
-
BYOP_CUSTOM_INVOICE_ADDRESS_MISSING- Trigger: 商户路由的(BYOP)payment 缺少必需的 custom invoice address
- Message: payment 通过商户的 connector 路由时,必须提供 BYOP custom invoice address
-
CONNECTOR_LABEL_ALREADY_EXISTS- Trigger: 创建 label 已存在的 connector
- Message: 此 label 的 connector 已存在。请选择其他 label。
Refunds
-
EXISTING_REFUND_REQUEST_PROCESSING- Trigger: 上一个 refund request 仍在处理中
- Message: 状态为 “Pending” 的 refund request 仍在处理中
-
LINE_ITEM_FULLY_REFUNDED- Trigger: 尝试对已完全退款的 line item 进行 refund
- Message: Line item 已完全退款,无法继续退款。
-
LINE_ITEM_NOT_FOUND- Trigger: Item ID 不属于所引用的 payment
- Message: 在 payment 中未找到 line item
-
LINE_ITEM_PRORATED- Trigger: 尝试对按比例计算的 line 进行 refund 或 update
- Message: Line item 无法退款,因为它是按比例计算的
-
LINE_ITEM_REFUND_AMOUNT_TOO_HIGH- Trigger: Refund amount > paid amount(含 tax)
- Message: Line item 请求的含 tax refund amount 为 ,高于 paid amount
-
LINE_ITEM_REFUND_AMOUNT_TOO_LOW- Trigger: Refund amount 低于 minimum threshold
- Message: Line item 请求的 refund amount 为 ,金额过低
-
NOTHING_TO_REFUND- Trigger: 没有剩余可退款金额;所有正数 line items 均已完全退款
- Message: 没有剩余可退款金额。所有正数 line items 均已完全退款。
-
PARTIAL_REFUND_NOT_ALLOWED- Trigger: 尝试对仅支持全额退款的 payment method 进行 partial refund
- Message: 此 payment method 不允许 partial refunds
-
PAYMENT_ALREADY_REFUNDED- Trigger: 重复 refund
- Message: 此 payment 已退款
-
PAYMENT_HAS_BEEN_REFUNDED- Trigger: Payment 已完全退款
- Message: Payment ID 已完全退款。
-
REFUND_AMOUNT_EXCEEDS_PAID_AMOUNT- Trigger: Aggregate refund amount > paid amount
- Message: 计算出的 refund amount 大于 paid amount
-
REFUND_WINDOW_EXPIRED- Trigger: 超出允许的 refund window
- Message: Payment 创建 天后无法发起 refunds。请联系 support@dodopayments.com。
-
ZERO_AMOUNT_PAYMENT_REFUND_NOT_ALLOWED- Trigger: 尝试对零金额 payment 进行 refund
- Message: 无法对 currency amount 为零的 payment 进行退款
Subscriptions & Add-ons
-
ADDONS_IN_USAGE_BASED_BILLING_NOT_SUPPORTED- Trigger: 尝试向 usage-based billing subscriptions 添加 addons
- Message: Usage Based Billing 不支持 Subscriptions 中的 addons
-
ADDONS_NOT_ALLOWED_FOR_ON_DEMAND- Trigger: 尝试向 on-demand subscriptions 添加 addons
- Message: on demand subscriptions 不允许使用 addons
-
CANCEL_SCHEDULED_PLAN_CHANGE_FOR_CUSTOMER_PORTAL_DISABLED- Trigger: Customer Portal 尝试取消 scheduled plan change,但企业已禁用此操作
- Message: customer portal 已禁用取消 scheduled plan change。
-
CHARGE_NOT_ALLOWED_FOR_SCHEDULED_CANCELLATION- Trigger: 尝试对计划取消的 subscription 收取费用
- Message: Subscription 已计划取消
-
CUSTOMER_HAS_EXISTING_SUBSCRIPTION- Trigger: 为已有 subscription 的 customer 创建 subscription,且不允许每位 customer 拥有多个 subscriptions
- Message: Customer 已有 subscription。若要允许每位 customer 拥有多个 subscriptions,请更改 business settings
-
DO_NOT_BILL_NOT_ALLOWED_IN_CUSTOMER_PORTAL- Trigger: Customer Portal plan change 使用
do_not_billproration mode - Message: customer portal 不允许使用 do_not_bill proration mode
- Trigger: Customer Portal plan change 使用
-
DUPLICATE_ADDON_IDS_IN_REQUEST- Trigger: 同一个
addon_id在 request 中出现多次 - Message: 不允许重复的 addon IDs
- Trigger: 同一个
-
INACTIVE_SUBSCRIPTION_PLAN_CHANGE_NOT_SUPPORTED- Trigger: 对 inactive subscription 执行 plan change
- Message: 不支持对 inactive subscriptions 更改 plans
-
INVALID_PRORATION_MODE_WITH_NEXT_BILLING_DATE- Trigger: 将不同于
full_immediately的 proration mode 与effective_at: next_billing_date一起使用 - Message: effective_at: next_billing_date 仅允许使用 full_immediately proration mode
- Trigger: 将不同于
-
MISSING_ADDON_IDS- Trigger:
addon_idlist 为空或包含未知 IDs - Message: 一个或多个 product IDs 不存在:
- Trigger:
-
ON_DEMAND_PLAN_CHANGE_NOT_SUPPORTED- Trigger: on-demand 不允许 plan swap
- Message: 不支持对 on demand subscriptions 更改 plans
-
ON_DEMAND_USAGE_BASED_BILLING_NOT_SUPPORTED- Trigger: 尝试将 on-demand 用于 usage-based billing
- Message: Usage Based Billing 不支持 On Demand Subscriptions
-
ONE_TIME_PRODUCTS_NOT_ALLOWED_FOR_ON_DEMAND- Trigger: 向 on-demand subscription 添加 one-time product
- Message: on demand subscriptions 不允许使用 one-time products
-
PENDING_PLAN_CHANGE_EXISTS- Trigger: 上一个 plan change 仍在等待 payment 时请求新的 plan change
- Message: 此 subscription 已存在 pending plan change。请等待当前 payment 完成。
-
PLAN_CHANGE_FOR_CUSTOMER_PORTAL_DISABLED- Trigger: 通过 Customer Portal 执行 plan change,但企业已禁用此功能
- Message: customer portal 的 Subscription plan change 已禁用。
-
PLAN_CHANGE_NOT_ALLOWED_FOR_SCHEDULED_CANCELLATION- Trigger: 尝试对计划取消的 subscription 执行 plan change
- Message: Subscription 已计划取消
-
SCHEDULE_PLAN_CHANGE_FOR_CUSTOMER_PORTAL_DISABLED- Trigger: 通过 Customer Portal 安排 plan change,但企业已禁用此功能
- Message: 此企业已禁用安排 plan changes。
-
SCHEDULED_PLAN_CHANGE_EXISTS- Trigger: 已存在 scheduled plan change 时创建新的 scheduled plan change
- Message: 此 subscription 已存在 scheduled plan change。创建新的变更前,请先取消现有的 scheduled change。
-
SCHEDULED_PLAN_CHANGE_NOT_FOUND- Trigger: 引用或取消不存在的 scheduled plan change
- Message: 未找到此 subscription 的 scheduled plan change。
-
SUBSCRIPTION_EXPIRED- Trigger: Billing past
expires_at - Message: Subscription 已过期,无法创建新的 charges
- Trigger: Billing past
-
SUBSCRIPTION_INACTIVE- Trigger: Status ≠
active - Message: Subscription 未激活
- Trigger: Status ≠
-
SUBSCRIPTION_NOT_ON_DEMAND- Trigger: 预期为 on-demand,但得到的是 fixed interval
- Message: Subscription 已不是 on demand
-
SUBSCRIPTION_PAYMENT_RETRY_LIMIT_EXCEEDED- Trigger: Subscription payment retries 超过最大尝试次数
- Message: 此 subscription 已超过 10 次的最大 retry limit
Products, Cart & Brands
-
BRAND_ALREADY_ARCHIVED- Trigger: 归档已经归档的 brand
- Message: Brand 已归档
-
BRAND_ARCHIVED- Trigger: 更新已归档的 brand、提交其进行 verification,或向其标记新的 product、product collection 或 subscription
- Message: Brand 已归档,无法更新
-
BRAND_ARCHIVE_TARGET_REQUIRED- Trigger: 归档仍包含 products、live subscriptions 或 product collections 且没有
move_products_totarget 的 brand - Message: Brand 有 12 个 product(s)。请将 move_products_to 设置为 target brand,以便重新标记它们。
- Trigger: 归档仍包含 products、live subscriptions 或 product collections 且没有
-
BRAND_MISMATCH- Trigger: Cart items 属于不同的 brands
- Message: Product cart 中的所有 items 应属于同一 brand
-
BRAND_NOT_ENABLED- Trigger: Brand 已禁用或未激活
- Message: 提供的 brand 未启用
-
BRAND_SUBMISSION_NOT_ENABLED- Trigger: Brand verification resubmission 功能未启用
- Message: Brand verificatin resubmission 未启用
-
CANNOT_ARCHIVE_PRIMARY_BRAND- Trigger: 归档 primary brand,其 brand ID 即 business ID
- Message: Primary brand 无法归档
-
FILE_IN_USE- Trigger: 删除仍被 active entitlement grants 引用的 digital product file
- Message: Digital file 仍被 active grants 引用
-
INVALID_BRAND_ARCHIVE_TARGET- Trigger:
move_products_to指定正在归档的 brand、已归档的 brand 或其他 business 的 brand - Message: move_products_to 必须是此 business 中未归档的 brand
- Trigger:
-
INVALID_SUGGESTED_PRICE- Trigger: PWYW price < minimum allowed price
- Message: Suggested Price 不能低于 minimum price。对于 pay what you want,price 被视为 minimum accepted amount
-
LOCALIZED_PRICE_ALREADY_EXISTS- Trigger: 此 product 和 country/currency 已存在 localized price
- Message: 此 product 和 country/currency 已存在 localized price
-
LOCALIZED_PRICE_DUPLICATES_BASE- Trigger: Localized price 与 product 的 base currency/country 重复
- Message: Localized price 与 product 的 base currency/country 重复
-
LOCALIZED_PRICE_SHAPE_MISMATCH- Trigger: Localized price shape 与 product 的
pricing_mode不匹配 - Message: Localized price shape 与 product 的 pricing_mode 不匹配
- Trigger: Localized price shape 与 product 的
-
MISSING_PRODUCT_INFORMATION- Trigger: Product 存在但缺少必需信息
- Message: Product 存在,但其他必需信息缺失或无效
-
PAY_AS_YOU_WANT_AMOUNT_REQUIRED- Trigger: PWYW product 缺少 price
- Message: pay as you want product 必须提供 Amount
-
PRODUCT_CART_EMTPY- Trigger: 提交空的 product cart
- Message: product_cart 为空(错误代码特意拼写为
EMTPY,以匹配 API 返回的确切值)
-
PRODUCT_COLLECTION_IS_DELETED- Trigger: 操作已删除的 product collection
- Message: 无消息
-
PRODUCT_COLLECTION_MUST_HAVE_PRODUCTS- Trigger: 从 collection 中移除最后一个 product(或最后一个包含 products 的 group)
- Message: 无法删除 collection 中的最后一个 product。请改为归档 collection。
-
PRODUCT_IS_DELETED- Trigger: Product 已软删除
- Message: 无消息
-
PRODUCT_PRICING_MODE_REQUIRED- Trigger: 在 product 的
pricing_mode设置前添加 localized prices - Message: 添加 localized prices 前必须设置 product pricing_mode
- Trigger: 在 product 的
-
SLUG_ALREADY_TAKEN- Trigger: 请求的 product slug / short URL 已被使用
- Message: Slug 已被占用
-
UNABLE_TO_EDIT_PRIMARY_BRAND- Trigger: 尝试通过常规 API 更新 primary brand
- Message: 无法通过此 API endpoint 更新 primary brand。
Discounts
-
DISCOUNT_ALREADY_USED_ON_SUBSCRIPTION- Trigger: 重新应用已在此 subscription 上使用过的 discount
- Message: 此 discount 已在此 subscription 上使用过
-
DISCOUNT_CODE_ALREADY_EXISTS- Trigger: 创建重复的 discount code
- Message: Discount Code 已存在
-
DISCOUNT_CODE_EXPIRED- Trigger: Discount code 已超过其
expires_atdate - Message: Discount code 已过期
- Trigger: Discount code 已超过其
-
DISCOUNT_CODE_USAGE_LIMIT_EXCEEDED- Trigger: 达到
usage_limit后重复使用 discount - Message: Usage limit 不能小于 times_used / Discount code 已达到 usage limit
- Note: 终止状态——该 code 已耗尽。请勿重试。
- Trigger: 达到
-
DISCOUNT_CONCURRENT_REDEMPTION- Trigger: 同一 code 的另一次 redemption 持有 usage-limit row lock 的时间过长
- Message: Discount 正在并发兑换;请重试
- Note: 临时错误。该 code 可能仍有可用额度,因此可以安全重试。不要向 customer 显示“code 已耗尽”。
-
DISCOUNT_CURRENCY_OPTION_INVALID- Trigger: create 或 update 时使用无效的
currency_options - Message: Flat discount 至少需要一个具有可解析 default 的 currency option / 不允许重复的 currency options / 最多只能将一个 currency option 标记为 default
- Trigger: create 或 update 时使用无效的
-
DISCOUNT_CUSTOMER_NOT_ELIGIBLE- Trigger: Customer 不满足 code 的
customer_eligibility(first_time、existing,或不在specificcode 的 allow list 中) - Message: Customer 不符合此 discount code 的条件
- Trigger: Customer 不满足 code 的
-
DISCOUNT_MINIMUM_SUBTOTAL_NOT_MET- Trigger: Cart subtotal 低于为 checkout currency 配置的
minimum_subtotal - Message: Cart subtotal 低于 discount 要求的 minimum required subtotal
- Trigger: Cart subtotal 低于为 checkout currency 配置的
-
DISCOUNT_NOT_YET_ACTIVE- Trigger: 在
starts_atdate 前使用 code - Message: Discount code 尚未激活(starts_at 位于未来)
- Trigger: 在
-
DISCOUNT_PER_CUSTOMER_USAGE_LIMIT_EXCEEDED- Trigger: Customer 已兑换 code
per_customer_usage_limit次 - Message: 此 discount code 已超过每位 customer 的 usage limit
- Trigger: Customer 已兑换 code
-
DISCOUNT_NOT_APPLICABLE_TO_NEW_PRODUCT- Trigger: 将 plan change 到现有 discount 不适用的 product
- Message: Discount 不适用于新 plan 的 product
-
DISCOUNT_NOT_AVAILABLE_FOR_ON_DEMAND- Trigger: 将 code 应用于 on-demand subscription
- Message: Discount coupon 不适用于 on demand subscriptions
-
DISCOUNT_NOT_AVAILABLE_FOR_PRODUCT- Trigger: 将 code 应用于无关的 product(s)
- Message: Discount coupon 不适用于此 product
-
INVALID_DISCOUNT_CODE- Trigger: Code 不存在或不适用
- Message: Invalid Discount Code / Discount Code 无法应用于 cart 中的任何 product
-
INVALID_PERCENTAGE- Trigger: Percent amount > 100%(或 10,000 basis points)
- Message: Percentage amount 不能超过 10000 / Discount code amount 不能超过 100%
-
UNSUPPORTED_DISCOUNT_TYPE- Trigger: 使用不受支持的 discount type。
percentage和flat均受支持;不支持 per-unit amount discounts。 - Message: 仅支持 percentage 和 flat discount codes
- Trigger: 使用不受支持的 discount type。
License Keys
-
ACTIVATION_LIMIT_LESS_THAN_CURRENT_AMOUNT- Trigger: License-key activations:new limit < existing instance count
- Message: New activation limit 不能小于 current instances count
-
INACTIVE_LICENSE_KEY- Trigger: Key status ≠
active - Message: License key 未激活
- Trigger: Key status ≠
-
LICENSE_KEY_LIMIT_REACHED- Trigger: Activations = limit
- Message: 已达到 license key activation limit
-
LICENSE_KEY_NOT_FOUND- Trigger: Instance ID 或 key ID 无效
- Message: 未找到 license key instance,或其不属于此 license key
-
NO_EXPIRY_ON_SUBSCRIPTION_LICENSE_KEYS- Trigger: 尝试为 sub-based key 设置 expiry
- Message: 无法为 subscription-based license key 设置 expiry date
Usage-Based Billing & Meters
-
DUPLICATE_METER_IDS_IN_REQUEST- Trigger: 同一 meter ID 在 request 中出现多次
- Message: 不允许重复的 Meter Ids
-
INVALID_QUANTITY- Trigger: 为 usage-based pricing 指定无效 quantity
- Message: Usage based price products 仅允许 1 个 quantity
-
METER_IS_DELETED- Trigger: 尝试使用已删除的 meter
- Message: Meter 已被删除
-
MISSING_METER_IDS- Trigger: Meter ID list 为空或包含无效 IDs
- Message: 一个或多个 meter IDs 不存在:
Credit-Based Billing
-
CREDIT_ENTITLEMENT_IS_DELETED- Trigger: 操作已删除的 credit entitlement
- Message: Credit entitlement 已被删除
-
CREDIT_ENTITLEMENT_NAME_ALREADY_EXISTS- Trigger: 创建 name 已存在的 credit entitlement
- Message: 此 name 的 credit entitlement 已存在
-
OVERAGE_LIMIT_EXCEEDED- Trigger: usage 或 credit deduction 将超过配置的 overage limit
- Message: 已超过 overage limit
Wallet
-
INSUFFICIENT_WALLET_FUNDS- Trigger: Wallet balance < debit amount
- Message: Wallet 中的资金不足
-
NEGATIVE_BALANCE_ADJUSTMENT- Trigger: 尝试使 wallet balance 变为负数
- Message: 不允许将 wallet balance 变为负数
Currency, Tax & Region
-
EXCHANGE_RATE_NOT_FOUND- Trigger:
from → tocurrency pair 没有 FX rate - Message: 未找到将 Currency 转换为 Currency 的 exchange rate
- Trigger:
-
INVALID_TAX_ID- Trigger: VAT/GST/TIN validation 失败
- Message: Tax Id 无效
-
REQUEST_AMOUNT_BELOW_MINIMUM- Trigger: Amount < product minimum
- Message: Amount 不能低于 product 指定的 minimum amount
-
TOTAL_PAYMENT_AMOUNT_BELOW_MINIMUM_AMOUNT- Trigger: Combined cart total < gateway minimum
- Message: 处理 payment 至少需要
-
UNSUPPORTED_BILLING_CURRENCY- Trigger: 此 subscription 不支持请求的 billing currency
- Message: Subscriptions 不支持非 USD billing currency
-
UNSUPPORTED_COUNTRY- Trigger: Geo 尚不受支持
- Message: 当前不支持 Country
-
UNSUPPORTED_CURRENCY- Trigger: Product 或 addon currency 不是 Dodo Payments 可收取费用的 currency。Base prices 可使用任何可收取费用的 currency 设置,因此这通常意味着 currency code 无效或尚不受支持。
- Message: Currency 当前不受支持 / 当前仅支持 USD 和 INR products / Addon price 当前仅支持 USD 和 INR / billing_currency 只能请求 USD 或 INR / Currency Not Supported / Unexpected currency for Indian card subscriptions
-
UNSUPPORTED_TAX_CATEGORY- Trigger: Tax category string 不在 enum 中
- Message: 当前不支持 Category
Validation & Requests
-
DUPLICATE_LINE_ITEMS_IN_REQUEST- Trigger: 同一个
item_id在items[]中出现两次 - Message: items array 中指定了重复的 item_ids
- Trigger: 同一个
-
INVALID_QUERY_PARAMS- Trigger: 互斥或格式错误的 query parameters
- Message: Query params 只能包含 time_frame 或 (start, end) 其中之一
-
INVALID_REQUEST_BODY- Trigger: JSON 格式错误或违反 schema
- Message: Request body 无效。请检查 request headers 和 object。
-
INVALID_REQUEST_PARAMETERS- Trigger: Semantics 错误(例如 date 已在过去)
- Message: 无法将 next_billing_date 更改为过去的时间
-
MAXIMUM_KEYS_REACHED- Trigger: Metadata / custom-fields 超过 50 个 pairs
- Message: 超过 50 个 key-value pairs
General & System
-
INTEGER_CONVERSION_FAILURE- Trigger: server-side 任何 integer ↔ string/decimal conversion 失败
- Message: Integer Conversion Failure
-
INTERNAL_SERVER_ERROR- Trigger: 未捕获的 exceptions;应在 server-side 记录详细信息
- Message: 无 public message(generic 500)
-
NOT_FOUND- Trigger: 任何缺失 resource 的通用 404
- Message: 未找到 Item (或更具体的消息)
-
TOO_MANY_REQUESTS- Trigger: 429 rate-limit
- Message: 无消息
-
UNSUPPORTED_ACTION- Trigger: Resource type 不支持的 action
- Message: 不支持对 usage based subscriptions 更改 plans
Best Practices
- 始终在 application 中优雅地处理 errors
- 实现适当的 error logging
- 为最终用户使用合适的 error messages
- 为 transient errors 实现 retry logic
- 对于未解决的问题,请联系 support