fix: going into past due
This commit is contained in:
@@ -117,6 +117,7 @@ export const handleSubscriptionUpdated = async ({
|
||||
}
|
||||
|
||||
// Cancel subscription immediately
|
||||
|
||||
if (subscription.status === "past_due" && org.config.cancel_on_past_due) {
|
||||
const stripeCli = createStripeCli({
|
||||
org,
|
||||
@@ -130,14 +131,11 @@ export const handleSubscriptionUpdated = async ({
|
||||
logger.info(
|
||||
`Latest invoice billing reason: ${latestInvoice.billing_reason}`,
|
||||
);
|
||||
logger.info(`Latest invoice status: ${latestInvoice.status}`);
|
||||
|
||||
if (
|
||||
latestInvoice.status !== "open" ||
|
||||
latestInvoice.billing_reason !== "subscription_cycle"
|
||||
) {
|
||||
const validInvoiceReasons = ["subscription_cycle", "subscription_create"];
|
||||
if (!validInvoiceReasons.includes(latestInvoice.billing_reason ?? "")) {
|
||||
logger.info(
|
||||
"sub.updated, latest invoice isn't open or billing reason isn't subscription_update, past_due not forcing cancel",
|
||||
"sub.updated, latest invoice billing reason isn't subscription_cycle / subscription_create, past_due not forcing cancel",
|
||||
{
|
||||
data: {
|
||||
subscriptionId: subscription.id,
|
||||
@@ -165,7 +163,9 @@ export const handleSubscriptionUpdated = async ({
|
||||
},
|
||||
);
|
||||
await stripeCli.subscriptions.cancel(subscription.id);
|
||||
if (latestInvoice.status === "open") {
|
||||
await stripeCli.invoices.voidInvoice(subscription.latest_invoice);
|
||||
}
|
||||
} catch (error: any) {
|
||||
logger.error(
|
||||
`subscription.updated: error cancelling / voiding: ${error.message}`,
|
||||
@@ -183,48 +183,3 @@ export const handleSubscriptionUpdated = async ({
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// server-1 | subscription.updated, previous attributes: { status: 'active' }
|
||||
// server-1 | current period start: 8 Jul 2025
|
||||
// server-1 | current period end: 8 Aug 2025
|
||||
// server-1 | subscription.updated: past due, cancelling: sub_1Rig399mx3u0jkgOquNmw5fM
|
||||
|
||||
// server-1 | subscription.updated, previous attributes: { status: 'active' }
|
||||
// server-1 | current period start: 8 Aug 2025
|
||||
// server-1 | current period end: 8 Sep 2025
|
||||
// server-1 | subscription.updated: past due, cancelling: sub_1Rig3z9mx3u0jkgOzJTci91r
|
||||
|
||||
// const lockKey = `sub_updated_${subscription.id}`;
|
||||
// // Create a lock to prevent race conditions
|
||||
// let lockAcquired = false;
|
||||
// try {
|
||||
// let attempts = 0;
|
||||
|
||||
// while (!lockAcquired && attempts < 3) {
|
||||
// lockAcquired = await getWebhookLock({ lockKey, logger });
|
||||
// if (!lockAcquired) {
|
||||
// attempts++;
|
||||
// console.log(
|
||||
// `sub.updated: failed to acquire lock for ${subscription.id}, attempt ${attempts}`,
|
||||
// );
|
||||
// if (attempts < 3) {
|
||||
// await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
// }
|
||||
// } else {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// } catch (error) {
|
||||
// logger.error("lock error, setting lockAcquired to true");
|
||||
// lockAcquired = true;
|
||||
// }
|
||||
|
||||
// if (!lockAcquired) {
|
||||
// throw new RecaseError({
|
||||
// message: `Failed to acquire lock for stripe webhook, sub.updated.`,
|
||||
// code: ErrCode.InvalidRequest,
|
||||
// statusCode: 400,
|
||||
// });
|
||||
// }
|
||||
|
||||
// await releaseWebhookLock({ lockKey, logger });
|
||||
|
||||
@@ -26,7 +26,6 @@ export const createAndFinalizeInvoice = async ({
|
||||
customer: stripeCusId,
|
||||
auto_advance: false,
|
||||
subscription: stripeSubId,
|
||||
|
||||
collection_method: chargeAutomatically
|
||||
? "charge_automatically"
|
||||
: "send_invoice",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { beforeAll, describe, test } from "bun:test";
|
||||
import { FreeTrialDuration, LegacyVersion } from "@autumn/shared";
|
||||
import { LegacyVersion } from "@autumn/shared";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import chalk from "chalk";
|
||||
@@ -8,6 +8,7 @@ import { constructFeatureItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||
import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js";
|
||||
import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js";
|
||||
import { advanceToNextInvoice } from "../utils/testAttachUtils/testAttachUtils.js";
|
||||
|
||||
// UNCOMMENT FROM HERE
|
||||
const pro = constructProduct({
|
||||
@@ -16,12 +17,6 @@ const pro = constructProduct({
|
||||
items: [
|
||||
constructFeatureItem({ featureId: TestFeature.Words, includedUsage: 100 }),
|
||||
],
|
||||
freeTrial: {
|
||||
length: 7,
|
||||
duration: FreeTrialDuration.Day,
|
||||
unique_fingerprint: false,
|
||||
card_required: true,
|
||||
},
|
||||
});
|
||||
|
||||
describe(`${chalk.yellowBright("temp: Testing add ons")}`, () => {
|
||||
@@ -34,7 +29,7 @@ describe(`${chalk.yellowBright("temp: Testing add ons")}`, () => {
|
||||
ctx,
|
||||
customerId,
|
||||
customerData: {},
|
||||
attachPm: "success",
|
||||
attachPm: "fail",
|
||||
withTestClock: true,
|
||||
});
|
||||
|
||||
@@ -51,6 +46,13 @@ describe(`${chalk.yellowBright("temp: Testing add ons")}`, () => {
|
||||
await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: pro.id,
|
||||
invoice: true,
|
||||
enable_product_immediately: true,
|
||||
});
|
||||
|
||||
await advanceToNextInvoice({
|
||||
stripeCli: ctx.stripeCli,
|
||||
testClockId,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
101
server/tests/_temp/temp2.test.ts
Normal file
101
server/tests/_temp/temp2.test.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { beforeAll, describe, test } from "bun:test";
|
||||
import { LegacyVersion } from "@autumn/shared";
|
||||
import { TestFeature } from "@tests/setup/v2Features.js";
|
||||
import ctx from "@tests/utils/testInitUtils/createTestContext.js";
|
||||
import chalk from "chalk";
|
||||
import { AutumnInt } from "@/external/autumn/autumnCli.js";
|
||||
import { constructArrearProratedItem } from "@/utils/scriptUtils/constructItem.js";
|
||||
import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js";
|
||||
import { initCustomerV3 } from "@/utils/scriptUtils/testUtils/initCustomerV3.js";
|
||||
import { initProductsV0 } from "@/utils/scriptUtils/testUtils/initProductsV0.js";
|
||||
import { attachFailedPaymentMethod } from "../../src/external/stripe/stripeCusUtils.js";
|
||||
import { CusService } from "../../src/internal/customers/CusService.js";
|
||||
|
||||
// UNCOMMENT FROM HERE
|
||||
const pro = constructProduct({
|
||||
type: "pro",
|
||||
|
||||
items: [
|
||||
constructArrearProratedItem({
|
||||
featureId: TestFeature.Users,
|
||||
pricePerUnit: 10,
|
||||
includedUsage: 0,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
describe(`${chalk.yellowBright("temp: Testing pay per use")}`, () => {
|
||||
const customerId = "temp2";
|
||||
const autumn: AutumnInt = new AutumnInt({ version: LegacyVersion.v1_4 });
|
||||
|
||||
let testClockId: string;
|
||||
beforeAll(async () => {
|
||||
const result = await initCustomerV3({
|
||||
ctx,
|
||||
customerId,
|
||||
customerData: {},
|
||||
attachPm: "success",
|
||||
withTestClock: true,
|
||||
});
|
||||
|
||||
await initProductsV0({
|
||||
ctx,
|
||||
products: [pro],
|
||||
prefix: customerId,
|
||||
});
|
||||
|
||||
testClockId = result.testClockId!;
|
||||
});
|
||||
|
||||
test("should attach pro product", async () => {
|
||||
await autumn.attach({
|
||||
customer_id: customerId,
|
||||
product_id: pro.id,
|
||||
});
|
||||
|
||||
// await advanceToNextInvoice({
|
||||
// stripeCli: ctx.stripeCli,
|
||||
// testClockId,
|
||||
// });
|
||||
|
||||
const customer = await CusService.get({
|
||||
db: ctx.db,
|
||||
idOrInternalId: customerId,
|
||||
orgId: ctx.org.id,
|
||||
env: ctx.env,
|
||||
});
|
||||
|
||||
await attachFailedPaymentMethod({
|
||||
stripeCli: ctx.stripeCli,
|
||||
customer: customer!,
|
||||
});
|
||||
|
||||
// await autumn.track({
|
||||
// customer_id: customerId,
|
||||
// feature_id: TestFeature.Users,
|
||||
// value: 1,
|
||||
// });
|
||||
|
||||
await autumn.entities.create(customerId, [
|
||||
{
|
||||
id: "1",
|
||||
name: "Entity 1",
|
||||
feature_id: TestFeature.Users,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
// test("should cancel one add on", async () => {
|
||||
// await autumn.cancel({
|
||||
// customer_id: customerId,
|
||||
// product_id: addOn.id,
|
||||
// });
|
||||
|
||||
// await expectSubToBeCorrect({
|
||||
// customerId,
|
||||
// db,
|
||||
// org,
|
||||
// env,
|
||||
// });
|
||||
// });
|
||||
});
|
||||
Reference in New Issue
Block a user