在设置cancel_at_period_end时,Stripe customer.subscription.deleted webhook 总是在延迟后发送

问题描述 投票:0回答:1

我有一个测试用例执行以下操作(开玩笑):

... (some setup code irrelevant to the issue)

await assertEndpointResponse("post", "cancel-subscription", HttpStatus.OK, inviterContext.axiosAuthHeaders);
await clock.advanceDays(40); 
await delay(CLOCK_ADVANCE_DELAY + ENDPOINT_DELAY);
await verifyGetSubscription(customer.id, SubscriptionType.DUO_MONTHLY, "canceled", inviterContext.axiosAuthHeaders)

...

基本上,订阅是在设置的早期使用测试时钟创建的,然后取消,然后我想验证它是否在我的数据库中取消。我相信代码的其余部分是正确的,因为它用于大量其他测试,并且出于同样的原因,我对时钟实现也充满信心。不过,下面提供了:

import Stripe from "stripe";

export default class StripeTestClock {
    private stripe: Stripe;
    private clockId?: string;
    private frozenTime: number=0;

    constructor(stripeInstance: Stripe) {
        this.stripe = stripeInstance;
    }

    /**
     * Creates a Stripe test clock starting at the given frozen time.
     * If no frozen time is provided, it defaults to the current Unix timestamp.
     * 
     * @param frozenTime - The initial time for the test clock (in seconds since Unix epoch).
     * @returns The ID of the created test clock.
     */
    async create(frozenTime: number = Math.floor(Date.now() / 1000)): Promise<string> {
        if (this.clockId) {
            console.warn("A test clock already exists. Reusing existing clock.");
            return this.clockId;
        }

        try {
            const clock = await this.stripe.testHelpers.testClocks.create({
                frozen_time: frozenTime,
            });
            this.clockId = clock.id;
            this.frozenTime = frozenTime;
            return this.clockId;
        } catch (error) {
            console.error("Failed to create Stripe test clock:", (error as Error).message);
            throw error;
        }
    }

    /**
     * Advances the test clock to a new frozen time.
     * 
     * @param advanceToTime - The new time to advance the test clock to (in seconds since Unix epoch).
     * @throws If the clock ID is not set or the Stripe API call fails.
     */
    async advance(advanceToTime: number): Promise<void> {
        if (!this.clockId) {
            throw new Error("Cannot advance test clock: Clock ID is not set.");
        }

        try {
            await this.stripe.testHelpers.testClocks.advance(this.clockId, {
                frozen_time: advanceToTime,
            });
            this.frozenTime = advanceToTime; // Update the frozen time
        } catch (error) {
            console.error("Failed to advance Stripe test clock:", (error as Error).message);
            throw error;
        }
    }

    /**
     * Advances the test clock by the specified number of days.
     * 
     * @param days - The number of days to advance the clock.
     * @throws If the clock ID is not set or the Stripe API call fails.
     */
    async advanceDays(days: number): Promise<void> {
        if (!this.clockId) {
            throw new Error("Cannot advance test clock: Clock ID or is not set.");
        }

        const advanceToTime = this.frozenTime + days * 24 * 60 * 60; // Advance by days in seconds
        await this.advance(advanceToTime);
    }

    /**
     * Deletes the current test clock to clean up resources.
     * 
     * @throws If the clock ID is not set or the Stripe API call fails.
     */
    async delete(): Promise<void> {
        if (!this.clockId) {
            console.warn("No test clock to delete. Skipping deletion.");
            return;
        }

        try {
            await this.stripe.testHelpers.testClocks.del(this.clockId);
            this.clockId = undefined;
            this.frozenTime = 0;
        } catch (error) {
            console.error("Failed to delete Stripe test clock:", (error as Error).message);
            throw error;
        }
    }

    /**
     * Retrieves the ID of the current test clock.
     * 
     * @returns The ID of the current test clock, or undefined if no clock is set.
     */
    getClockId(): string | undefined {
        return this.clockId;
    }

    /**
     * Retrieves the current frozen time of the test clock.
     * 
     * @returns The current frozen time of the test clock, or undefined if no clock is set.
     */
    getFrozenTime(): number {
        return this.frozenTime;
    }

    /**
     * Refreshes the current frozen time by fetching the latest test clock state.
     * 
     * @throws If the clock ID is not set or the Stripe API call fails.
     */
    async refreshFrozenTime(): Promise<void> {
        if (!this.clockId) {
            throw new Error("Cannot refresh frozen time: Clock ID is not set.");
        }

        try {
            const clock = await this.stripe.testHelpers.testClocks.retrieve(this.clockId);
            this.frozenTime = clock.frozen_time;
        } catch (error) {
            console.error("Failed to refresh frozen time:", (error as Error).message);
            throw error;
        }
    }
}

cancel-susbcription
端点仅执行以下操作:

await stripe.subscriptions.update(subscriptionId, {
     cancel_at_period_end: true
})

出于某种原因,我在

stripe.subscription.deleted
调用后大约 500 毫秒收到我的
delay(CLOCK_ADVANCE_DELAY + ENDPOINT_DELAY)
webhook,无论我在延迟中输入了什么。例如,
CLOCK_ADVANCE_DELAY + ENDPOINT_DELAY
是 10 秒,以下是 Stripe 中发送的 webhook:

enter image description here

但是当我将该行更新为

delay(CLOCK_ADVANCE_DELAY)
时,我明白了

enter image description here

(5 秒 + 一点差异)。而在另一个方向上,

delay(CLOCK_ADVANCE_DELAY + ENDPOINT_DELAY + 20000)
则有 30 秒的差异。为什么会出现这种情况?

jestjs stripe-payments
1个回答
0
投票

根据描述,看起来可能是由于

subscriptions.update
延迟调用造成的。你可以检查一下你的代码,看看在
delay()
之前是否还有另一个
clock.advanceDays(40)
被调用?

© www.soinside.com 2019 - 2024. All rights reserved.