跳转到主要内容

状态概览

付款从创建到最终结算会经历多个阶段。每个阶段有三个状态——待处理、处理中和已完成——让您在整个流程的每个步骤都能获得精细的可见性。

状态流转

在**收款(Cash In)**阶段,FAILEDERROR 是终态——未收集到任何资金,因此不会发起退款。对于所有后续阶段(KYT 出账、出款),FAILEDERROR 是过渡状态:付款将转入退款流程(REFUND_PENDINGREFUND_PROCESSINGREFUNDED)。REJECTED 也会触发退款流程,因为它仅在收款阶段之后发生。

状态说明

状态阶段说明建议提示语
CREATED初始付款已接收并持久化,等待进入处理队列”付款已提交”
CASH_IN_PENDING收款入账资金收集已入队”正在处理付款”
CASH_IN_PROCESSING收款正在从预存余额收集资金”正在收集资金”
CASH_IN_COMPLETED收款资金已成功收集”资金已收集”
KYT_OUT_PENDINGKYT 出账出账交易已入队等待合规检查”出账检查待处理”
KYT_OUT_PROCESSINGKYT 出账出账合规审查进行中”出账检查进行中”
KYT_OUT_COMPLETEDKYT 出账出账合规检查通过”准备转账”
CASH_OUT_PENDING出款向收款方的付款已入队”转账已入队”
CASH_OUT_PROCESSING出款资金已提交至支付服务商”转账处理中”
CASH_OUT_COMPLETED出款支付服务商已确认交付”转账已交付”
COMPLETED终态付款已完全结算”付款已送达!“
REVIEW_NEEDED审查交易已标记为需要人工合规审查”审查中——我们会通知您”
FAILED错误状态操作失败。收款阶段发生时为终态(未收集资金);后续阶段发生时转入退款流程。“转账失败——请检查收款方信息”
REJECTED错误状态付款因合规审查被拦截。始终触发退款流程,因为它仅在收款阶段之后发生。“转账被拒绝——合规问题”
ERROR错误状态内部系统错误。收款阶段发生时为终态(未收集资金);后续阶段发生时转入退款流程。“内部错误——请联系支持”
REFUND_PENDING退款退回预存余额的退款已入队”退款已发起”
REFUND_PROCESSING退款退款进行中”退款进行中”
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', 'FAILED', 'REJECTED', 'ERROR', '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.cash_in_pending':
    case 'payout.cash_in_processing':
    case 'payout.cash_in_completed':
    case 'payout.kyt_out_pending':
    case 'payout.kyt_out_processing':
    case 'payout.kyt_out_completed':
    case 'payout.cash_out_pending':
    case 'payout.cash_out_processing':
    case 'payout.cash_out_completed':
    case 'payout.refund_pending':
    case 'payout.refund_processing': {
      const status = event.replace('payout.', '').toUpperCase();
      updatePayoutStatus(data.id, status);
      break;
    }

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

    case 'payout.review_needed':
      // 交易已标记为人工审查——无需操作,付款将在审查完成后自动恢复
      updatePayoutStatus(data.id, 'REVIEW_NEEDED');
      alertComplianceTeam(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 分钟),如果付款在任何处理状态停留时间超过预期,及时通知团队。
每种失败状态都有不同的原因。FAILEDERROR 发生的阶段决定是否发起退款:
  • 收款阶段的 FAILED / ERROR — 终态。未收集到任何资金,不会发起退款。ERROR 若持续出现请联系支持。
  • 收款阶段之后的 FAILED / ERROR(KYT 出账、出款)— 过渡状态。由于资金已收集,付款自动转入 REFUND_PENDINGREFUND_PROCESSINGREFUNDED
  • REJECTED — 因合规审查被拦截。不可自动重试,应在内部升级处理。由于资金已收集,始终会触发退款流程。
  • REVIEW_NEEDED — KYT 检查标记了该交易需要人工审查。审查完成后付款将自动恢复或转为 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 错误