状态概览
付款从创建到最终结算会经历多个状态。跟踪这些状态可帮助您确认交付、及早发现故障,并向用户提供准确的反馈。
状态流转
状态说明
| 状态 | 说明 | 建议提示语 |
|---|
CREATED | 付款已接收并持久化,等待进入处理队列。 | “付款已提交” |
PENDING | 付款已排队,等待发送至支付服务商。 | “即将处理” |
PROCESSING | 付款已提交至支付服务商,等待确认。 | “转账处理中” |
COMPLETED | 支付服务商已确认资金成功交付给收款方。 | “付款已送达!“ |
ERROR | 发生内部系统错误,未进行任何付款。如持续出现请联系支持。 | “内部错误——请联系支持” |
FAILED | 因收款方账户信息无效或不正确,付款被拒绝。 | “转账失败——请检查收款方信息” |
REJECTED | 付款因合规审查被拦截,不会被处理。 | “转账被拒绝——合规问题” |
REFUNDED | 已发送的资金已退回预存余额。 | “资金已退回余额” |
获取付款状态
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、FAILED 和 REJECTED
每种失败状态有不同的原因,对应的处理方式也不同:
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 通知