fix: canceling product from Stripe billing portal schedules free product
This commit is contained in:
39
package-lock.json
generated
39
package-lock.json
generated
@@ -13867,6 +13867,7 @@
|
||||
"@types/express": "^5.0.0",
|
||||
"@unkey/api": "^0.29.0",
|
||||
"ai": "^4.3.10",
|
||||
"autumn-js": "^0.0.12",
|
||||
"body-parser": "^1.20.3",
|
||||
"bullmq": "^5.31.1",
|
||||
"chai": "^5.1.2",
|
||||
@@ -14299,6 +14300,20 @@
|
||||
"npm": ">= 8.6.0"
|
||||
}
|
||||
},
|
||||
"server/node_modules/@supabase/ssr": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.6.1.tgz",
|
||||
"integrity": "sha512-QtQgEMvaDzr77Mk3vZ3jWg2/y+D8tExYF7vcJT+wQ8ysuvOeGGjYbZlvj5bHYsj/SpC0bihcisnwPrM4Gp5G4g==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cookie": "^1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@supabase/supabase-js": "^2.43.4"
|
||||
}
|
||||
},
|
||||
"server/node_modules/@types/body-parser": {
|
||||
"version": "1.19.5",
|
||||
"license": "MIT",
|
||||
@@ -14453,6 +14468,30 @@
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"server/node_modules/autumn-js": {
|
||||
"version": "0.0.12",
|
||||
"resolved": "https://registry.npmjs.org/autumn-js/-/autumn-js-0.0.12.tgz",
|
||||
"integrity": "sha512-NUjAlrJZb5QG4I+Vjuv64z0H7MsSYvdr5kNBDQ3YuW0p+Dh1XmxF75nQ51wQh56o/LQb4KrPfvrWefDZ3uIOBQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@clerk/nextjs": "^6.16.0",
|
||||
"@supabase/ssr": "^0.6.1",
|
||||
"better-auth": "^1.2.7",
|
||||
"react": "^18.2.0 || ^19.0.0",
|
||||
"react-dom": "^18.2.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@clerk/nextjs": {
|
||||
"optional": true
|
||||
},
|
||||
"@supabase/ssr": {
|
||||
"optional": true
|
||||
},
|
||||
"better-auth": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"server/node_modules/body-parser": {
|
||||
"version": "1.20.3",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
"@types/express": "^5.0.0",
|
||||
"@unkey/api": "^0.29.0",
|
||||
"ai": "^4.3.10",
|
||||
"autumn-js": "^0.0.12",
|
||||
"body-parser": "^1.20.3",
|
||||
"bullmq": "^5.31.1",
|
||||
"chai": "^5.1.2",
|
||||
|
||||
@@ -83,7 +83,10 @@ export const handleSubscriptionUpdated = async ({
|
||||
nullish(previousAttributes?.canceled_at) &&
|
||||
!nullish(subscription.canceled_at);
|
||||
|
||||
if (isCanceled && updatedCusProducts.length > 0) {
|
||||
let comment = subscription.cancellation_details?.comment;
|
||||
let isAutumnDowngrade = comment === "autumn_downgrade";
|
||||
|
||||
if (isCanceled && updatedCusProducts.length > 0 && !isAutumnDowngrade) {
|
||||
let allDefaultProducts = await ProductService.getFullDefaultProducts({
|
||||
sb,
|
||||
orgId: org.id,
|
||||
@@ -98,8 +101,11 @@ export const handleSubscriptionUpdated = async ({
|
||||
inStatuses: [CusProductStatus.Scheduled],
|
||||
});
|
||||
|
||||
// Default products to activate...
|
||||
let defaultProducts = allDefaultProducts.filter((p) =>
|
||||
cusProducts.some((cp: FullCusProduct) => cp.product.group == p.group)
|
||||
updatedCusProducts.some(
|
||||
(cp: FullCusProduct) => cp.product.group == p.group
|
||||
)
|
||||
);
|
||||
|
||||
let customer = updatedCusProducts[0].customer;
|
||||
@@ -114,7 +120,7 @@ export const handleSubscriptionUpdated = async ({
|
||||
|
||||
for (let product of defaultProducts) {
|
||||
let alreadyScheduled = cusProducts.some(
|
||||
(cp: FullCusProduct) => cp.product.id == product.id
|
||||
(cp: FullCusProduct) => cp.product.group == product.group
|
||||
);
|
||||
|
||||
if (alreadyScheduled) {
|
||||
|
||||
@@ -159,10 +159,16 @@ export const handleDowngrade = async ({
|
||||
if (differenceInDays(latestEndDate, curEndDate) > 10) {
|
||||
await stripeCli.subscriptions.update(sub.id, {
|
||||
cancel_at: latestPeriodEnd,
|
||||
cancellation_details: {
|
||||
comment: "autumn_downgrade",
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await stripeCli.subscriptions.update(sub.id, {
|
||||
cancel_at_period_end: true,
|
||||
cancellation_details: {
|
||||
comment: "autumn_downgrade",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -265,7 +271,7 @@ export const handleDowngrade = async ({
|
||||
},
|
||||
});
|
||||
|
||||
// 3. Update cus product
|
||||
// 4. Update cus product
|
||||
logger.info("3. Inserting new full cus product (starts at period end)");
|
||||
await createFullCusProduct({
|
||||
sb: req.sb,
|
||||
@@ -277,28 +283,6 @@ export const handleDowngrade = async ({
|
||||
isDowngrade: true,
|
||||
});
|
||||
|
||||
// 4. For scheduled products that are not in same interval, remove from schedule
|
||||
for (const scheduleObj of schedules) {
|
||||
const { schedule, interval } = scheduleObj;
|
||||
|
||||
let intervalsToRemove = [];
|
||||
if (!itemSets.some((itemSet) => itemSet.interval === interval)) {
|
||||
intervalsToRemove.push(interval);
|
||||
}
|
||||
|
||||
await cancelFutureProductSchedule({
|
||||
sb: req.sb,
|
||||
org: attachParams.org,
|
||||
stripeCli,
|
||||
cusProducts: attachParams.cusProducts!,
|
||||
product: product,
|
||||
includeOldItems: false,
|
||||
logger: req.logtail,
|
||||
inIntervals: intervalsToRemove,
|
||||
env: attachParams.customer.env,
|
||||
});
|
||||
}
|
||||
|
||||
if (attachParams.org.api_version! >= APIVersion.v1_1) {
|
||||
res.status(200).json(
|
||||
AttachResultSchema.parse({
|
||||
|
||||
@@ -255,13 +255,6 @@ export const handleNewProductItems = async ({
|
||||
`Ents: new(${newEnts.length}), updated(${updatedEnts.length}), deleted(${deletedEnts.length})`
|
||||
);
|
||||
|
||||
console.log("newEnts", newEnts);
|
||||
console.log("newPrices", newPrices);
|
||||
console.log("updatedEnts", updatedEnts);
|
||||
console.log("updatedPrices", updatedPrices);
|
||||
console.log("deletedEnts", deletedEnts);
|
||||
console.log("deletedPrices", deletedPrices);
|
||||
|
||||
if (newFeatures.length > 0) {
|
||||
await FeatureService.insert({
|
||||
sb,
|
||||
|
||||
@@ -5,11 +5,11 @@ MOCHA_CMD="npx mocha --parallel --timeout 10000000 --ignore tests/00_setup.ts"
|
||||
# TEST PARALLEL
|
||||
if [ "$1" == "basic-parallel" ]; then
|
||||
MOCHA_PARALLEL=true $MOCHA_SETUP && $MOCHA_CMD \
|
||||
tests/basic/*.ts \
|
||||
tests/basic/multi-feature/*.ts \
|
||||
tests/basic/entities/*.ts \
|
||||
# tests/basic/referrals/*.ts \
|
||||
# tests/attach/**/*.ts \
|
||||
tests/basic/referrals/*.ts \
|
||||
tests/attach/**/*.ts \
|
||||
# tests/basic/*.ts \
|
||||
# tests/basic/multi-feature/*.ts \
|
||||
# tests/basic/entities/*.ts \
|
||||
|
||||
|
||||
elif [ "$1" == "advanced-parallel" ]; then
|
||||
|
||||
@@ -49,12 +49,7 @@ describe(`${chalk.yellowBright(
|
||||
});
|
||||
|
||||
customer = await initCustomer({
|
||||
customer_data: {
|
||||
id: customerId,
|
||||
name: "Test Customer",
|
||||
email: "test@test.com",
|
||||
fingerprint: "fp1",
|
||||
},
|
||||
customerId,
|
||||
sb: this.sb,
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
@@ -103,9 +98,35 @@ describe(`${chalk.yellowBright(
|
||||
assert.isNotNull(resPro);
|
||||
});
|
||||
|
||||
// Advance time 1 month
|
||||
it("Advancing stripe clock and seeing if pro is attached", async function () {
|
||||
// Attach premium to see if scheduled product is removed
|
||||
it("checking if attach premium will remove scheduled product", async function () {
|
||||
this.timeout(30000);
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.premium.id,
|
||||
});
|
||||
|
||||
const res = await AutumnCli.getCustomer(customerId);
|
||||
const resPro = res.products.find(
|
||||
(p: any) =>
|
||||
p.id === products.pro.id && p.status === CusProductStatus.Scheduled
|
||||
);
|
||||
assert.isUndefined(resPro);
|
||||
|
||||
compareMainProduct({
|
||||
sent: products.premium,
|
||||
cusRes: res,
|
||||
});
|
||||
});
|
||||
|
||||
// Advance time 1 month
|
||||
it("Attaching pro, advancing stripe clock and seeing if pro is attached", async function () {
|
||||
this.timeout(30000);
|
||||
await AutumnCli.attach({
|
||||
customerId: customerId,
|
||||
productId: products.pro.id,
|
||||
});
|
||||
|
||||
const stripeCli = createStripeCli({
|
||||
org: this.org,
|
||||
env: this.env,
|
||||
|
||||
Reference in New Issue
Block a user