Files
cfw-autumn/server/src/internal/customers/attach/attachFunctions/upgradeDiffIntFlow/handleUpgradeDiffInt.ts
2025-06-09 17:04:15 +01:00

138 lines
3.7 KiB
TypeScript

import { ExtendedRequest } from "@/utils/models/Request.js";
import {
AttachParams,
AttachResultSchema,
} from "../../../cusProducts/AttachParams.js";
import { getStripeSubs } from "@/external/stripe/stripeSubUtils.js";
import { getExistingCusProducts } from "@/internal/customers/cusProducts/cusProductUtils/getExistingCusProducts.js";
import { CusProductService } from "@/internal/customers/cusProducts/CusProductService.js";
import {
APIVersion,
AttachConfig,
AttachScenario,
CusProductStatus,
ProcessorType,
SuccessCode,
} from "@autumn/shared";
import { createFullCusProduct } from "@/internal/customers/add-product/createFullCusProduct.js";
import { attachToInsertParams } from "@/internal/products/productUtils.js";
import { insertInvoiceFromAttach } from "@/internal/invoices/invoiceUtils.js";
import { updateSubsDiffInt } from "./updateSubsDiffInt.js";
export const handleUpgradeDiffInterval = async ({
req,
res,
attachParams,
config,
}: {
req: ExtendedRequest;
res?: any;
attachParams: AttachParams;
config: AttachConfig;
}) => {
const logger = req.logtail;
const { stripeCli, customer, products, cusProducts } = attachParams;
const { carryUsage, disableTrial } = config;
const product = products[0];
let { curMainProduct: curCusProduct } = getExistingCusProducts({
product,
cusProducts: cusProducts || [],
internalEntityId: attachParams.internalEntityId,
});
curCusProduct = curCusProduct!;
const stripeSubs = await getStripeSubs({
stripeCli,
subIds: curCusProduct.subscription_ids,
expand: ["items.data.price.tiers"],
});
logger.info("1. Updating current subscriptions in Stripe");
let { newSubs, invoice, newInvoiceIds } = await updateSubsDiffInt({
db: req.db,
curCusProduct,
stripeCli,
attachParams,
stripeSubs,
logger,
config,
});
logger.info("3. Expiring old cus product");
await CusProductService.update({
db: req.db,
cusProductId: curCusProduct.id,
updates: {
subscription_ids: curCusProduct.subscription_ids!.filter(
(subId) => subId !== newSubs[0].id,
),
processor: {
type: ProcessorType.Stripe,
subscription_id: null,
},
status: CusProductStatus.Expired,
},
});
// Insert new cus product
logger.info("4. Creating new cus product");
await createFullCusProduct({
db: req.db,
attachParams: attachToInsertParams(attachParams, products[0]),
subscriptionIds: newSubs.map((sub) => sub.id),
anchorToUnix:
newSubs.length > 0 ? newSubs[0].current_period_end * 1000 : undefined,
disableFreeTrial: disableTrial,
carryExistingUsages: carryUsage,
carryOverTrial: true,
scenario: AttachScenario.Upgrade,
logger,
});
// Insert invoices
logger.info("5. Inserting invoices");
const batchInsertInvoice = [];
for (const invoiceId of newInvoiceIds || []) {
batchInsertInvoice.push(
insertInvoiceFromAttach({
db: req.db,
attachParams,
invoiceId,
logger,
}),
);
}
await Promise.all(batchInsertInvoice);
let curProductName = curCusProduct.product.name;
if (res) {
let apiVersion = attachParams.apiVersion || APIVersion.v1;
if (apiVersion >= APIVersion.v1_1) {
res.status(200).json(
AttachResultSchema.parse({
customer_id: customer.id,
product_ids: products.map((p) => p.id),
code: SuccessCode.UpgradedToNewProduct,
message: `Successfully upgraded from ${curProductName} to ${product.name}`,
invoice: config.invoiceOnly ? invoice : undefined,
}),
);
} else {
res.status(200).json({
success: true,
message: `Successfully upgraded from ${curProductName} to ${product.name}`,
});
}
}
};