跳转到主要内容
当付款失败时,Dodo Payments 会通过标准化的 error_code 和人类可读的 error_message 告诉您失败原因。此指南展示如何读取这些字段、决定是否值得重试,并在不向客户暴露敏感信息的情况下恢复付款。

Dodo Payments 如何报告失败

每个失败的支付——无论是一次性结账还是订阅续订——都会在支付对象上携带相同的失败字段:
字段类型描述
statusstring用于失败支付的 failed。其他非成功状态包括 cancelledrequires_customer_actionrequires_payment_method
error_codestring | null标准化的失败原因,例如 INSUFFICIENT_FUNDSPROCESSING_ERROR。请参阅 Transaction Failures 参考获取完整列表。
error_messagestring | null人类可读的失败解释。
retry_attemptinteger原始收费的 01 或更高标识计划的订阅续订重试。
error_codeerror_message 在付款实际失败之前是 null。始终先检查 status,然后读取错误字段。

payment.failed Webhook

检测失败的最可靠方法是 payment.failed webhook。事件在 data 中封装完整的支付对象:
payment.failed payload
{
  "business_id": "bus_P3SXLcppjXgagmHS",
  "type": "payment.failed",
  "timestamp": "2025-08-04T05:36:41.609359Z",
  "data": {
    "payload_type": "Payment",
    "payment_id": "pay_2IjeQm4hqU6RA4Z4kwDee",
    "status": "failed",
    "error_code": "PROCESSING_ERROR",
    "error_message": "An error occurred while processing your card. Try again in a little bit.",
    "retry_attempt": 0,
    "subscription_id": null,
    "currency": "USD",
    "total_amount": 400,
    "payment_method": "card",
    "card_last_four": "0119",
    "card_network": "VISA",
    "payment_link": "https://test.checkout.dodopayments.com/cbq",
    "customer": {
      "customer_id": "cus_8VbC6JDZzPEqfB",
      "email": "test@acme.com",
      "name": "Test user"
    }
  }
}
一个最小的处理器读取 error_code 并基于它进行路由:
import { Webhook } from "standardwebhooks";
import express from "express";

const app = express();
// Mount the raw body parser so the exact payload is available for verification
app.use(express.raw({ type: "application/json" }));

const webhook = new Webhook(process.env.DODO_PAYMENTS_WEBHOOK_KEY);

app.post("/webhooks/dodo", async (req, res) => {
  // Verify the signature against the raw body before trusting the payload
  const payload = req.body.toString();
  await webhook.verify(payload, req.headers);

  const event = JSON.parse(payload);

  if (event.type === "payment.failed") {
    const payment = event.data;

    console.log(
      `Payment ${payment.payment_id} failed: ${payment.error_code} (${payment.error_message})`
    );

    if (payment.subscription_id) {
      // Subscription renewal — Dodo retries soft declines for you
      await flagSubscriptionPaymentIssue(payment.subscription_id, payment.error_code);
    } else {
      // One-time payment — prompt the customer to try again
      await notifyCustomerOfFailedPayment(payment.customer.customer_id, payment.error_code);
    }
  }

  res.json({ received: true });
});
始终在处理之前验证 webhook 签名。请参阅 Webhooks guide 获取完整设置,包括签名验证和幂等性。

决定是否重试:软拒绝 vs. 硬拒绝

error_code 告诉您重试相同的支付方式是否值得。
拒绝类型意味着什么该怎么办
软拒绝临时或可纠正的(例如 INSUFFICIENT_FUNDSPROCESSING_ERRORNETWORK_ERRORTRY_AGAIN_LATER)。重试——延迟后,或客户修复输入后——可以成功。
硬拒绝终端(例如 STOLEN_CARDLOST_CARDDO_NOT_HONORFRAUDULENT)。不要重试同一张卡。请客户提供不同的支付方式。
Transaction Failures 参考列出了每个 error_code 的拒绝类型和建议操作。

处理结账与续订失败

您的恢复方式取决于客户是否在场。
客户正在积极结账。显示清晰的消息并让他们立即重试或使用其他卡。
  • requires_payment_method — 客户未提供支付方式:他们未输入卡片详情,或受到提示未采取行动。这通常是结账放弃,而不是拒绝——重新接触客户完成付款(参见 Abandoned Cart Recovery)。
  • requires_customer_action — 需要额外验证(如 3DS);让客户完成验证。参见 3D Secure handling

重试失败的付款

  • 订阅: 启用 Subscription Payment Retries 来恢复软拒绝,且无需集成工作。您还可以通过让客户通过 Update Payment Method API 更新他们的支付方式来触发恢复,该方法将收取所有未缴款项。
  • 一次性付款: 重新发送结账或 payment_link,以便客户可以使用不同的方法重试。对于一次性付款,没有自动重试。
不要对同一张卡进行硬拒绝重试。卡片网络可能会将重复拒绝标记为滥用行为,这会影响您的授权率。

安全地向客户展示错误

向客户显示友好的消息——永远不要显示原始 error_code
Customer-facing messaging
const CUSTOMER_MESSAGES = {
  INSUFFICIENT_FUNDS: "Your card has insufficient funds. Please use another card.",
  EXPIRED_CARD: "Your card has expired. Please use a card with a valid expiry date.",
  INCORRECT_CVC: "The security code (CVC) is incorrect. Please re-enter it.",
};

function customerMessage(errorCode) {
  // Sensitive declines must never reveal the real reason
  const SENSITIVE = ["STOLEN_CARD", "LOST_CARD", "PICKUP_CARD", "FRAUDULENT"];
  if (SENSITIVE.includes(errorCode)) {
    return "Your card was declined. Please contact your bank or use another card.";
  }
  return CUSTOMER_MESSAGES[errorCode] ?? "Your payment could not be processed. Please try another card.";
}
切勿透露 STOLEN_CARDLOST_CARDPICKUP_CARDFRAUDULENT 的真实原因。 公开这些可能会给骗子提供线索。显示通用的拒绝消息,并只在内部记录特定的 error_code

相关

Transaction Failures

每个拒绝代码、其类型和建议的操作。

Error Codes

不是卡片拒绝的 API 和业务逻辑错误。

Subscription Payment Retries

订阅续订时软拒绝的自动恢复。

Subscription Dunning

恢复硬拒绝的电子邮件序列。

Payment Webhooks

支付事件的完整负载模式。

Testing Failures

模拟拒绝和续订失败的测试卡。
最后修改于 2026年6月18日