跳转到主要内容

状态概览

付款从创建到最终结算会经历多个状态。跟踪这些状态可帮助您确认交付、及早发现故障,并向用户提供准确的反馈。

状态流转

状态说明

状态说明建议提示语
CREATED付款已接收并持久化,等待进入处理队列。“付款已提交”
PENDING付款已排队,等待发送至支付服务商。“即将处理”
PROCESSING付款已提交至支付服务商,等待确认。“转账处理中”
COMPLETED支付服务商已确认资金成功交付给收款方。“付款已送达!“
ERROR发生内部系统错误,未进行任何付款。如持续出现请联系支持。“内部错误——请联系支持”
FAILED因收款方账户信息无效或不正确,付款被拒绝。“转账失败——请检查收款方信息”
REJECTED付款因合规审查被拦截,不会被处理。“转账被拒绝——合规问题”
REFUNDED已发送的资金已退回预存余额。“资金已退回余额”

获取付款状态

GET /api/v2/payouts/{id}
const getPayoutStatus = async (payoutId) => {
  const response = await fetch(
    `https://sandbox.killb.app/api/v2/payouts/${payoutId}`,
    {
      headers: { 'Authorization': `Bearer ${token}` }
    }
  );

  const payout = await response.json();
  return payout.status;
};

轮询状态

当 webhooks 不可用时,使用轮询作为备选方案:
const waitForPayout = async (payoutId, timeoutMs = 300000) => {
  const terminalStatuses = ['COMPLETED', 'ERROR', 'FAILED', 'REJECTED', 'REFUNDED'];
  const start = Date.now();

  while (Date.now() - start < timeoutMs) {
    const response = await fetch(
      `https://sandbox.killb.app/api/v2/payouts/${payoutId}`,
      { headers: { 'Authorization': `Bearer ${token}` } }
    );

    const payout = await response.json();
    console.log('状态:', payout.status);

    if (terminalStatuses.includes(payout.status)) {
      return payout;
    }

    // 等待 15 秒后再次检查
    await new Promise(resolve => setTimeout(resolve, 15000));
  }

  throw new Error(`付款 ${payoutId} 在超时时间内未完成结算`);
};

const result = await waitForPayout('payout-abc123');

if (result.status === 'COMPLETED') {
  console.log('资金已成功交付');
} else {
  console.error('付款以状态结束:', result.status);
}
优先使用 webhooks 作为主要通知方式。每 15–30 秒轮询一次适合作为备用方案。

通过 Webhooks 获取状态更新

订阅 PAYOUT 事件类型,在每次状态变更时实时收到通知:
// 订阅付款事件
await fetch('/api/v2/webhooks', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    url: 'https://your-app.com/webhooks/killb',
    secret: 'your-webhook-secret',
    events: ['PAYOUT'],
    active: true
  })
});
处理收到的付款 webhook 事件:
app.post('/webhooks/killb', (req, res) => {
  const { event, data } = req.body;

  switch (event) {
    case 'payout.pending':
      updatePayoutStatus(data.id, 'PENDING');
      break;

    case 'payout.processing':
      updatePayoutStatus(data.id, 'PROCESSING');
      break;

    case 'payout.completed':
      updatePayoutStatus(data.id, 'COMPLETED');
      notifyRecipient(data);
      break;

    case 'payout.error':
      // 内部系统错误——未进行任何付款
      updatePayoutStatus(data.id, 'ERROR');
      alertOpsTeam(data);
      break;

    case 'payout.failed':
      // 收款方账户信息无效或不正确
      updatePayoutStatus(data.id, 'FAILED');
      notifyInvalidBeneficiary(data);
      break;

    case 'payout.rejected':
      // 因合规审查被拦截
      updatePayoutStatus(data.id, 'REJECTED');
      escalateToCompliance(data);
      break;

    case 'payout.refunded':
      // 资金已退回预存余额
      updatePayoutStatus(data.id, 'REFUNDED');
      reconcileBalance(data);
      break;
  }

  res.status(200).json({ received: true });
});

最佳实践

始终为 PAYOUT 事件配置 webhook 端点。这可以即时获取状态更新,无需轮询开销,并减少不必要的 API 调用。
如果 webhook 交付失败,每 15–30 秒查询一次 GET /api/v2/payouts/{id}。设置合理的超时时间(例如 5 分钟),如果付款在 PROCESSING 状态停留时间超过预期,及时通知团队。
每种失败状态有不同的原因,对应的处理方式也不同:
  • ERROR — KillB 内部系统问题,未进行任何付款。无需修改收款方信息,若持续出现请联系支持。
  • FAILED — 因收款方账户信息无效或不正确,银行拒绝了转账。请检查并更正收款方信息后重试。
  • REJECTED — 付款因合规审查被拦截,不可自动重试,应在内部升级处理。
通过 GET /api/v2/payouts 获取前一天的所有付款,并与内部账本核对。比较预期与实际的终态,及早发现差异。
const { payouts } = await listPayouts({ limit: 100 });
const needsAttention = payouts.filter(p =>
  ['ERROR', 'FAILED', 'REJECTED'].includes(p.status)
);

if (needsAttention.length > 0) {
  console.warn(`${needsAttention.length} 笔付款需要处理`, needsAttention);
}

下一步

配置 Webhooks

配置并保护 webhook 通知

错误处理

优雅地处理 API 错误