feat(round-10): 新增出海潜客多时区最佳联系窗口算法与Stripe回调幂等性防重处理 (79套件957测试全绿)

这个提交包含在:
wikiboss
2026-09-21 01:59:11 +08:00
父节点 9f8e309a4a
当前提交 f46b24a177
共有 6 个文件被更改,包括 643 次插入 和 1 次删除

查看文件

@@ -436,4 +436,63 @@ describe('Point 21: Stripe 国际信用卡订阅支付与 Webhook 自动化入
expect(res.message).toContain('签名验证失败');
});
});
// =========================================================================
// 5. Webhook 幂等去重与异常事件状态机 (Idempotency & Failure Grace Period)
// =========================================================================
describe('5. Webhook 幂等去重与异常事件状态机', () => {
it('5.1 同一 event.id 重复推送时触发幂等拦截,防止重复入账', async () => {
const event: StripeWebhookEvent = {
id: 'evt_unique_1001',
type: 'checkout.session.completed',
created: 1700000000,
data: {
object: {
client_reference_id: 88,
metadata: { tenantId: '88', plan: 'pro' },
},
},
};
const firstRes = await stripeService.handleWebhookEvent(event);
expect(firstRes.action).toBe('upgraded');
// 重复推送相同事件
const secondRes = await stripeService.handleWebhookEvent(event);
expect(secondRes.success).toBe(true);
expect(secondRes.action).toBe('idempotent_ignored');
});
it('5.2 收到 invoice.payment_failed 扣款失败时流转为 past_due 宽限期', async () => {
// 先建立活跃订阅
subscriptions.push({
id: 99,
tenantId: 66,
planType: 'pro',
billingCycle: 'monthly',
status: 'active',
stripeSubscriptionId: 'sub_pastdue_test',
currentPeriodEnd: new Date().toISOString(),
} as TeamSubscriptionEntity);
const failEvent: StripeWebhookEvent = {
id: 'evt_fail_999',
type: 'invoice.payment_failed',
created: 1700000000,
data: {
object: {
subscription: 'sub_pastdue_test',
metadata: { tenantId: '66' },
},
},
};
const res = await stripeService.handleWebhookEvent(failEvent);
expect(res.status).toBe('past_due');
expect(res.action).toBe('payment_failed');
const updated = subscriptions.find(s => s.tenantId === 66);
expect(updated?.status).toBe('past_due');
});
});
});