chore: ran biome format
This commit is contained in:
@@ -9,7 +9,7 @@ export default function CustomerDetailsExample() {
|
||||
|
||||
const getEntitlement = (featureId: string) => {
|
||||
return customer?.features.find(
|
||||
(entitlement: any) => entitlement.feature_id === featureId
|
||||
(entitlement: any) => entitlement.feature_id === featureId,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "react"
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
||||
return (
|
||||
@@ -11,11 +11,11 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
||||
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
className
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { Input }
|
||||
export { Input };
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client"
|
||||
"use client";
|
||||
|
||||
import { useTheme } from "next-themes"
|
||||
import { Toaster as Sonner, ToasterProps } from "sonner"
|
||||
import { useTheme } from "next-themes";
|
||||
import { Toaster as Sonner, ToasterProps } from "sonner";
|
||||
|
||||
const Toaster = ({ ...props }: ToasterProps) => {
|
||||
const { theme = "system" } = useTheme()
|
||||
const { theme = "system" } = useTheme();
|
||||
|
||||
return (
|
||||
<Sonner
|
||||
@@ -19,7 +19,7 @@ const Toaster = ({ ...props }: ToasterProps) => {
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export { Toaster }
|
||||
export { Toaster };
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import * as React from "react"
|
||||
import * as React from "react";
|
||||
|
||||
const MOBILE_BREAKPOINT = 768
|
||||
const MOBILE_BREAKPOINT = 768;
|
||||
|
||||
export function useIsMobile() {
|
||||
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
|
||||
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
|
||||
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
||||
const onChange = () => {
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
||||
}
|
||||
mql.addEventListener("change", onChange)
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
||||
return () => mql.removeEventListener("change", onChange)
|
||||
}, [])
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
||||
};
|
||||
mql.addEventListener("change", onChange);
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
||||
return () => mql.removeEventListener("change", onChange);
|
||||
}, []);
|
||||
|
||||
return !!isMobile
|
||||
return !!isMobile;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import { clsx, type ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ const getSingleCustomer = async ({
|
||||
// });
|
||||
|
||||
let scheduleIds = customers[0].customer_products.flatMap(
|
||||
(cp) => cp.scheduled_ids || []
|
||||
(cp) => cp.scheduled_ids || [],
|
||||
);
|
||||
|
||||
scheduleIds = Array.from(new Set(scheduleIds));
|
||||
@@ -121,7 +121,7 @@ const checkCustomerCorrect = async ({
|
||||
}
|
||||
|
||||
fullCus.entities = entities.filter(
|
||||
(entity) => entity.internal_customer_id === fullCus.internal_id
|
||||
(entity) => entity.internal_customer_id === fullCus.internal_id,
|
||||
);
|
||||
|
||||
// console.log(`Checking ${fullCus.email} (${fullCus.id})`);
|
||||
@@ -148,12 +148,12 @@ const checkCustomerCorrect = async ({
|
||||
cp.status !== CusProductStatus.Scheduled &&
|
||||
(cusProduct.internal_entity_id
|
||||
? cusProduct.internal_entity_id == cp.internal_entity_id
|
||||
: true)
|
||||
: true),
|
||||
);
|
||||
|
||||
assert(
|
||||
mainCusProd,
|
||||
`Found scheduled cus product with no main product (${cusProduct.product.name})`
|
||||
`Found scheduled cus product with no main product (${cusProduct.product.name})`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -168,22 +168,22 @@ const checkCustomerCorrect = async ({
|
||||
cp.id !== cusProduct.id &&
|
||||
!cp.product.is_add_on &&
|
||||
cp.status !== CusProductStatus.Scheduled &&
|
||||
cp.internal_entity_id == cusProduct.internal_entity_id
|
||||
cp.internal_entity_id == cusProduct.internal_entity_id,
|
||||
);
|
||||
|
||||
assert(
|
||||
!otherCusProd,
|
||||
`found two cus products from the same group: ${otherCusProd?.product.name} and ${cusProduct.product.name}`
|
||||
`found two cus products from the same group: ${otherCusProd?.product.name} and ${cusProduct.product.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
let stripeSubs = subs.filter((sub: any) =>
|
||||
cusProduct.subscription_ids!.some((id: string) => id === sub.id)
|
||||
cusProduct.subscription_ids!.some((id: string) => id === sub.id),
|
||||
);
|
||||
|
||||
assert(
|
||||
stripeSubs.length === cusProduct.subscription_ids!.length,
|
||||
"number of stripe subs should be the same as number of subscription ids"
|
||||
"number of stripe subs should be the same as number of subscription ids",
|
||||
);
|
||||
|
||||
// let subItems = stripeSubs.flatMap((sub: any) => sub.items.data);
|
||||
@@ -203,7 +203,7 @@ const checkCustomerCorrect = async ({
|
||||
|
||||
if (cusEnt.usage_allowed && !cusPrice) {
|
||||
assert.fail(
|
||||
`Feature ${cusEnt.feature_id} has usage allowed but no related cus price`
|
||||
`Feature ${cusEnt.feature_id} has usage allowed but no related cus price`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -331,7 +331,7 @@ export const check = async () => {
|
||||
schedules: stripeSchedules,
|
||||
org,
|
||||
entities,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -372,7 +372,7 @@ export const check = async () => {
|
||||
}
|
||||
|
||||
console.log(
|
||||
`COMPLETED ERROR CHECK FOR ${new Date().toISOString().slice(0, 16)}`
|
||||
`COMPLETED ERROR CHECK FOR ${new Date().toISOString().slice(0, 16)}`,
|
||||
);
|
||||
|
||||
if (process.env.NODE_ENV == "production") {
|
||||
|
||||
@@ -47,7 +47,7 @@ export const check = async () => {
|
||||
const checkCustomers = ["9bafd636-0c52-46b3-8ecd-1708d6faa373"];
|
||||
|
||||
fullCustomers = fullCustomers.filter((customer) =>
|
||||
checkCustomers.includes(customer.id || "")
|
||||
checkCustomers.includes(customer.id || ""),
|
||||
);
|
||||
|
||||
for (const customer of fullCustomers) {
|
||||
|
||||
@@ -20,7 +20,7 @@ const { db, client } = initDrizzle();
|
||||
export const cronTask = async () => {
|
||||
console.log(
|
||||
"\n----------------------------------\nRUNNING RESET CRON:",
|
||||
format(new UTCDate(), "yyyy-MM-dd HH:mm:ss")
|
||||
format(new UTCDate(), "yyyy-MM-dd HH:mm:ss"),
|
||||
);
|
||||
|
||||
try {
|
||||
@@ -41,7 +41,7 @@ export const cronTask = async () => {
|
||||
db,
|
||||
cusEnt: cusEnt,
|
||||
cacheEnabledOrgs,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export const cronTask = async () => {
|
||||
|
||||
console.log(
|
||||
"FINISHED RESET CRON:",
|
||||
format(new UTCDate(), "yyyy-MM-dd HH:mm:ss")
|
||||
format(new UTCDate(), "yyyy-MM-dd HH:mm:ss"),
|
||||
);
|
||||
console.log("----------------------------------\n");
|
||||
} catch (error) {
|
||||
@@ -75,7 +75,7 @@ const job = new CronJob(
|
||||
},
|
||||
null, // onComplete
|
||||
true, // start immediately
|
||||
"UTC" // timezone (adjust as needed)
|
||||
"UTC", // timezone (adjust as needed)
|
||||
);
|
||||
|
||||
cronTask();
|
||||
|
||||
@@ -74,11 +74,11 @@ const checkSubAnchor = async ({
|
||||
console.log("Checking billing cycle anchor");
|
||||
console.log(
|
||||
"Next reset at ",
|
||||
format(new UTCDate(nextResetAt), "dd MMM yyyy HH:mm:ss")
|
||||
format(new UTCDate(nextResetAt), "dd MMM yyyy HH:mm:ss"),
|
||||
);
|
||||
console.log(
|
||||
"Billing cycle anchor",
|
||||
format(new UTCDate(billingCycleAnchor), "dd MMM yyyy HH:mm:ss")
|
||||
format(new UTCDate(billingCycleAnchor), "dd MMM yyyy HH:mm:ss"),
|
||||
);
|
||||
|
||||
const billingCycleDay = getDate(new UTCDate(billingCycleAnchor));
|
||||
@@ -134,7 +134,7 @@ const handleShortDurationCusEnt = async ({
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Reseting short cus ent (${cusEnt.feature_id}) [${ent.interval}], customer: ${cusEnt.customer_id}, org: ${cusEnt.customer.org_id}`
|
||||
`Reseting short cus ent (${cusEnt.feature_id}) [${ent.interval}], customer: ${cusEnt.customer_id}, org: ${cusEnt.customer.org_id}`,
|
||||
);
|
||||
|
||||
let org = await OrgService.get({
|
||||
@@ -190,7 +190,7 @@ export const resetCustomerEntitlement = async ({
|
||||
|
||||
const entOptions = getEntOptions(
|
||||
cusEnt.customer_product.options,
|
||||
cusEnt.entitlement
|
||||
cusEnt.entitlement,
|
||||
);
|
||||
|
||||
// Handle if entitlement changed to unlimited...
|
||||
@@ -207,10 +207,10 @@ export const resetCustomerEntitlement = async ({
|
||||
|
||||
console.log(
|
||||
`Reset ${cusEnt.id} | customer: ${chalk.yellow(
|
||||
cusEnt.customer_id
|
||||
cusEnt.customer_id,
|
||||
)} | feature: ${chalk.yellow(
|
||||
cusEnt.feature_id
|
||||
)} | new balance: unlimited`
|
||||
cusEnt.feature_id,
|
||||
)} | new balance: unlimited`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -226,10 +226,10 @@ export const resetCustomerEntitlement = async ({
|
||||
|
||||
console.log(
|
||||
`Reset ${cusEnt.id} | customer: ${chalk.yellow(
|
||||
cusEnt.customer_id
|
||||
cusEnt.customer_id,
|
||||
)} | feature: ${chalk.yellow(
|
||||
cusEnt.feature_id
|
||||
)} | reset to lifetime (next_reset_at: null)`
|
||||
cusEnt.feature_id,
|
||||
)} | reset to lifetime (next_reset_at: null)`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -290,14 +290,14 @@ export const resetCustomerEntitlement = async ({
|
||||
|
||||
console.log(
|
||||
`Reset ${cusEnt.id} | customer: ${chalk.yellow(
|
||||
cusEnt.customer_id
|
||||
cusEnt.customer_id,
|
||||
)} | feature: ${chalk.yellow(
|
||||
cusEnt.feature_id
|
||||
cusEnt.feature_id,
|
||||
)} | new balance: ${chalk.green(
|
||||
resetBalance
|
||||
resetBalance,
|
||||
)} | new next_reset_at: ${chalk.green(
|
||||
format(new UTCDate(nextResetAt), "dd MMM yyyy HH:mm:ss")
|
||||
)}`
|
||||
format(new UTCDate(nextResetAt), "dd MMM yyyy HH:mm:ss"),
|
||||
)}`,
|
||||
);
|
||||
|
||||
// let cacheOrg = cacheEnabledOrgs.find(
|
||||
@@ -324,7 +324,7 @@ export const resetCustomerEntitlement = async ({
|
||||
// }
|
||||
} catch (error: any) {
|
||||
console.log(
|
||||
`Failed to reset ${cusEnt.id} | ${cusEnt.customer_id} | ${cusEnt.feature_id}, error: ${error}`
|
||||
`Failed to reset ${cusEnt.id} | ${cusEnt.customer_id} | ${cusEnt.feature_id}, error: ${error}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -143,7 +143,7 @@ export const initLogger = () => {
|
||||
},
|
||||
},
|
||||
// Use multistream to send logs to multiple destinations
|
||||
pino.multistream(streams)
|
||||
pino.multistream(streams),
|
||||
);
|
||||
|
||||
return logger;
|
||||
|
||||
@@ -62,7 +62,7 @@ autumnWebhookRouter.post(
|
||||
switch (type) {
|
||||
case WebhookEventType.CustomerProductsUpdated:
|
||||
console.log(
|
||||
`Type: ${type}, Scenario: ${data?.scenario}, Product: ${data?.updated_product?.id}`
|
||||
`Type: ${type}, Scenario: ${data?.scenario}, Product: ${data?.updated_product?.id}`,
|
||||
);
|
||||
break;
|
||||
case WebhookEventType.CustomerThresholdReached:
|
||||
@@ -82,5 +82,5 @@ autumnWebhookRouter.post(
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
22
server/src/external/caching/CacheManager.ts
vendored
22
server/src/external/caching/CacheManager.ts
vendored
@@ -22,7 +22,7 @@ export class CacheManager {
|
||||
}
|
||||
|
||||
this.client = new Redis(redisUrl, {
|
||||
retryStrategy: (times) => {
|
||||
retryStrategy: () => {
|
||||
return 5000;
|
||||
},
|
||||
});
|
||||
@@ -59,7 +59,7 @@ export class CacheManager {
|
||||
}
|
||||
|
||||
public static async getJson(key: string) {
|
||||
let client = await CacheManager.getClient();
|
||||
const client = await CacheManager.getClient();
|
||||
|
||||
if (!client) {
|
||||
throw new Error("Cache client not initialized");
|
||||
@@ -70,7 +70,7 @@ export class CacheManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
let res = await client.get(key);
|
||||
const res = await client.get(key);
|
||||
|
||||
if (!res) {
|
||||
return null;
|
||||
@@ -79,8 +79,12 @@ export class CacheManager {
|
||||
return JSON.parse(res);
|
||||
}
|
||||
|
||||
public static async setJson(key: string, value: any, ttl: number | string = 3600) {
|
||||
let client = await CacheManager.getClient();
|
||||
public static async setJson(
|
||||
key: string,
|
||||
value: any,
|
||||
ttl: number | string = 3600,
|
||||
) {
|
||||
const client = await CacheManager.getClient();
|
||||
if (!client) {
|
||||
throw new Error("Cache client not initialized");
|
||||
}
|
||||
@@ -90,9 +94,9 @@ export class CacheManager {
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof ttl === 'number') {
|
||||
if (typeof ttl === "number") {
|
||||
await client.set(key, JSON.stringify(value), "EX", ttl);
|
||||
} else if(typeof ttl === 'string' && ttl.toLowerCase() === 'forever') {
|
||||
} else if (typeof ttl === "string" && ttl.toLowerCase() === "forever") {
|
||||
await client.set(key, JSON.stringify(value));
|
||||
}
|
||||
}
|
||||
@@ -104,7 +108,7 @@ export class CacheManager {
|
||||
action: string;
|
||||
value: string;
|
||||
}) {
|
||||
let client = await CacheManager.getClient();
|
||||
const client = await CacheManager.getClient();
|
||||
if (!client) {
|
||||
throw new Error("Cache client not initialized");
|
||||
}
|
||||
@@ -118,7 +122,7 @@ export class CacheManager {
|
||||
}
|
||||
|
||||
static async disconnect() {
|
||||
let client = await CacheManager.getClient();
|
||||
const client = await CacheManager.getClient();
|
||||
if (!client) {
|
||||
throw new Error("Cache client not initialized");
|
||||
}
|
||||
|
||||
2
server/src/external/clerkUtils.ts
vendored
2
server/src/external/clerkUtils.ts
vendored
@@ -91,7 +91,7 @@ export const getStripeKey = async (orgId: string, env: AppEnv) => {
|
||||
export const createOrgAndAssignUser = async (
|
||||
name: string,
|
||||
slug: string,
|
||||
userId: string
|
||||
userId: string,
|
||||
) => {
|
||||
try {
|
||||
const orgRes = await clerkClient.organizations.createOrganization({
|
||||
|
||||
@@ -92,22 +92,22 @@ export class ClickHouseManager {
|
||||
];
|
||||
|
||||
const queryResults = await Promise.allSettled(
|
||||
requiredQueries.map((query) => ClickHouseManager.readSQLFile(query))
|
||||
requiredQueries.map((query) => ClickHouseManager.readSQLFile(query)),
|
||||
);
|
||||
|
||||
const failedQueries = queryResults.filter(
|
||||
(result) => result.status === "rejected"
|
||||
(result) => result.status === "rejected",
|
||||
);
|
||||
|
||||
if (failedQueries.length > 0) {
|
||||
console.error(
|
||||
`Failed to read ${failedQueries.length} ClickHouse queries. Please re-pull the latest version of Autumn. `
|
||||
`Failed to read ${failedQueries.length} ClickHouse queries. Please re-pull the latest version of Autumn. `,
|
||||
);
|
||||
failedQueries.forEach((result, index) => {
|
||||
if (result.status === "rejected") {
|
||||
console.error(
|
||||
`Query ${requiredQueries[index]} failed:`,
|
||||
result.reason
|
||||
result.reason,
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -122,7 +122,7 @@ export class ClickHouseManager {
|
||||
|
||||
if (!ClickHouseManager.clickhouseAvailable) {
|
||||
console.log(
|
||||
"0. ClickHouse is not available, please set the CLICKHOUSE_URL, CLICKHOUSE_USERNAME, and CLICKHOUSE_PASSWORD environment variables."
|
||||
"0. ClickHouse is not available, please set the CLICKHOUSE_URL, CLICKHOUSE_USERNAME, and CLICKHOUSE_PASSWORD environment variables.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -134,7 +134,7 @@ export class ClickHouseManager {
|
||||
if (process.env.CLICKHOUSE_SKIP_ENSURES?.toLowerCase() === "true") {
|
||||
console.group();
|
||||
console.log(
|
||||
"✓ Skipping query ensures - queries assumed to exist already"
|
||||
"✓ Skipping query ensures - queries assumed to exist already",
|
||||
);
|
||||
console.groupEnd();
|
||||
return;
|
||||
@@ -159,7 +159,7 @@ export class ClickHouseManager {
|
||||
console.error(`✗ Failed to execute query ${query}:`, error);
|
||||
process.exit(1);
|
||||
}
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
console.groupEnd();
|
||||
@@ -180,7 +180,7 @@ export class ClickHouseManager {
|
||||
private async executeQuery(
|
||||
query: ClickHouseQuery,
|
||||
client: ClickHouseClient,
|
||||
options: any = {}
|
||||
options: any = {},
|
||||
) {
|
||||
const queryContent = await this.readSQLFile(query);
|
||||
if (!queryContent) {
|
||||
@@ -210,7 +210,7 @@ export class ClickHouseManager {
|
||||
client?: ClickHouseClient,
|
||||
options: QueryParams = {
|
||||
format: "TabSeparatedRaw",
|
||||
} as QueryParams
|
||||
} as QueryParams,
|
||||
) {
|
||||
const manager = await ClickHouseManager.getInstance();
|
||||
const clickhouseClient = client || (await ClickHouseManager.getClient());
|
||||
|
||||
2
server/src/external/logtail/logtailUtils.ts
vendored
2
server/src/external/logtail/logtailUtils.ts
vendored
@@ -45,7 +45,7 @@ const createLogMethod = (pinoMethod: any, logtailMethod?: any) => {
|
||||
const errorObject = args.find((arg) => arg instanceof Error);
|
||||
if (errorObject) {
|
||||
message = rewriteAppPath(
|
||||
errorObject.stack || errorObject.message || "Error occurred"
|
||||
errorObject.stack || errorObject.message || "Error occurred",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ export const createStripeMeteredPrice = async ({
|
||||
|
||||
export const arrearProratedToStripeTiers = (
|
||||
price: Price,
|
||||
entitlement: EntitlementWithFeature
|
||||
entitlement: EntitlementWithFeature,
|
||||
) => {
|
||||
let usageConfig = structuredClone(price.config) as UsagePriceConfig;
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ export const searchStripeMeter = async ({
|
||||
logger.info(`Stripe meter list took ${end - start}ms`);
|
||||
|
||||
let stripeMeter = allStripeMeters.find(
|
||||
(m) => m.event_name == eventName || m.id == meterId
|
||||
(m) => m.event_name == eventName || m.id == meterId,
|
||||
);
|
||||
|
||||
return stripeMeter;
|
||||
@@ -87,7 +87,7 @@ export const getStripeMeter = async ({
|
||||
createNew = true;
|
||||
} else {
|
||||
logger.info(
|
||||
`✅ Found existing meter for ${product.name} - ${feature!.name}`
|
||||
`✅ Found existing meter for ${product.name} - ${feature!.name}`,
|
||||
);
|
||||
return stripeMeter;
|
||||
}
|
||||
@@ -107,7 +107,7 @@ export const getStripeMeter = async ({
|
||||
// IN ARREAR
|
||||
export const priceToInArrearTiers = (
|
||||
price: Price,
|
||||
entitlement: Entitlement
|
||||
entitlement: Entitlement,
|
||||
) => {
|
||||
let usageConfig = structuredClone(price.config) as UsagePriceConfig;
|
||||
const tiers: any[] = [];
|
||||
@@ -177,7 +177,7 @@ export const createStripeInArrearPrice = async ({
|
||||
if (internalEntityId && !useCheckout) {
|
||||
if (!curStripeProduct) {
|
||||
logger.info(
|
||||
`Creating stripe in arrear product for ${relatedEnt.feature.name} (internal entity ID exists!)`
|
||||
`Creating stripe in arrear product for ${relatedEnt.feature.name} (internal entity ID exists!)`,
|
||||
);
|
||||
let stripeProduct = await stripeCli.products.create({
|
||||
name: `${product.name} - ${feature!.name}`,
|
||||
@@ -199,7 +199,7 @@ export const createStripeInArrearPrice = async ({
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Creating stripe in arrear price for ${relatedEnt.feature.name} (no internal entity ID)`
|
||||
`Creating stripe in arrear price for ${relatedEnt.feature.name} (no internal entity ID)`,
|
||||
);
|
||||
|
||||
if (!feature) {
|
||||
@@ -223,7 +223,7 @@ export const createStripeInArrearPrice = async ({
|
||||
|
||||
const tiers = priceToInArrearTiers(
|
||||
price,
|
||||
getPriceEntitlement(price, entitlements)
|
||||
getPriceEntitlement(price, entitlements),
|
||||
);
|
||||
|
||||
let priceAmountData = {};
|
||||
|
||||
@@ -23,7 +23,7 @@ import { DrizzleCli } from "@/db/initDrizzle.js";
|
||||
|
||||
export const prepaidToStripeTiers = (
|
||||
price: Price,
|
||||
entitlement: EntitlementWithFeature
|
||||
entitlement: EntitlementWithFeature,
|
||||
) => {
|
||||
let usageConfig = structuredClone(price.config) as UsagePriceConfig;
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ export const priceToOneOffAndTiered = ({
|
||||
const amount = getPriceForOverage(price, overage);
|
||||
if (!config.stripe_product_id) {
|
||||
console.log(
|
||||
`WARNING: One off & tiered in advance price has no stripe product id: ${price.id}, ${relatedEnt.feature.name}`
|
||||
`WARNING: One off & tiered in advance price has no stripe product id: ${price.id}, ${relatedEnt.feature.name}`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -57,7 +57,7 @@ export const deleteCouponFromCus = async ({
|
||||
|
||||
try {
|
||||
let stripeCus = (await stripeCli.customers.retrieve(
|
||||
stripeCusId
|
||||
stripeCusId,
|
||||
)) as Stripe.Customer;
|
||||
if (stripeCus.discount?.id === discountId) {
|
||||
await stripeCli.customers.deleteDiscount(stripeCusId, discountId);
|
||||
|
||||
@@ -87,8 +87,8 @@ const couponToStripeValue = ({
|
||||
const amountOff = Math.round(
|
||||
prices?.reduce(
|
||||
(acc, price) => acc + (price.config as FixedPriceConfig).amount,
|
||||
0
|
||||
) || 0
|
||||
0,
|
||||
) || 0,
|
||||
);
|
||||
|
||||
console.log("amountOff in couponToStripeValue", amountOff);
|
||||
@@ -162,7 +162,7 @@ export const createStripeCoupon = async ({
|
||||
for (const promoCode of reward.promo_codes) {
|
||||
try {
|
||||
const stripePromoCode = await stripeCli.promotionCodes.retrieve(
|
||||
promoCode.code
|
||||
promoCode.code,
|
||||
);
|
||||
throw new RecaseError({
|
||||
message: `Promo code ${promoCode.code} (${stripePromoCode.id}) already exists in Stripe`,
|
||||
|
||||
@@ -58,7 +58,7 @@ export async function ensureStripeProductsWithEnv({
|
||||
for (let fullProduct of fullProducts) {
|
||||
const initProduct = async () => {
|
||||
let existsInStripe = products.data.find(
|
||||
(p) => p.id === fullProduct.processor?.id
|
||||
(p) => p.id === fullProduct.processor?.id,
|
||||
);
|
||||
|
||||
if (existsInStripe) {
|
||||
@@ -75,7 +75,7 @@ export async function ensureStripeProductsWithEnv({
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`initialized product ${fullProduct.id} in Stripe during Stripe connection, env: ${env}`
|
||||
`initialized product ${fullProduct.id} in Stripe during Stripe connection, env: ${env}`,
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error(`Failed to init product in stripe: ${error}`);
|
||||
|
||||
@@ -46,11 +46,11 @@ export const createStripeSubThroughInvoice = async ({
|
||||
|
||||
let subItems = items.filter(
|
||||
(i: any, index: number) =>
|
||||
prices[index].config!.interval !== BillingInterval.OneOff
|
||||
prices[index].config!.interval !== BillingInterval.OneOff,
|
||||
);
|
||||
let invoiceItems = items.filter(
|
||||
(i: any, index: number) =>
|
||||
prices[index].config!.interval === BillingInterval.OneOff
|
||||
prices[index].config!.interval === BillingInterval.OneOff,
|
||||
);
|
||||
|
||||
try {
|
||||
|
||||
@@ -96,7 +96,7 @@ export const payForInvoice = async ({
|
||||
};
|
||||
} catch (error: any) {
|
||||
logger.error(
|
||||
`❌ Stripe error: Failed to pay invoice: ${error?.message || error}`
|
||||
`❌ Stripe error: Failed to pay invoice: ${error?.message || error}`,
|
||||
);
|
||||
|
||||
if (voidIfFailed) {
|
||||
@@ -189,7 +189,7 @@ export const getInvoiceDiscounts = ({
|
||||
let autumnDiscounts = expandedInvoice.discounts.map((discount: any) => {
|
||||
const amountOff = discount.coupon.amount_off;
|
||||
const amountUsed = totalDiscountAmounts?.find(
|
||||
(item) => item.discount === discount.id
|
||||
(item) => item.discount === discount.id,
|
||||
)?.amount;
|
||||
|
||||
let autumnDiscount: InvoiceDiscount = {
|
||||
|
||||
@@ -16,7 +16,7 @@ export const checkKeyValid = async (apiKey: string) => {
|
||||
export const createWebhookEndpoint = async (
|
||||
apiKey: string,
|
||||
env: AppEnv,
|
||||
orgId: string
|
||||
orgId: string,
|
||||
) => {
|
||||
const stripe = new Stripe(apiKey);
|
||||
|
||||
|
||||
15
server/src/external/stripe/stripeSubUtils.ts
vendored
15
server/src/external/stripe/stripeSubUtils.ts
vendored
@@ -46,7 +46,7 @@ export const getStripeSubs = async ({
|
||||
} catch (error: any) {
|
||||
console.log(
|
||||
`(warning) getStripeSubs: Failed to get sub ${subId}`,
|
||||
error.message
|
||||
error.message,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -134,7 +134,7 @@ export const getUsageBasedSub = async ({
|
||||
let autumnSub = autumnSubs?.find((sub) => sub.stripe_id == stripeSub.id);
|
||||
if (autumnSub) {
|
||||
let containsFeature = autumnSub.usage_features.includes(
|
||||
feature.internal_id!
|
||||
feature.internal_id!,
|
||||
);
|
||||
if (containsFeature) {
|
||||
return stripeSub;
|
||||
@@ -150,7 +150,7 @@ export const getUsageBasedSub = async ({
|
||||
if (
|
||||
!usageFeatures ||
|
||||
usageFeatures.find(
|
||||
(feat: any) => feat.internal_id == feature.internal_id
|
||||
(feat: any) => feat.internal_id == feature.internal_id,
|
||||
) === undefined
|
||||
) {
|
||||
continue;
|
||||
@@ -180,14 +180,15 @@ export const getSubItemsForCusProduct = async ({
|
||||
prices.some(
|
||||
(p) =>
|
||||
p.config?.stripe_price_id == item.price.id ||
|
||||
(p.config as UsagePriceConfig).stripe_product_id == item.price.product
|
||||
(p.config as UsagePriceConfig).stripe_product_id ==
|
||||
item.price.product,
|
||||
)
|
||||
) {
|
||||
subItems.push(item);
|
||||
}
|
||||
}
|
||||
let otherSubItems = stripeSub.items.data.filter(
|
||||
(item) => !subItems.some((i) => i.id == item.id)
|
||||
(item) => !subItems.some((i) => i.id == item.id),
|
||||
);
|
||||
|
||||
return { subItems, otherSubItems };
|
||||
@@ -207,7 +208,7 @@ export const getStripeSchedules = async ({
|
||||
scheduleId,
|
||||
{
|
||||
expand: ["phases.items.price"],
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (schedule.status == "canceled") {
|
||||
@@ -217,7 +218,7 @@ export const getStripeSchedules = async ({
|
||||
const batchPricesGet = [];
|
||||
for (const item of schedule.phases[0].items) {
|
||||
batchPricesGet.push(
|
||||
stripeCli.prices.retrieve((item.price as Stripe.Price).id as string)
|
||||
stripeCli.prices.retrieve((item.price as Stripe.Price).id as string),
|
||||
);
|
||||
}
|
||||
const prices = await Promise.all(batchPricesGet);
|
||||
|
||||
@@ -58,7 +58,7 @@ const getIntervalToPrices = (prices: Price[]) => {
|
||||
if (oneOffPrices && Object.keys(intervalToPrices).length > 1) {
|
||||
const nextIntervalKey = Object.keys(intervalToPrices)[0];
|
||||
intervalToPrices[nextIntervalKey!].push(
|
||||
...structuredClone(intervalToPrices[BillingInterval.OneOff])
|
||||
...structuredClone(intervalToPrices[BillingInterval.OneOff]),
|
||||
);
|
||||
delete intervalToPrices[BillingInterval.OneOff];
|
||||
}
|
||||
@@ -151,7 +151,7 @@ export const getStripeSubItems = async ({
|
||||
products: attachParams.products,
|
||||
price,
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
throw new RecaseError({
|
||||
code: ErrCode.ProductNotFound,
|
||||
@@ -190,7 +190,7 @@ export const getStripeSubItems = async ({
|
||||
org,
|
||||
interval: interval as BillingInterval,
|
||||
intervalCount,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ export const getStripeSubItems = async ({
|
||||
interval: b.interval,
|
||||
intervalCount: b.intervalCount,
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
return itemSets;
|
||||
@@ -276,7 +276,7 @@ export const getStripeSubItems2 = async ({
|
||||
products: attachParams.products,
|
||||
price,
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
throw new RecaseError({
|
||||
code: ErrCode.ProductNotFound,
|
||||
|
||||
@@ -93,7 +93,7 @@ export const findStripeItemForPrice = ({
|
||||
config.stripe_product_id == si.price?.product ||
|
||||
config.stripe_empty_price_id == si.price?.id
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (stripeItem) return stripeItem;
|
||||
@@ -108,7 +108,7 @@ export const findStripeItemForPrice = ({
|
||||
config.stripe_price_id == si.price?.id ||
|
||||
(stripeProdId && si.price?.product == stripeProdId)
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ export const findStripePriceFromPrices = ({
|
||||
autumnStripePricesMatch({
|
||||
stripePrice: p,
|
||||
autumnPrice,
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -26,9 +26,9 @@ export const undoSubUpdate = async ({
|
||||
(item) =>
|
||||
!prevItems.some((prevItem) =>
|
||||
curSub.items.data.some(
|
||||
(curItem) => curItem.price.id === item.price.id
|
||||
)
|
||||
)
|
||||
(curItem) => curItem.price.id === item.price.id,
|
||||
),
|
||||
),
|
||||
)
|
||||
.map((item) => {
|
||||
return {
|
||||
|
||||
10
server/src/external/stripe/stripeWebhooks.ts
vendored
10
server/src/external/stripe/stripeWebhooks.ts
vendored
@@ -38,7 +38,7 @@ const logStripeWebhook = ({
|
||||
event: Stripe.Event;
|
||||
}) => {
|
||||
req.logtail.info(
|
||||
`${chalk.yellow("STRIPE").padEnd(18)} ${event.type.padEnd(30)} ${req.org.slug} | ${event.id}`
|
||||
`${chalk.yellow("STRIPE").padEnd(18)} ${event.type.padEnd(30)} ${req.org.slug} | ${event.id}`,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -85,7 +85,7 @@ stripeWebhookRouter.post(
|
||||
event = await stripe.webhooks.constructEventAsync(
|
||||
request.body,
|
||||
sig,
|
||||
webhookSecret
|
||||
webhookSecret,
|
||||
);
|
||||
} catch (err: any) {
|
||||
response.status(400).send(`Webhook Error: ${err.message}`);
|
||||
@@ -280,7 +280,7 @@ stripeWebhookRouter.post(
|
||||
|
||||
// DO NOT DELETE -- RESPONSIBLE FOR SENDING SUCCESSFUL RESPONSE TO STRIPE...
|
||||
response.status(200).send();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const coreEvents = [
|
||||
@@ -319,7 +319,7 @@ export const handleStripeWebhookRefresh = async ({
|
||||
eventType,
|
||||
object: data.object,
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -331,7 +331,7 @@ export const handleStripeWebhookRefresh = async ({
|
||||
|
||||
if (!cus) {
|
||||
logger.warn(
|
||||
`Searched for customer by stripe id, but not found: ${stripeCusId}`
|
||||
`Searched for customer by stripe id, but not found: ${stripeCusId}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
2
server/src/external/stripe/utils.ts
vendored
2
server/src/external/stripe/utils.ts
vendored
@@ -56,7 +56,7 @@ export const calculateMetered1Price = ({
|
||||
}) => {
|
||||
const allowance = product.entitlements.metered1.allowance;
|
||||
const usagePrice = product.prices.find(
|
||||
(p: any) => p.config.feature_id === metered1Feature.id
|
||||
(p: any) => p.config.feature_id === metered1Feature.id,
|
||||
);
|
||||
|
||||
const usageConfig = usagePrice.config as UsagePriceConfig;
|
||||
|
||||
@@ -76,7 +76,7 @@ export const handleCheckoutSessionCompleted = async ({
|
||||
|
||||
console.log(
|
||||
"Handling checkout.completed: autumn metadata:",
|
||||
checkoutSession.metadata?.autumn_metadata_id
|
||||
checkoutSession.metadata?.autumn_metadata_id,
|
||||
);
|
||||
|
||||
if (attachParams.setupPayment) {
|
||||
@@ -132,14 +132,14 @@ export const handleCheckoutSessionCompleted = async ({
|
||||
console.log("Inserting products list");
|
||||
for (const productOptions of attachParams.productsList) {
|
||||
const product = attachParams.products.find(
|
||||
(p) => p.id === productOptions.product_id
|
||||
(p) => p.id === productOptions.product_id,
|
||||
);
|
||||
|
||||
if (!product) {
|
||||
logger.error(
|
||||
`checkout.completed: product not found for productOptions: ${JSON.stringify(
|
||||
productOptions
|
||||
)}`
|
||||
productOptions,
|
||||
)}`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -149,7 +149,7 @@ export const handleCheckoutSessionCompleted = async ({
|
||||
attachParams: attachToInsertParams(
|
||||
attachParams,
|
||||
product,
|
||||
productOptions.entity_id || undefined
|
||||
productOptions.entity_id || undefined,
|
||||
),
|
||||
subscriptionIds: checkoutSub ? [checkoutSub?.id!] : undefined,
|
||||
anchorToUnix,
|
||||
@@ -182,7 +182,7 @@ export const handleCheckoutSessionCompleted = async ({
|
||||
attachParams,
|
||||
invoiceId,
|
||||
logger,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ export const getOptionsFromCheckoutSession = async ({
|
||||
const usageInAdvanceExists = attachParams.prices.some(
|
||||
(price) =>
|
||||
getBillingType(price.config as UsagePriceConfig) ==
|
||||
BillingType.UsageInAdvance
|
||||
BillingType.UsageInAdvance,
|
||||
);
|
||||
|
||||
if (!usageInAdvanceExists) {
|
||||
@@ -54,7 +54,7 @@ export const getOptionsFromCheckoutSession = async ({
|
||||
}
|
||||
|
||||
const index = optionsList.findIndex(
|
||||
(feature) => feature.internal_feature_id == config.internal_feature_id
|
||||
(feature) => feature.internal_feature_id == config.internal_feature_id,
|
||||
);
|
||||
|
||||
if (index == -1) {
|
||||
|
||||
@@ -92,7 +92,7 @@ export const handleCheckoutSub = async ({
|
||||
price: emptyPrice,
|
||||
quantity: 0,
|
||||
}
|
||||
: (getEmptyPriceItem({ price: arrearPrice, org }) as any)
|
||||
: (getEmptyPriceItem({ price: arrearPrice, org }) as any),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ export const handleRemainingSets = async ({
|
||||
attachParams.apiVersion == APIVersion.v1_4
|
||||
) {
|
||||
const replaceIndex = remainingItems.findIndex(
|
||||
(item) => item.price == config.stripe_price_id
|
||||
(item) => item.price == config.stripe_price_id,
|
||||
);
|
||||
|
||||
if (replaceIndex != -1) {
|
||||
|
||||
@@ -33,7 +33,7 @@ export async function handleCusDiscountDeleted({
|
||||
|
||||
if (customer.env !== env || customer.org_id !== org.id) {
|
||||
logger.info(
|
||||
`discount.deleted: env or org mismatch, skipping, ${customer.env} !== ${env} || ${customer.org_id} !== ${org.id}`
|
||||
`discount.deleted: env or org mismatch, skipping, ${customer.env} !== ${env} || ${customer.org_id} !== ${org.id}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -45,7 +45,7 @@ export async function handleCusDiscountDeleted({
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`discount.deleted:, discount ID: ${discount.id}, found ${redemptions.length} redemptions`
|
||||
`discount.deleted:, discount ID: ${discount.id}, found ${redemptions.length} redemptions`,
|
||||
);
|
||||
|
||||
if (redemptions.length == 0) return;
|
||||
@@ -55,12 +55,12 @@ export async function handleCusDiscountDeleted({
|
||||
r.reward_program.reward.id ===
|
||||
(typeof discount.coupon == "string"
|
||||
? discount.coupon
|
||||
: discount.coupon.id)
|
||||
: discount.coupon.id),
|
||||
);
|
||||
|
||||
if (discount.subscription) {
|
||||
logger.info(
|
||||
`Discount is a subscription, paidProductRedemption: ${paidProductRedemption?.id}`
|
||||
`Discount is a subscription, paidProductRedemption: ${paidProductRedemption?.id}`,
|
||||
);
|
||||
|
||||
if (!paidProductRedemption) return;
|
||||
@@ -88,7 +88,7 @@ export async function handleCusDiscountDeleted({
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error(
|
||||
`Failed to update subscription ${discount.subscription} with paid product coupon, error: ${error.message}`
|
||||
`Failed to update subscription ${discount.subscription} with paid product coupon, error: ${error.message}`,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
@@ -119,12 +119,12 @@ export async function handleCusDiscountDeleted({
|
||||
});
|
||||
|
||||
const stripeCus = (await stripeCli.customers.retrieve(
|
||||
discount.customer
|
||||
discount.customer,
|
||||
)) as Stripe.Customer;
|
||||
|
||||
if (stripeCus && notNullish(stripeCus.discount)) {
|
||||
logger.info(
|
||||
`discount.deleted: stripe customer ${discount.customer} already has a discount`
|
||||
`discount.deleted: stripe customer ${discount.customer} already has a discount`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -138,7 +138,7 @@ export async function handleCusDiscountDeleted({
|
||||
|
||||
if (!reward) {
|
||||
logger.warn(
|
||||
`discount.deleted: reward ${redemption.reward_program.internal_id} not found`
|
||||
`discount.deleted: reward ${redemption.reward_program.internal_id} not found`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -163,7 +163,7 @@ export async function handleCusDiscountDeleted({
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`discount.deleted: applied reward ${reward.name} on customer ${customer.name} (${customer.id})`
|
||||
`discount.deleted: applied reward ${reward.name} on customer ${customer.name} (${customer.id})`,
|
||||
);
|
||||
logger.info(`Redemption ID: ${redemption.id}`);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ export const handleContUsePrices = async ({
|
||||
|
||||
let feature = cusEnt.entitlement.feature;
|
||||
logger.info(
|
||||
`Handling invoice.created for in arrear prorated, feature: ${feature.id}`
|
||||
`Handling invoice.created for in arrear prorated, feature: ${feature.id}`,
|
||||
);
|
||||
|
||||
let replaceables = cusEnt.replaceables.filter((r) => r.delete_next_cycle);
|
||||
|
||||
@@ -84,7 +84,7 @@ const handleInArrearProrated = async ({
|
||||
|
||||
let feature = cusEnt.entitlement.feature;
|
||||
logger.info(
|
||||
`Handling invoice.created for in arrear prorated, feature: ${feature.id}`
|
||||
`Handling invoice.created for in arrear prorated, feature: ${feature.id}`,
|
||||
);
|
||||
|
||||
let deletedEntities = await EntityService.list({
|
||||
@@ -100,12 +100,12 @@ const handleInArrearProrated = async ({
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`✨ Handling in arrear prorated, customer ${customer.name}, org: ${org.slug}`
|
||||
`✨ Handling in arrear prorated, customer ${customer.name}, org: ${org.slug}`,
|
||||
);
|
||||
|
||||
logger.info(
|
||||
`Deleting entities, feature ${feature.id}, customer ${customer.id}, org ${org.slug}`,
|
||||
deletedEntities
|
||||
deletedEntities,
|
||||
);
|
||||
|
||||
// Get linked cus ents
|
||||
@@ -119,7 +119,7 @@ const handleInArrearProrated = async ({
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Linked cus ent: ${linkedCusEnt.feature_id}, isLinked: ${isLinked}`
|
||||
`Linked cus ent: ${linkedCusEnt.feature_id}, isLinked: ${isLinked}`,
|
||||
);
|
||||
|
||||
// Delete cus ent ids
|
||||
@@ -140,7 +140,7 @@ const handleInArrearProrated = async ({
|
||||
console.log(`Updated ${updated.length} cus ents`);
|
||||
|
||||
logger.info(
|
||||
`Feature: ${feature.id}, customer: ${customer.id}, deleted entities from cus ent`
|
||||
`Feature: ${feature.id}, customer: ${customer.id}, deleted entities from cus ent`,
|
||||
);
|
||||
linkedCusEnt.entities = newEntities;
|
||||
}
|
||||
@@ -154,7 +154,7 @@ const handleInArrearProrated = async ({
|
||||
logger.info(
|
||||
`Feature: ${feature.id}, Deleted ${
|
||||
deletedEntities.length
|
||||
}, entities: ${deletedEntities.map((e) => `${e.id}`).join(", ")}`
|
||||
}, entities: ${deletedEntities.map((e) => `${e.id}`).join(", ")}`,
|
||||
);
|
||||
|
||||
// Increase balance
|
||||
@@ -319,13 +319,13 @@ export const handleInvoiceCreated = async ({
|
||||
|
||||
if (activeProducts.length == 0) {
|
||||
logger.warn(
|
||||
`Stripe invoice.created -- no active products found (${org.slug})`
|
||||
`Stripe invoice.created -- no active products found (${org.slug})`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let internalEntityId = activeProducts.find(
|
||||
(p) => p.internal_entity_id
|
||||
(p) => p.internal_entity_id,
|
||||
)?.internal_entity_id;
|
||||
|
||||
let features = await FeatureService.list({
|
||||
|
||||
@@ -51,7 +51,7 @@ export const handlePrepaidPrices = async ({
|
||||
|
||||
if (!cusEnt) {
|
||||
logger.error(
|
||||
`Tried to handle prepaid price for ${cusPrice.id} (${cusPrice.price.id}) but no cus ent found`
|
||||
`Tried to handle prepaid price for ${cusPrice.id} (${cusPrice.price.id}) but no cus ent found`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ export const handleUsagePrices = async ({
|
||||
Math.abs(
|
||||
differenceInMinutes(
|
||||
new Date(activeProduct.created_at),
|
||||
new Date(invoice.created * 1000)
|
||||
)
|
||||
new Date(invoice.created * 1000),
|
||||
),
|
||||
) < 10;
|
||||
|
||||
let invoiceFromUpgrade =
|
||||
@@ -106,7 +106,7 @@ export const handleUsagePrices = async ({
|
||||
} else {
|
||||
if (!config.stripe_meter_id) {
|
||||
logger.warn(
|
||||
`Price ${price.id} has no stripe meter id, skipping invoice.created for usage in arrear`
|
||||
`Price ${price.id} has no stripe meter id, skipping invoice.created for usage in arrear`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -118,7 +118,7 @@ export const handleUsagePrices = async ({
|
||||
});
|
||||
|
||||
const usageTimestamp = Math.round(
|
||||
subDays(new Date(invoice.created * 1000), 1).getTime() / 1000
|
||||
subDays(new Date(invoice.created * 1000), 1).getTime() / 1000,
|
||||
);
|
||||
|
||||
await submitUsageToStripe({
|
||||
|
||||
@@ -69,7 +69,7 @@ export const handleInvoiceFinalized = async ({
|
||||
}
|
||||
|
||||
let prices = activeProducts.flatMap((cp) =>
|
||||
cp.customer_prices.map((cpr: FullCustomerPrice) => cpr.price)
|
||||
cp.customer_prices.map((cpr: FullCustomerPrice) => cpr.price),
|
||||
);
|
||||
|
||||
let invoiceItems = await getInvoiceItems({
|
||||
|
||||
@@ -78,7 +78,7 @@ export const handleSubCreated = async ({
|
||||
try {
|
||||
subUsageFeatures = JSON.parse(subscription.metadata?.usage_features);
|
||||
subUsageFeatures = subUsageFeatures.map(
|
||||
(feature: any) => feature.internal_id
|
||||
(feature: any) => feature.internal_id,
|
||||
);
|
||||
} catch (error) {
|
||||
console.log("Error parsing usage features", error);
|
||||
@@ -102,7 +102,7 @@ export const handleSubCreated = async ({
|
||||
|
||||
console.log(
|
||||
"Handling subscription.created for scheduled cus products:",
|
||||
cusProds.length
|
||||
cusProds.length,
|
||||
);
|
||||
|
||||
let batchUpdate = [];
|
||||
@@ -131,7 +131,7 @@ export const handleSubCreated = async ({
|
||||
let invoiceItems = await getInvoiceItems({
|
||||
stripeInvoice: invoice,
|
||||
prices: cusProd.customer_prices.map(
|
||||
(cpr: FullCustomerPrice) => cpr.price
|
||||
(cpr: FullCustomerPrice) => cpr.price,
|
||||
),
|
||||
logger,
|
||||
});
|
||||
@@ -171,7 +171,7 @@ export const handleSubCreated = async ({
|
||||
.map((cp) => cp.price)
|
||||
.filter(
|
||||
(p: Price) =>
|
||||
getBillingType(p.config as any) == BillingType.UsageInArrear
|
||||
getBillingType(p.config as any) == BillingType.UsageInArrear,
|
||||
);
|
||||
|
||||
if (arrearPrices.length == 0) {
|
||||
@@ -181,7 +181,7 @@ export const handleSubCreated = async ({
|
||||
let itemsToDelete = [];
|
||||
for (const arrearPrice of arrearPrices) {
|
||||
let subItem = subscription.items.data.find(
|
||||
(i) => i.price.id == arrearPrice.config?.stripe_price_id
|
||||
(i) => i.price.id == arrearPrice.config?.stripe_price_id,
|
||||
);
|
||||
|
||||
if (!subItem) {
|
||||
@@ -200,12 +200,12 @@ export const handleSubCreated = async ({
|
||||
items: itemsToDelete,
|
||||
});
|
||||
console.log(
|
||||
`sub.created, cus product with entity: deleted ${itemsToDelete.length} items`
|
||||
`sub.created, cus product with entity: deleted ${itemsToDelete.length} items`,
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`sub.created, cus product with entity: failed to delete items`,
|
||||
error
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export const handleSubDeleted = async ({
|
||||
if (activeCusProducts.length === 0) {
|
||||
if (data.livemode) {
|
||||
logger.warn(
|
||||
`subscription.deleted: ${data.id} - no customer products found`
|
||||
`subscription.deleted: ${data.id} - no customer products found`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -47,14 +47,14 @@ export const handleSubDeleted = async ({
|
||||
cancellationComment === "autumn_cancel"
|
||||
) {
|
||||
logger.info(
|
||||
`sub.deleted: ${subscription.id} from ${cancellationComment}, skipping`
|
||||
`sub.deleted: ${subscription.id} from ${cancellationComment}, skipping`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cancellationComment?.includes("trial_canceled")) {
|
||||
logger.info(
|
||||
`sub.deleted: ${subscription.id} from trial canceled, skipping`
|
||||
`sub.deleted: ${subscription.id} from trial canceled, skipping`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ export const handleCusProductDeleted = async ({
|
||||
|
||||
if (usagePrices.length > 0) {
|
||||
logger.info(
|
||||
`sub.deleted, submitting usage for ${fullCus.id}, ${cusProduct.product.name}`
|
||||
`sub.deleted, submitting usage for ${fullCus.id}, ${cusProduct.product.name}`,
|
||||
);
|
||||
|
||||
await createUsageInvoice({
|
||||
@@ -89,14 +89,14 @@ export const handleCusProductDeleted = async ({
|
||||
|
||||
if (scheduled_ids && scheduled_ids.length > 0 && !prematurelyCanceled) {
|
||||
logger.info(
|
||||
`sub.deleted: removing sub_id from cus product ${cusProduct.id}`
|
||||
`sub.deleted: removing sub_id from cus product ${cusProduct.id}`,
|
||||
);
|
||||
await CusProductService.update({
|
||||
db,
|
||||
cusProductId: cusProduct.id,
|
||||
updates: {
|
||||
subscription_ids: cusProduct.subscription_ids?.filter(
|
||||
(id) => id !== subscription.id
|
||||
(id) => id !== subscription.id,
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -93,7 +93,7 @@ export const handleSubscriptionUpdated = async ({
|
||||
|
||||
if (updatedCusProducts.length > 0) {
|
||||
logger.info(
|
||||
`✅ Updated ${updatedCusProducts.length} customer product${updatedCusProducts.length === 1 ? "" : "s"} (${updatedCusProducts.map((cp) => cp.id).join(", ")}) - Status: ${updatedCusProducts[0].status}${updatedCusProducts[0].canceled_at ? `, Canceled: ${new Date(updatedCusProducts[0].canceled_at).toISOString()}` : ""}`
|
||||
`✅ Updated ${updatedCusProducts.length} customer product${updatedCusProducts.length === 1 ? "" : "s"} (${updatedCusProducts.map((cp) => cp.id).join(", ")}) - Status: ${updatedCusProducts[0].status}${updatedCusProducts[0].canceled_at ? `, Canceled: ${new Date(updatedCusProducts[0].canceled_at).toISOString()}` : ""}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ export const handleSubscriptionUpdated = async ({
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
`Failed to update sub from stripe. Stripe sub ID: ${subscription.id}, org: ${org.slug}, env: ${env}`,
|
||||
error
|
||||
error,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -133,11 +133,11 @@ export const handleSubscriptionUpdated = async ({
|
||||
});
|
||||
|
||||
const latestInvoice = await stripeCli.invoices.retrieve(
|
||||
subscription.latest_invoice
|
||||
subscription.latest_invoice,
|
||||
);
|
||||
|
||||
logger.info(
|
||||
`Latest invoice billing reason: ${latestInvoice.billing_reason}`
|
||||
`Latest invoice billing reason: ${latestInvoice.billing_reason}`,
|
||||
);
|
||||
logger.info(`Latest invoice status: ${latestInvoice.status}`);
|
||||
|
||||
@@ -155,7 +155,7 @@ export const handleSubscriptionUpdated = async ({
|
||||
latestInvoiceStatus: latestInvoice.status,
|
||||
latestInvoiceBillingReason: latestInvoice.billing_reason,
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -171,7 +171,7 @@ export const handleSubscriptionUpdated = async ({
|
||||
latestInvoiceStatus: latestInvoice.status,
|
||||
latestInvoiceBillingReason: latestInvoice.billing_reason,
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
await stripeCli.subscriptions.cancel(subscription.id);
|
||||
await stripeCli.invoices.voidInvoice(subscription.latest_invoice);
|
||||
@@ -187,7 +187,7 @@ export const handleSubscriptionUpdated = async ({
|
||||
latestInvoiceStatus: latestInvoice.status,
|
||||
latestInvoiceBillingReason: latestInvoice.billing_reason,
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ export const handleSchedulePhaseCompleted = async ({
|
||||
subObject.schedule as string,
|
||||
{
|
||||
expand: ["customer"],
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const cusProducts = await CusProductService.getByScheduleId({
|
||||
@@ -52,7 +52,7 @@ export const handleSchedulePhaseCompleted = async ({
|
||||
|
||||
if (shouldExpire) {
|
||||
logger.info(
|
||||
`Expiring cus product: ${cusProduct.product.name} (entity ID: ${cusProduct.entity_id})`
|
||||
`Expiring cus product: ${cusProduct.product.name} (entity ID: ${cusProduct.entity_id})`,
|
||||
);
|
||||
await CusProductService.update({
|
||||
db: req.db,
|
||||
@@ -104,7 +104,7 @@ export const handleSchedulePhaseCompleted = async ({
|
||||
const currentPhase = schedule.phases.findIndex(
|
||||
(phase) =>
|
||||
phase.start_date <= Math.floor(now / 1000) &&
|
||||
(phase.end_date ? phase.end_date > Math.floor(now / 1000) : true)
|
||||
(phase.end_date ? phase.end_date > Math.floor(now / 1000) : true),
|
||||
);
|
||||
|
||||
if (
|
||||
@@ -123,7 +123,7 @@ export const handleSchedulePhaseCompleted = async ({
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error(
|
||||
`schedule.phase.completed: failed to cancel schedule ${schedule.id}, error: ${error.message}`
|
||||
`schedule.phase.completed: failed to cancel schedule ${schedule.id}, error: ${error.message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ const updateCusProductCanceled = async ({
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`Updating cus products for sub ${sub.id} to canceled | canceled_at: ${canceledAt}`
|
||||
`Updating cus products for sub ${sub.id} to canceled | canceled_at: ${canceledAt}`,
|
||||
);
|
||||
|
||||
await CusProductService.updateByStripeSubId({
|
||||
@@ -145,8 +145,8 @@ export const handleSubCanceled = async ({
|
||||
let defaultProducts = allDefaultProducts.filter((p) =>
|
||||
updatedCusProducts.some(
|
||||
(cp: FullCusProduct) =>
|
||||
cp.product.group == p.group && nullish(cp.internal_entity_id)
|
||||
)
|
||||
cp.product.group == p.group && nullish(cp.internal_entity_id),
|
||||
),
|
||||
);
|
||||
|
||||
if (defaultProducts.length == 0) return;
|
||||
@@ -156,14 +156,14 @@ export const handleSubCanceled = async ({
|
||||
const productNames = defaultProducts.map((p) => p.name).join(", ");
|
||||
const periodEnd = formatUnixToDateTime(end * 1000);
|
||||
logger.info(
|
||||
`subscription.updated: canceled -> attempting to schedule default products: ${productNames}, period end: ${periodEnd}`
|
||||
`subscription.updated: canceled -> attempting to schedule default products: ${productNames}, period end: ${periodEnd}`,
|
||||
);
|
||||
}
|
||||
|
||||
let scheduledCusProducts: FullCusProduct[] = [];
|
||||
for (let product of defaultProducts) {
|
||||
let alreadyScheduled = cusProducts.some(
|
||||
(cp: FullCusProduct) => cp.product.group == product.group
|
||||
(cp: FullCusProduct) => cp.product.group == product.group,
|
||||
);
|
||||
|
||||
if (alreadyScheduled) {
|
||||
@@ -205,7 +205,7 @@ export const handleSubCanceled = async ({
|
||||
scenario: AttachScenario.Cancel,
|
||||
cusProduct: cusProd,
|
||||
scheduledCusProduct: scheduledCusProducts.find(
|
||||
(cp) => cp.product.group === cusProd.product.group
|
||||
(cp) => cp.product.group === cusProd.product.group,
|
||||
),
|
||||
});
|
||||
} catch (error) {}
|
||||
|
||||
@@ -103,7 +103,7 @@ export const handleSubRenewed = async ({
|
||||
|
||||
if (curScheduledProduct) {
|
||||
logger.info(
|
||||
`sub.updated: renewed -> removing scheduled: ${curScheduledProduct.product.name}, main product: ${updatedCusProducts[0].product.name}`
|
||||
`sub.updated: renewed -> removing scheduled: ${curScheduledProduct.product.name}, main product: ${updatedCusProducts[0].product.name}`,
|
||||
);
|
||||
|
||||
await CusProductService.delete({
|
||||
@@ -126,7 +126,7 @@ export const handleSubRenewed = async ({
|
||||
scenario: AttachScenario.Renew,
|
||||
cusProduct: cusProd,
|
||||
deletedCusProduct: deletedCusProducts.find(
|
||||
(cp) => cp.product.group === cusProd.product.group
|
||||
(cp) => cp.product.group === cusProd.product.group,
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ export const subscribeToOrgUpdates = async ({ db }: { db: DrizzleCli }) => {
|
||||
}
|
||||
});
|
||||
|
||||
console.log("Successfully subscribed to organization updates via PostgreSQL LISTEN/NOTIFY");
|
||||
console.log(
|
||||
"Successfully subscribed to organization updates via PostgreSQL LISTEN/NOTIFY",
|
||||
);
|
||||
} catch (error) {
|
||||
console.warn("Error subscribing to org updates:", error);
|
||||
}
|
||||
|
||||
4
server/src/external/supabaseUtils.ts
vendored
4
server/src/external/supabaseUtils.ts
vendored
@@ -25,7 +25,7 @@ const fetchWithRetry = fetchRetry(fetch, {
|
||||
console.warn(
|
||||
`Retrying request... Attempt #${attempt + 1} - Status: ${
|
||||
response?.status
|
||||
}`
|
||||
}`,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
@@ -38,7 +38,7 @@ export const createSupabaseClient = () => {
|
||||
try {
|
||||
return createClient(
|
||||
process.env.SUPABASE_URL!,
|
||||
process.env.SUPABASE_SERVICE_KEY!
|
||||
process.env.SUPABASE_SERVICE_KEY!,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error creating Supabase client:", error);
|
||||
|
||||
4
server/src/external/webhooks/webhookUtils.ts
vendored
4
server/src/external/webhooks/webhookUtils.ts
vendored
@@ -5,7 +5,7 @@ export const verifySvixSignature = async (req: any, res: any) => {
|
||||
|
||||
if (!SIGNING_SECRET) {
|
||||
throw new Error(
|
||||
"Error: Please add SIGNING_SECRET from Clerk Dashboard to .env"
|
||||
"Error: Please add SIGNING_SECRET from Clerk Dashboard to .env",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export const verifySvixSignature = async (req: any, res: any) => {
|
||||
// Use constant-time comparison to prevent timing attacks
|
||||
return crypto.timingSafeEqual(
|
||||
Buffer.from(signature),
|
||||
Buffer.from(svixSignature)
|
||||
Buffer.from(svixSignature),
|
||||
);
|
||||
} catch (err) {
|
||||
return false;
|
||||
|
||||
@@ -68,7 +68,7 @@ const init = async () => {
|
||||
"If-Modified-Since",
|
||||
"If-Unmodified-Since",
|
||||
],
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
app.all("/api/auth/*", toNodeHandler(auth));
|
||||
|
||||
@@ -33,6 +33,8 @@ WHERE
|
||||
|
||||
const resultJson = await result.json();
|
||||
|
||||
return (resultJson.data as { total_payment_volume: number, label: string }[])[0];
|
||||
return (
|
||||
resultJson.data as { total_payment_volume: number; label: string }[]
|
||||
)[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,14 +84,14 @@ analyticsRouter.post("", (req, res) =>
|
||||
});
|
||||
|
||||
let usageList = events.data.filter(
|
||||
(event: any) => event.period <= Date.now()
|
||||
(event: any) => event.period <= Date.now(),
|
||||
);
|
||||
|
||||
res.status(200).json({
|
||||
list: usageList,
|
||||
});
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
export { analyticsRouter };
|
||||
|
||||
@@ -20,7 +20,7 @@ export async function getBillingCycleStartDate(
|
||||
orgId: string,
|
||||
customer?: FullCustomer,
|
||||
db?: DrizzleCli,
|
||||
intervalType?: "1bc" | "3bc"
|
||||
intervalType?: "1bc" | "3bc",
|
||||
) {
|
||||
// If no customer provided, return empty object (for aggregateAll case)
|
||||
if (!customer || !db || !intervalType) {
|
||||
@@ -42,13 +42,13 @@ export async function getBillingCycleStartDate(
|
||||
|
||||
const subscriptions = customer.subscriptions || [];
|
||||
const cusProducts = customer.customer_products.filter(
|
||||
(product: FullCusProduct) => ACTIVE_STATUSES.includes(product.status)
|
||||
(product: FullCusProduct) => ACTIVE_STATUSES.includes(product.status),
|
||||
);
|
||||
|
||||
if (cusProducts.length === 0) return {};
|
||||
|
||||
const fullProducts = cusProducts.map((cp: FullCusProduct) =>
|
||||
cusProductToProduct({ cusProduct: cp })
|
||||
cusProductToProduct({ cusProduct: cp }),
|
||||
);
|
||||
|
||||
const areAllProductsFree = checkIfAllProductsAreFree(fullProducts);
|
||||
@@ -64,7 +64,7 @@ export async function getBillingCycleStartDate(
|
||||
}
|
||||
|
||||
export function checkIfAllProductsAreFree(
|
||||
fullProducts: FullProduct[]
|
||||
fullProducts: FullProduct[],
|
||||
): boolean {
|
||||
return fullProducts.every((product: FullProduct) => {
|
||||
const isFree = isFreeProduct(product.prices);
|
||||
@@ -79,7 +79,7 @@ export function formatDateToString(date: Date): string {
|
||||
|
||||
export function getDateRangesFromSubscriptions(
|
||||
customerProductsFiltered: FullCusProduct[],
|
||||
subscriptions: Subscription[]
|
||||
subscriptions: Subscription[],
|
||||
): { startDates: string[]; endDates: string[] } {
|
||||
const startDates: string[] = [];
|
||||
const endDates: string[] = [];
|
||||
@@ -88,19 +88,19 @@ export function getDateRangesFromSubscriptions(
|
||||
product.subscription_ids?.forEach((subscriptionId: string) => {
|
||||
const subscription = subscriptions.find(
|
||||
(subscription: Subscription) =>
|
||||
subscription.stripe_id === subscriptionId
|
||||
subscription.stripe_id === subscriptionId,
|
||||
);
|
||||
|
||||
if (subscription) {
|
||||
startDates.push(
|
||||
formatDateToString(
|
||||
new Date((subscription.current_period_start ?? 0) * 1000)
|
||||
)
|
||||
new Date((subscription.current_period_start ?? 0) * 1000),
|
||||
),
|
||||
);
|
||||
endDates.push(
|
||||
formatDateToString(
|
||||
new Date((subscription.current_period_end ?? 0) * 1000)
|
||||
)
|
||||
new Date((subscription.current_period_end ?? 0) * 1000),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -110,7 +110,7 @@ export function getDateRangesFromSubscriptions(
|
||||
}
|
||||
|
||||
export function getDateRangesFromEntitlements(
|
||||
customerProducts?: FullCusProduct[]
|
||||
customerProducts?: FullCusProduct[],
|
||||
): { startDates: string[]; endDates: string[] } {
|
||||
const startDates: string[] = [];
|
||||
const endDates: string[] = [];
|
||||
@@ -120,8 +120,8 @@ export function getDateRangesFromEntitlements(
|
||||
JSON.stringify(
|
||||
customerProducts?.map((x) => x.customer_entitlements),
|
||||
null,
|
||||
4
|
||||
)
|
||||
4,
|
||||
),
|
||||
);
|
||||
|
||||
if (!customerProducts || customerProducts.length < 1) {
|
||||
@@ -140,20 +140,20 @@ export function getDateRangesFromEntitlements(
|
||||
(entitlement: FullCustomerEntitlement) => {
|
||||
if (entitlement.next_reset_at) {
|
||||
endDates.push(
|
||||
formatDateToString(new Date(entitlement.next_reset_at))
|
||||
formatDateToString(new Date(entitlement.next_reset_at)),
|
||||
);
|
||||
}
|
||||
|
||||
const startDate = calculateStartDateFromInterval(
|
||||
entitlement.entitlement.interval,
|
||||
entitlement.next_reset_at,
|
||||
entitlement.created_at
|
||||
entitlement.created_at,
|
||||
);
|
||||
|
||||
if (startDate) {
|
||||
startDates.push(startDate);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -163,7 +163,7 @@ export function getDateRangesFromEntitlements(
|
||||
export function calculateBillingCycleResult(
|
||||
startDates: string[],
|
||||
endDates: string[],
|
||||
intervalType: "1bc" | "3bc"
|
||||
intervalType: "1bc" | "3bc",
|
||||
) {
|
||||
const startDate = new Date(startDates[0]);
|
||||
const endDate = new Date(endDates[0]);
|
||||
@@ -180,7 +180,7 @@ export function calculateBillingCycleResult(
|
||||
export function calculateStartDateFromInterval(
|
||||
interval: EntInterval | null | undefined,
|
||||
nextResetAt: number | null | undefined,
|
||||
createdAt: number
|
||||
createdAt: number,
|
||||
): string | null {
|
||||
if (!nextResetAt && interval !== EntInterval.Lifetime) {
|
||||
return null;
|
||||
@@ -197,7 +197,7 @@ export function calculateStartDateFromInterval(
|
||||
return formatDateToString(new Date(nextResetAt! - 24 * 60 * 60 * 1000));
|
||||
case EntInterval.Week:
|
||||
return formatDateToString(
|
||||
new Date(nextResetAt! - 7 * 24 * 60 * 60 * 1000)
|
||||
new Date(nextResetAt! - 7 * 24 * 60 * 60 * 1000),
|
||||
);
|
||||
case EntInterval.Month:
|
||||
const monthResetDate = new Date(nextResetAt!);
|
||||
@@ -222,7 +222,7 @@ export function calculateStartDateFromInterval(
|
||||
|
||||
export function generateEventCountExpressions(
|
||||
eventNames: string[],
|
||||
noCount: boolean = false
|
||||
noCount: boolean = false,
|
||||
): string {
|
||||
const expressions = eventNames.map((eventName) => {
|
||||
// Replicate ClickHouse's replaceAll(eventName, '''', '''''')
|
||||
|
||||
@@ -179,7 +179,7 @@ export const handleProductsUpdated = async ({
|
||||
await ActionService.insert(db, action);
|
||||
} else {
|
||||
logger.warn(
|
||||
"products.updated, no req object found, skipping action insert"
|
||||
"products.updated, no req object found, skipping action insert",
|
||||
);
|
||||
}
|
||||
} catch (error: any) {
|
||||
|
||||
@@ -55,7 +55,7 @@ analyticsRouter.get("/event_names", async (req: any, res: any) =>
|
||||
features.some(
|
||||
(feature: Feature) =>
|
||||
feature.type == FeatureType.Metered &&
|
||||
feature.config.filters?.[0]?.value.includes(result[i])
|
||||
feature.config.filters?.[0]?.value.includes(result[i]),
|
||||
)
|
||||
) {
|
||||
eventNames.push(result[i]);
|
||||
@@ -73,7 +73,7 @@ analyticsRouter.get("/event_names", async (req: any, res: any) =>
|
||||
eventNames,
|
||||
});
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
const getTopEvents = async ({ req }: { req: ExtendedRequest }) => {
|
||||
@@ -106,7 +106,7 @@ const getTopEvents = async ({ req }: { req: ExtendedRequest }) => {
|
||||
features.some(
|
||||
(feature: Feature) =>
|
||||
feature.type == FeatureType.Metered &&
|
||||
feature.config.filters?.[0]?.value.includes(result[i])
|
||||
feature.config.filters?.[0]?.value.includes(result[i]),
|
||||
)
|
||||
) {
|
||||
eventNames.push(result[i]);
|
||||
@@ -201,7 +201,7 @@ analyticsRouter.post("/events", async (req: any, res: any) =>
|
||||
bcExclusionFlag,
|
||||
});
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
analyticsRouter.post("/raw", async (req: any, res: any) =>
|
||||
@@ -253,5 +253,5 @@ analyticsRouter.post("/raw", async (req: any, res: any) =>
|
||||
rawEvents: events,
|
||||
});
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -167,9 +167,9 @@ export class CusBatchService {
|
||||
: null,
|
||||
balance: ce.balance ? parseFloat(ce.balance) || 0 : 0,
|
||||
adjustment: ce.adjustment ? parseFloat(ce.adjustment) || 0 : 0,
|
||||
})
|
||||
}),
|
||||
),
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,18 +35,18 @@ const schema = z.object({
|
||||
}),
|
||||
{
|
||||
invalid_type_error: "statuses must be an array of strings",
|
||||
}
|
||||
},
|
||||
)
|
||||
.optional()
|
||||
.refine(
|
||||
(statuses) =>
|
||||
!statuses ||
|
||||
statuses.every((status) =>
|
||||
Object.values(CusProductStatus).includes(status)
|
||||
Object.values(CusProductStatus).includes(status),
|
||||
),
|
||||
{
|
||||
message: `statuses must contain only valid values: ${Object.values(CusProductStatus).join(", ")}`,
|
||||
}
|
||||
},
|
||||
),
|
||||
});
|
||||
|
||||
|
||||
@@ -61,13 +61,13 @@ export const getSingleEntityResponse = async ({
|
||||
p.internal_entity_id == entity.internal_id ||
|
||||
nullish(p.internal_entity_id)
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let entitySubs = (fullCus.subscriptions || []).filter((s: Subscription) =>
|
||||
entityCusProducts.some((p: FullCusProduct) =>
|
||||
p.subscription_ids?.includes(s.stripe_id || "")
|
||||
)
|
||||
p.subscription_ids?.includes(s.stripe_id || ""),
|
||||
),
|
||||
);
|
||||
|
||||
let { main, addOns } = await processFullCusProducts({
|
||||
@@ -151,7 +151,7 @@ export const getEntityResponse = async ({
|
||||
|
||||
for (const entityId of entityIds) {
|
||||
const entity = fullCus.entities.find(
|
||||
(e: Entity) => e.id == entityId || e.internal_id == entityId
|
||||
(e: Entity) => e.id == entityId || e.internal_id == entityId,
|
||||
);
|
||||
|
||||
if (!entity) {
|
||||
|
||||
@@ -52,7 +52,7 @@ export const handleGetEntity = async (req: any, res: any) =>
|
||||
logger,
|
||||
})
|
||||
: undefined,
|
||||
})
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -135,7 +135,7 @@ checkRouter.post("", async (req: any, res: any) => {
|
||||
|
||||
const { allowed, balance } = v2Response;
|
||||
const featureToUse = allFeatures.find(
|
||||
(f: Feature) => f.id === v2Response.feature_id
|
||||
(f: Feature) => f.id === v2Response.feature_id,
|
||||
);
|
||||
|
||||
if (allowed && req.isPublic !== true) {
|
||||
|
||||
@@ -151,7 +151,7 @@ export const getOptions = ({
|
||||
}
|
||||
|
||||
const currentOptions = cusProduct?.options.find(
|
||||
(o) => o.feature_id == i.feature_id
|
||||
(o) => o.feature_id == i.feature_id,
|
||||
);
|
||||
|
||||
let currentQuantity = currentOptions?.quantity;
|
||||
|
||||
@@ -22,7 +22,7 @@ const getFeatureAndCreditSystems = ({
|
||||
const { features } = req;
|
||||
|
||||
const feature: Feature | undefined = features.find(
|
||||
(feature: Feature) => feature.id === featureId
|
||||
(feature: Feature) => feature.id === featureId,
|
||||
);
|
||||
|
||||
const creditSystems = getCreditSystemsFromFeature({
|
||||
@@ -83,7 +83,7 @@ export const getCheckData = async ({ req }: { req: any }) => {
|
||||
cusEnt,
|
||||
entity: customer.entity!,
|
||||
features: allFeatures,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,12 +26,12 @@ export const getFeatureToUse = ({
|
||||
}) => {
|
||||
// 1. If there's a credit system
|
||||
let featureCusEnts = cusEnts.filter((cusEnt) =>
|
||||
cusEntMatchesFeature({ cusEnt, feature })
|
||||
cusEntMatchesFeature({ cusEnt, feature }),
|
||||
);
|
||||
|
||||
if (creditSystems.length > 0) {
|
||||
let creditCusEnts = cusEnts.filter((cusEnt) =>
|
||||
cusEntMatchesFeature({ cusEnt, feature: creditSystems[0] })
|
||||
cusEntMatchesFeature({ cusEnt, feature: creditSystems[0] }),
|
||||
);
|
||||
|
||||
if (creditCusEnts.length > 0) {
|
||||
@@ -75,7 +75,7 @@ export const getV2CheckResponse = async ({
|
||||
});
|
||||
|
||||
const featureCusEnts = cusEnts.filter((cusEnt) =>
|
||||
cusEntMatchesFeature({ cusEnt, feature: featureToUse })
|
||||
cusEntMatchesFeature({ cusEnt, feature: featureToUse }),
|
||||
);
|
||||
|
||||
const { unlimited, usageAllowed } = getUnlimitedAndUsageAllowed({
|
||||
@@ -84,7 +84,7 @@ export const getV2CheckResponse = async ({
|
||||
});
|
||||
|
||||
const cusPrices = cusProducts.flatMap(
|
||||
(cusProduct) => cusProduct.customer_prices
|
||||
(cusProduct) => cusProduct.customer_prices,
|
||||
);
|
||||
|
||||
const balances = await getCusBalances({
|
||||
|
||||
@@ -66,8 +66,8 @@ export class EventService {
|
||||
and(
|
||||
eq(events.internal_customer_id, internalCustomerId),
|
||||
eq(events.org_id, orgId),
|
||||
eq(events.env, env)
|
||||
)
|
||||
eq(events.env, env),
|
||||
),
|
||||
)
|
||||
.orderBy(desc(events.created_at))
|
||||
.limit(limit);
|
||||
|
||||
@@ -129,7 +129,7 @@ const getAffectedFeatures = async ({
|
||||
creditSystemContainsFeature({
|
||||
creditSystem: cs,
|
||||
meteredFeatureId: f.id,
|
||||
})
|
||||
}),
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -56,7 +56,7 @@ const getCusFeatureAndOrg = async ({
|
||||
creditSystemContainsFeature({
|
||||
creditSystem: f,
|
||||
meteredFeatureId: featureId,
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
if (!feature) {
|
||||
|
||||
@@ -58,7 +58,7 @@ export default async (req: any, res: any) =>
|
||||
{
|
||||
db,
|
||||
referralCodeId: referralCode.id,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (
|
||||
|
||||
@@ -37,7 +37,9 @@ export default async (req: any, res: any) =>
|
||||
try {
|
||||
await stripeCli.coupons.del(reward.id);
|
||||
} catch (error) {
|
||||
console.log(`Failed to delete coupon from stripe: ${(error as { message: string }).message}`);
|
||||
console.log(
|
||||
`Failed to delete coupon from stripe: ${(error as { message: string }).message}`,
|
||||
);
|
||||
}
|
||||
|
||||
await RewardService.delete({
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { RewardService } from "@/internal/rewards/RewardService.js";
|
||||
import { routeHandler } from "@/utils/routerUtils.js";
|
||||
|
||||
export default async (req: any, res: any) => routeHandler({
|
||||
export default async (req: any, res: any) =>
|
||||
routeHandler({
|
||||
req,
|
||||
res,
|
||||
action: "get reward",
|
||||
@@ -17,5 +18,5 @@ export default async (req: any, res: any) => routeHandler({
|
||||
});
|
||||
|
||||
res.status(200).json(reward);
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -3,4 +3,9 @@ import handleDeleteCoupon from "./handleDeleteCoupon.js";
|
||||
import handleGetCoupon from "./handleGetCoupon.js";
|
||||
import handleUpdateCoupon from "./handleUpdateCoupon.js";
|
||||
|
||||
export { handleCreateCoupon, handleDeleteCoupon, handleGetCoupon, handleUpdateCoupon };
|
||||
export {
|
||||
handleCreateCoupon,
|
||||
handleDeleteCoupon,
|
||||
handleGetCoupon,
|
||||
handleUpdateCoupon,
|
||||
};
|
||||
|
||||
@@ -88,5 +88,5 @@ rewardProgramRouter.put("/:id", (req, res) =>
|
||||
|
||||
return res.status(200).json(updatedRewardProgram);
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import express, { type Router } from "express";
|
||||
import { handleCreateCoupon, handleDeleteCoupon, handleGetCoupon, handleUpdateCoupon } from "./handlers/rewards/index.js";
|
||||
import {
|
||||
handleCreateCoupon,
|
||||
handleDeleteCoupon,
|
||||
handleGetCoupon,
|
||||
handleUpdateCoupon,
|
||||
} from "./handlers/rewards/index.js";
|
||||
|
||||
const rewardRouter: Router = express.Router();
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ trmnlRouter.post("/device_id", withOrgAuth, async (req: any, res: any) => {
|
||||
upstash = upstash!;
|
||||
|
||||
const trmnlConfig = (await upstash.get(
|
||||
`trmnl:device:${req.body.deviceId}`
|
||||
`trmnl:device:${req.body.deviceId}`,
|
||||
)) as {
|
||||
orgId: string;
|
||||
hideRevenue: boolean;
|
||||
@@ -229,7 +229,7 @@ trmnlRouter.post(
|
||||
hideRevenue: req.org.hideRevenue,
|
||||
});
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
export { trmnlRouter };
|
||||
|
||||
@@ -113,7 +113,7 @@ export class CusSearchService {
|
||||
// 1. Create base query to fetch all customerproducts
|
||||
let activeProdFilter = or(
|
||||
eq(customerProducts.status, CusProductStatus.Active),
|
||||
eq(customerProducts.status, CusProductStatus.PastDue)
|
||||
eq(customerProducts.status, CusProductStatus.PastDue),
|
||||
);
|
||||
|
||||
if (filters.status && filters.status.length > 0) {
|
||||
@@ -142,9 +142,9 @@ export class CusSearchService {
|
||||
...productVersionFilters.map((pv) =>
|
||||
and(
|
||||
eq(customerProducts.product_id, pv.productId),
|
||||
eq(products.version, pv.version)
|
||||
)
|
||||
)
|
||||
eq(products.version, pv.version),
|
||||
),
|
||||
),
|
||||
)
|
||||
: undefined,
|
||||
// Legacy product filtering (fallback)
|
||||
@@ -158,13 +158,13 @@ export class CusSearchService {
|
||||
case "canceled":
|
||||
return and(
|
||||
isNotNull(customerProducts.canceled_at),
|
||||
activeProdFilter
|
||||
activeProdFilter,
|
||||
);
|
||||
case "free_trial":
|
||||
return and(
|
||||
gt(customerProducts.trial_ends_at, Date.now()),
|
||||
isNotNull(customerProducts.free_trial_id),
|
||||
activeProdFilter
|
||||
activeProdFilter,
|
||||
);
|
||||
case CusProductStatus.Expired:
|
||||
return and(
|
||||
@@ -178,32 +178,32 @@ export class CusSearchService {
|
||||
and(
|
||||
eq(
|
||||
customerProductsAlias.internal_customer_id,
|
||||
customerProducts.internal_customer_id
|
||||
customerProducts.internal_customer_id,
|
||||
),
|
||||
eq(
|
||||
customerProductsAlias.product_id,
|
||||
customerProducts.product_id
|
||||
customerProducts.product_id,
|
||||
),
|
||||
or(
|
||||
eq(
|
||||
customerProductsAlias.status,
|
||||
CusProductStatus.Active
|
||||
CusProductStatus.Active,
|
||||
),
|
||||
eq(
|
||||
customerProductsAlias.status,
|
||||
CusProductStatus.PastDue
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
CusProductStatus.PastDue,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
default:
|
||||
return eq(customerProducts.status, status);
|
||||
}
|
||||
})
|
||||
}),
|
||||
)
|
||||
: undefined
|
||||
: undefined,
|
||||
);
|
||||
|
||||
let cusFilter = and(
|
||||
@@ -214,9 +214,9 @@ export class CusSearchService {
|
||||
? or(
|
||||
ilike(customers.id, `%${search}%`),
|
||||
ilike(customers.name, `%${search}%`),
|
||||
ilike(customers.email, `%${search}%`)
|
||||
ilike(customers.email, `%${search}%`),
|
||||
)
|
||||
: undefined
|
||||
: undefined,
|
||||
);
|
||||
|
||||
// Build the where clause
|
||||
@@ -232,7 +232,7 @@ export class CusSearchService {
|
||||
const whereClause = and(
|
||||
shouldApplyActiveFilter ? activeProdFilter : undefined,
|
||||
filtersDrizzle,
|
||||
cusFilter
|
||||
cusFilter,
|
||||
// resolvedLastItem && resolvedLastItem.internal_id
|
||||
// ? lt(customers.internal_id, resolvedLastItem.internal_id)
|
||||
// : undefined
|
||||
@@ -252,18 +252,18 @@ export class CusSearchService {
|
||||
.from(customerProducts)
|
||||
.leftJoin(
|
||||
customers,
|
||||
eq(customerProducts.internal_customer_id, customers.internal_id)
|
||||
eq(customerProducts.internal_customer_id, customers.internal_id),
|
||||
);
|
||||
|
||||
if (hasProductFilters) {
|
||||
return baseQuery.innerJoin(
|
||||
products,
|
||||
eq(customerProducts.internal_product_id, products.internal_id)
|
||||
eq(customerProducts.internal_product_id, products.internal_id),
|
||||
);
|
||||
} else {
|
||||
return baseQuery.leftJoin(
|
||||
products,
|
||||
eq(customerProducts.internal_product_id, products.internal_id)
|
||||
eq(customerProducts.internal_product_id, products.internal_id),
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -290,24 +290,24 @@ export class CusSearchService {
|
||||
const baseCountQuery = db
|
||||
.select({
|
||||
totalCount: sql<number>`count(distinct ${customers.internal_id})`.as(
|
||||
"total_count"
|
||||
"total_count",
|
||||
),
|
||||
})
|
||||
.from(customerProducts)
|
||||
.leftJoin(
|
||||
customers,
|
||||
eq(customerProducts.internal_customer_id, customers.internal_id)
|
||||
eq(customerProducts.internal_customer_id, customers.internal_id),
|
||||
);
|
||||
|
||||
if (hasProductFilters) {
|
||||
return baseCountQuery.innerJoin(
|
||||
products,
|
||||
eq(customerProducts.internal_product_id, products.internal_id)
|
||||
eq(customerProducts.internal_product_id, products.internal_id),
|
||||
);
|
||||
} else {
|
||||
return baseCountQuery.leftJoin(
|
||||
products,
|
||||
eq(customerProducts.internal_product_id, products.internal_id)
|
||||
eq(customerProducts.internal_product_id, products.internal_id),
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -318,8 +318,8 @@ export class CusSearchService {
|
||||
and(
|
||||
shouldApplyActiveFilter ? activeProdFilter : undefined,
|
||||
filtersDrizzle,
|
||||
cusFilter
|
||||
)
|
||||
cusFilter,
|
||||
),
|
||||
),
|
||||
]);
|
||||
|
||||
@@ -379,10 +379,10 @@ export class CusSearchService {
|
||||
or(
|
||||
eq(customerProducts.status, CusProductStatus.Active),
|
||||
eq(customerProducts.status, CusProductStatus.PastDue),
|
||||
eq(customerProducts.status, CusProductStatus.Scheduled)
|
||||
)
|
||||
)
|
||||
)
|
||||
eq(customerProducts.status, CusProductStatus.Scheduled),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
const baseWhereClause = and(
|
||||
@@ -392,10 +392,10 @@ export class CusSearchService {
|
||||
? or(
|
||||
ilike(customers.id, `%${search}%`),
|
||||
ilike(customers.name, `%${search}%`),
|
||||
ilike(customers.email, `%${search}%`)
|
||||
ilike(customers.email, `%${search}%`),
|
||||
)
|
||||
: undefined,
|
||||
noneFilter
|
||||
noneFilter,
|
||||
);
|
||||
|
||||
let baseQuery;
|
||||
@@ -434,11 +434,11 @@ export class CusSearchService {
|
||||
? or(
|
||||
ilike(customers.id, `%${search}%`),
|
||||
ilike(customers.name, `%${search}%`),
|
||||
ilike(customers.email, `%${search}%`)
|
||||
ilike(customers.email, `%${search}%`),
|
||||
)
|
||||
: undefined,
|
||||
noneFilter
|
||||
)
|
||||
noneFilter,
|
||||
),
|
||||
),
|
||||
]);
|
||||
|
||||
@@ -482,8 +482,8 @@ export class CusSearchService {
|
||||
and(
|
||||
eq(customers.internal_id, lastItem.internal_id),
|
||||
eq(customers.org_id, orgId),
|
||||
eq(customers.env, env)
|
||||
)
|
||||
eq(customers.env, env),
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
@@ -533,9 +533,9 @@ export class CusSearchService {
|
||||
? or(
|
||||
ilike(customers.id, `%${search}%`),
|
||||
ilike(customers.name, `%${search}%`),
|
||||
ilike(customers.email, `%${search}%`)
|
||||
ilike(customers.email, `%${search}%`),
|
||||
)
|
||||
: undefined
|
||||
: undefined,
|
||||
);
|
||||
|
||||
// Build the where clause for base query
|
||||
@@ -543,7 +543,7 @@ export class CusSearchService {
|
||||
filterClause,
|
||||
resolvedLastItem && resolvedLastItem.internal_id
|
||||
? lt(customers.internal_id, resolvedLastItem.internal_id)
|
||||
: undefined
|
||||
: undefined,
|
||||
);
|
||||
|
||||
// Create the base customer query as a subquery with appropriate pagination
|
||||
@@ -598,11 +598,11 @@ export class CusSearchService {
|
||||
.from(baseQuery)
|
||||
.leftJoin(
|
||||
customerProducts,
|
||||
eq(baseQuery.internal_id, customerProducts.internal_customer_id)
|
||||
eq(baseQuery.internal_id, customerProducts.internal_customer_id),
|
||||
)
|
||||
.leftJoin(
|
||||
products,
|
||||
eq(customerProducts.internal_product_id, products.internal_id)
|
||||
eq(customerProducts.internal_product_id, products.internal_id),
|
||||
)
|
||||
.orderBy(desc(baseQuery.internal_id)),
|
||||
totalCountQuery,
|
||||
|
||||
@@ -73,7 +73,7 @@ export class CusService {
|
||||
withTrialsUsed,
|
||||
withSubs,
|
||||
withEvents,
|
||||
entityId
|
||||
entityId,
|
||||
);
|
||||
|
||||
let result = await db.execute(query);
|
||||
@@ -124,10 +124,10 @@ export class CusService {
|
||||
where: and(
|
||||
or(
|
||||
eq(customers.id, idOrInternalId),
|
||||
eq(customers.internal_id, idOrInternalId)
|
||||
eq(customers.internal_id, idOrInternalId),
|
||||
),
|
||||
eq(customers.org_id, orgId),
|
||||
eq(customers.env, env)
|
||||
eq(customers.env, env),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -153,7 +153,7 @@ export class CusService {
|
||||
where: and(
|
||||
ilike(customers.email, email),
|
||||
eq(customers.org_id, orgId),
|
||||
eq(customers.env, env)
|
||||
eq(customers.env, env),
|
||||
),
|
||||
});
|
||||
|
||||
@@ -276,8 +276,8 @@ export class CusService {
|
||||
and(
|
||||
eq(customers.internal_id, internalId),
|
||||
eq(customers.org_id, orgId),
|
||||
eq(customers.env, env)
|
||||
)
|
||||
eq(customers.env, env),
|
||||
),
|
||||
)
|
||||
.returning();
|
||||
|
||||
|
||||
@@ -201,7 +201,7 @@ export const expireOrDeleteCusProduct = async ({
|
||||
cp.status === CusProductStatus.Scheduled &&
|
||||
(internalEntityId
|
||||
? cp.internal_entity_id === internalEntityId
|
||||
: nullish(cp.internal_entity_id))
|
||||
: nullish(cp.internal_entity_id)),
|
||||
);
|
||||
|
||||
if (curScheduledProduct) {
|
||||
@@ -354,7 +354,7 @@ export const createFullCusProduct = async ({
|
||||
|
||||
const cusProdId = generateId("cus_prod");
|
||||
logger.info(
|
||||
`Inserting cus product ${product.id} for ${customer.name}, cus product ID: ${cusProdId}`
|
||||
`Inserting cus product ${product.id} for ${customer.name}, cus product ID: ${cusProdId}`,
|
||||
);
|
||||
logger.info(productOptions);
|
||||
|
||||
@@ -499,7 +499,7 @@ export const createFullCusProduct = async ({
|
||||
db,
|
||||
rows: operation.toInsert,
|
||||
fullCusEnt: operation.cusEnt,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -521,7 +521,7 @@ export const createFullCusProduct = async ({
|
||||
// db,
|
||||
// cusEntID: ce.id,
|
||||
// }),
|
||||
}))
|
||||
})),
|
||||
);
|
||||
|
||||
const fullCusProduct = {
|
||||
|
||||
@@ -69,13 +69,13 @@ export const handleCreateCheckout = async ({
|
||||
getNextStartOfMonthUnix({
|
||||
interval: itemSets[0].interval,
|
||||
intervalCount: itemSets[0].intervalCount,
|
||||
}) / 1000
|
||||
}) / 1000,
|
||||
)
|
||||
: undefined;
|
||||
|
||||
if (attachParams.billingAnchor) {
|
||||
billingCycleAnchorUnixSeconds = Math.floor(
|
||||
attachParams.billingAnchor / 1000
|
||||
attachParams.billingAnchor / 1000,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ export const handleCreateCheckout = async ({
|
||||
try {
|
||||
checkout = await stripeCli.checkout.sessions.create(sessionParams);
|
||||
logger.info(
|
||||
`✅ Successfully created checkout for customer ${customer.id || customer.internal_id}`
|
||||
`✅ Successfully created checkout for customer ${customer.id || customer.internal_id}`,
|
||||
);
|
||||
} catch (error: any) {
|
||||
let msg = error.message;
|
||||
@@ -177,7 +177,7 @@ export const handleCreateCheckout = async ({
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`✅ Created fallback checkout session with card payment method for customer ${customer.id || customer.internal_id}`
|
||||
`✅ Created fallback checkout session with card payment method for customer ${customer.id || customer.internal_id}`,
|
||||
);
|
||||
} else {
|
||||
throw error;
|
||||
@@ -199,7 +199,7 @@ export const handleCreateCheckout = async ({
|
||||
}, product(s) ${attachParams.products.map((p) => p.name).join(", ")}`,
|
||||
product_ids: attachParams.products.map((p) => p.id),
|
||||
customer_id: customer.id || customer.internal_id,
|
||||
})
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
res.status(200).json({
|
||||
|
||||
@@ -95,7 +95,7 @@ export const handleCreateInvoiceCheckout = async ({
|
||||
product_ids: attachParams.products.map((p) => p.id),
|
||||
customer_id:
|
||||
attachParams.customer.id || attachParams.customer.internal_id,
|
||||
})
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -109,7 +109,7 @@ export const handleCreateInvoiceCheckout = async ({
|
||||
product_ids: attachParams.products.map((p) => p.id),
|
||||
customer_id:
|
||||
attachParams.customer.id || attachParams.customer.internal_id,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ export const initCusEntEntities = ({
|
||||
resetBalance?: number | null;
|
||||
}) => {
|
||||
let newEntities: Record<string, EntityBalance> | null = notNullish(
|
||||
entitlement.entity_feature_id
|
||||
entitlement.entity_feature_id,
|
||||
)
|
||||
? {}
|
||||
: null;
|
||||
|
||||
@@ -139,7 +139,7 @@ export const createStripeSub2 = async ({
|
||||
latestInvoice.status === "draft"
|
||||
) {
|
||||
subscription.latest_invoice = await stripeCli.invoices.finalizeInvoice(
|
||||
(subscription.latest_invoice as Stripe.Invoice).id!
|
||||
(subscription.latest_invoice as Stripe.Invoice).id!,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ export const getMergeCusProduct = async ({
|
||||
let mergeCusProduct = undefined;
|
||||
if (!config.disableMerge && !freeTrial) {
|
||||
mergeCusProduct = cusProducts?.find((cp) =>
|
||||
products.some((p) => p.group == cp.product.group)
|
||||
products.some((p) => p.group == cp.product.group),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ export const handleOneOffFunction = async ({
|
||||
if (config.invoiceCheckout) {
|
||||
if (stripeInvoice.status === "draft" && config.finalizeInvoice) {
|
||||
stripeInvoice = await stripeCli.invoices.finalizeInvoice(
|
||||
stripeInvoice.id!
|
||||
stripeInvoice.id!,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ export const handleOneOffFunction = async ({
|
||||
db: req.db,
|
||||
attachParams: attachToInsertParams(attachParams, product),
|
||||
logger,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
await Promise.all(batchInsert);
|
||||
@@ -226,7 +226,7 @@ export const handleOneOffFunction = async ({
|
||||
product_ids: products.map((p) => p.id),
|
||||
customer_id: customer.id || customer.internal_id,
|
||||
scenario: AttachScenario.New,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -253,7 +253,7 @@ export const handlePaidProduct = async ({
|
||||
}) || 0) * 1000
|
||||
: undefined),
|
||||
logger,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
await Promise.all(batchInsert);
|
||||
@@ -272,7 +272,7 @@ export const handlePaidProduct = async ({
|
||||
invoice: invoiceOnly
|
||||
? attachToInvoiceResponse({ invoice })
|
||||
: undefined,
|
||||
})
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
res.status(200).json({
|
||||
|
||||
@@ -53,7 +53,7 @@ export const handleRenewProduct = async ({
|
||||
code: SuccessCode.RenewedProduct,
|
||||
message: `Successfully renewed product ${product.name}`,
|
||||
product_ids: [product.id],
|
||||
})
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -63,7 +63,7 @@ export const handleRenewProduct = async ({
|
||||
(cp) =>
|
||||
cp.subscription_ids?.includes(curSubId!) &&
|
||||
cp.canceled &&
|
||||
cp.id !== curCusProduct?.id
|
||||
cp.id !== curCusProduct?.id,
|
||||
);
|
||||
|
||||
let expectedEnd = undefined;
|
||||
@@ -73,7 +73,7 @@ export const handleRenewProduct = async ({
|
||||
subId: curSubId,
|
||||
});
|
||||
const subItems = curSub?.items.data.filter((item) =>
|
||||
subItemInCusProduct({ cusProduct: curCusProduct!, subItem: item })
|
||||
subItemInCusProduct({ cusProduct: curCusProduct!, subItem: item }),
|
||||
);
|
||||
expectedEnd = getLatestPeriodEnd({ subItems });
|
||||
}
|
||||
@@ -117,7 +117,7 @@ export const handleRenewProduct = async ({
|
||||
// Case 1: Add current cus product back to schedule and remove scheduled product from schedule
|
||||
if (schedule) {
|
||||
logger.info(
|
||||
`RENEW FLOW: adding cur cus product back to schedule ${schedule.id}`
|
||||
`RENEW FLOW: adding cur cus product back to schedule ${schedule.id}`,
|
||||
);
|
||||
const newItems = await paramsToScheduleItems({
|
||||
req,
|
||||
@@ -149,7 +149,7 @@ export const handleRenewProduct = async ({
|
||||
});
|
||||
} else {
|
||||
logger.info(
|
||||
`RENEW FLOW: no new schedule items, releasing schedule ${schedule.id}`
|
||||
`RENEW FLOW: no new schedule items, releasing schedule ${schedule.id}`,
|
||||
);
|
||||
await stripeCli.subscriptionSchedules.release(schedule.id);
|
||||
|
||||
@@ -233,7 +233,7 @@ export const handleRenewProduct = async ({
|
||||
product_ids: [product.id],
|
||||
customer_id:
|
||||
attachParams.customer.id || attachParams.customer.internal_id,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -46,14 +46,14 @@ export const handleInvoiceCheckoutPaid = async ({
|
||||
console.log("Inserting products list");
|
||||
for (const productOptions of attachParams.productsList) {
|
||||
const product = attachParams.products.find(
|
||||
(p) => p.id === productOptions.product_id
|
||||
(p) => p.id === productOptions.product_id,
|
||||
);
|
||||
|
||||
if (!product) {
|
||||
logger.error(
|
||||
`checkout.completed: product not found for productOptions: ${JSON.stringify(
|
||||
productOptions
|
||||
)}`
|
||||
productOptions,
|
||||
)}`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -63,7 +63,7 @@ export const handleInvoiceCheckoutPaid = async ({
|
||||
attachParams: attachToInsertParams(
|
||||
attachParams,
|
||||
product,
|
||||
productOptions.entity_id || undefined
|
||||
productOptions.entity_id || undefined,
|
||||
),
|
||||
subscriptionIds: subIds,
|
||||
anchorToUnix,
|
||||
@@ -84,7 +84,7 @@ export const handleInvoiceCheckoutPaid = async ({
|
||||
carryExistingUsages: config.carryUsage,
|
||||
scenario: AttachScenario.New,
|
||||
logger: req.logger,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -92,6 +92,6 @@ export const handleInvoiceCheckoutPaid = async ({
|
||||
}
|
||||
|
||||
req.logger.info(
|
||||
`✅ invoice.paid, successfully inserted cus products: ${attachParams.products.map((p) => p.id).join(", ")}`
|
||||
`✅ invoice.paid, successfully inserted cus products: ${attachParams.products.map((p) => p.id).join(", ")}`,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -56,11 +56,11 @@ export const getAddAndRemoveProducts = async ({
|
||||
|
||||
for (const productOptions of productsList) {
|
||||
const product = attachParams.products.find(
|
||||
(p) => p.id === productOptions.product_id
|
||||
(p) => p.id === productOptions.product_id,
|
||||
);
|
||||
|
||||
const entity = attachParams.customer.entities.find(
|
||||
(e) => e.id === productOptions.entity_id
|
||||
(e) => e.id === productOptions.entity_id,
|
||||
);
|
||||
|
||||
const { curSameProduct, curScheduledProduct } = getExistingCusProducts({
|
||||
|
||||
@@ -170,7 +170,7 @@ export const handleMultiAttachFlow = async ({
|
||||
});
|
||||
for (const productOptions of newProdList) {
|
||||
const product = attachParams.products.find(
|
||||
(p) => p.id === productOptions.product_id
|
||||
(p) => p.id === productOptions.product_id,
|
||||
)!;
|
||||
|
||||
if (productOptions.quantity === 0) continue;
|
||||
@@ -185,7 +185,7 @@ export const handleMultiAttachFlow = async ({
|
||||
attachParams: attachToInsertParams(
|
||||
attachParams,
|
||||
product,
|
||||
productOptions.entity_id || undefined
|
||||
productOptions.entity_id || undefined,
|
||||
),
|
||||
subscriptionIds: curSub ? [curSub?.id!] : undefined,
|
||||
anchorToUnix,
|
||||
@@ -196,7 +196,7 @@ export const handleMultiAttachFlow = async ({
|
||||
mergeCusProduct && isTrialing({ cusProduct: mergeCusProduct })
|
||||
? mergeCusProduct?.trial_ends_at!
|
||||
: undefined,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -214,8 +214,8 @@ export const handleMultiAttachFlow = async ({
|
||||
invoice: attachParams.invoiceOnly
|
||||
? attachToInvoiceResponse({ invoice })
|
||||
: undefined,
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -48,7 +48,7 @@ export const handleScheduleFunction2 = async ({
|
||||
const curCusProduct = attachParamsToCurCusProduct({ attachParams });
|
||||
const curSub = await paramsToCurSub({ attachParams });
|
||||
const subItems = curSub?.items.data.filter((item) =>
|
||||
subItemInCusProduct({ cusProduct: curCusProduct!, subItem: item })
|
||||
subItemInCusProduct({ cusProduct: curCusProduct!, subItem: item }),
|
||||
);
|
||||
|
||||
const expectedEnd = getLatestPeriodEnd({ subItems });
|
||||
@@ -74,7 +74,7 @@ export const handleScheduleFunction2 = async ({
|
||||
|
||||
if (currentPhaseIndex == newItems.phases.length - 1) {
|
||||
logger.info(
|
||||
`SCHEDULE FLOW: no subsequent phases, releasing schedule ${schedule?.id}`
|
||||
`SCHEDULE FLOW: no subsequent phases, releasing schedule ${schedule?.id}`,
|
||||
);
|
||||
await stripeCli.subscriptionSchedules.release(schedule!.id);
|
||||
await CusProductService.updateByStripeScheduledId({
|
||||
@@ -195,7 +195,7 @@ export const handleScheduleFunction2 = async ({
|
||||
product_ids: [product.id],
|
||||
customer_id:
|
||||
attachParams.customer.id || attachParams.customer.internal_id,
|
||||
})
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
res.status(200).json({
|
||||
|
||||
@@ -73,7 +73,7 @@ export const handleQuantityDowngrade = async ({
|
||||
.minus(
|
||||
notNullish(oldOptions.upcoming_quantity)
|
||||
? oldOptions.upcoming_quantity!
|
||||
: oldOptions.quantity
|
||||
: oldOptions.quantity,
|
||||
)
|
||||
.toNumber();
|
||||
|
||||
@@ -117,7 +117,7 @@ export const handleQuantityDowngrade = async ({
|
||||
|
||||
const product = cusProductToProduct({ cusProduct });
|
||||
const feature = req.features.find(
|
||||
(f: Feature) => f.internal_id == newOptions.internal_feature_id
|
||||
(f: Feature) => f.internal_id == newOptions.internal_feature_id,
|
||||
)!;
|
||||
const invoiceItem = constructStripeInvoiceItem({
|
||||
product,
|
||||
@@ -135,13 +135,13 @@ export const handleQuantityDowngrade = async ({
|
||||
stripeSubId: stripeSub.id,
|
||||
stripeCustomerId: stripeSub.customer as string,
|
||||
periodStart: Math.floor(
|
||||
attachParams.now ? attachParams.now / 1000 : Date.now()
|
||||
attachParams.now ? attachParams.now / 1000 : Date.now(),
|
||||
),
|
||||
periodEnd: Math.floor(end * 1000),
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`🔥 Creating downgrade prepaid invoice item: ${invoiceItem.description} - ${amount}`
|
||||
`🔥 Creating downgrade prepaid invoice item: ${invoiceItem.description} - ${amount}`,
|
||||
);
|
||||
|
||||
await stripeCli.invoiceItems.create(invoiceItem);
|
||||
|
||||
@@ -62,7 +62,7 @@ export const handleQuantityUpgrade = async ({
|
||||
.minus(
|
||||
notNullish(oldOptions.upcoming_quantity)
|
||||
? oldOptions.upcoming_quantity!
|
||||
: oldOptions.quantity
|
||||
: oldOptions.quantity,
|
||||
)
|
||||
.toNumber();
|
||||
|
||||
@@ -114,7 +114,7 @@ export const handleQuantityUpgrade = async ({
|
||||
// });
|
||||
|
||||
const feature = features.find(
|
||||
(f: Feature) => f.internal_id == newOptions.internal_feature_id
|
||||
(f: Feature) => f.internal_id == newOptions.internal_feature_id,
|
||||
)!;
|
||||
|
||||
const product = cusProductToProduct({ cusProduct });
|
||||
@@ -138,7 +138,7 @@ export const handleQuantityUpgrade = async ({
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`🔥 Creating prepaid invoice item: ${invoiceItem.description} - ${amount}`
|
||||
`🔥 Creating prepaid invoice item: ${invoiceItem.description} - ${amount}`,
|
||||
);
|
||||
|
||||
await stripeCli.invoiceItems.create(invoiceItem);
|
||||
@@ -192,7 +192,7 @@ export const handleQuantityUpgrade = async ({
|
||||
if (cusEnt) {
|
||||
const incrementBy = new Decimal(difference).mul(billingUnits).toNumber();
|
||||
logger.info(
|
||||
`🔥 Incrementing feature ${cusEnt.entitlement.feature.id} balance by ${incrementBy}`
|
||||
`🔥 Incrementing feature ${cusEnt.entitlement.feature.id} balance by ${incrementBy}`,
|
||||
);
|
||||
await CusEntService.increment({
|
||||
db,
|
||||
|
||||
@@ -155,7 +155,7 @@ export const createUsageInvoiceItems = async ({
|
||||
const invoiceItem = invoiceItems[i];
|
||||
const createInvoiceItem = async () => {
|
||||
logger.info(
|
||||
`🌟 Creating usage invoice item: ${invoiceItem.description}, amount: ${invoiceItem.price_data.unit_amount}`
|
||||
`🌟 Creating usage invoice item: ${invoiceItem.description}, amount: ${invoiceItem.price_data.unit_amount}`,
|
||||
);
|
||||
|
||||
await stripeCli.invoiceItems.create({
|
||||
@@ -195,7 +195,7 @@ export const resetUsageBalances = async ({
|
||||
});
|
||||
|
||||
let index = cusProduct.customer_entitlements.findIndex(
|
||||
(ce) => ce.id === cusEntId
|
||||
(ce) => ce.id === cusEntId,
|
||||
);
|
||||
|
||||
cusProduct.customer_entitlements[index] = {
|
||||
|
||||
@@ -109,7 +109,7 @@ export const handleUpgradeFlow = async ({
|
||||
// Do something about current sub...
|
||||
} else if (shouldCancelSub({ sub: curSub!, newSubItems: subItems })) {
|
||||
logger.info(
|
||||
`UPGRADE FLOW: canceling sub ${curSub!.id}, proration: ${config.proration}`
|
||||
`UPGRADE FLOW: canceling sub ${curSub!.id}, proration: ${config.proration}`,
|
||||
);
|
||||
canceled = true;
|
||||
const { stripeCli } = attachParams;
|
||||
@@ -201,7 +201,7 @@ export const handleUpgradeFlow = async ({
|
||||
db: req.db,
|
||||
attachParams: attachToInsertParams(
|
||||
attachParams,
|
||||
attachParams.products[0]
|
||||
attachParams.products[0],
|
||||
),
|
||||
subscriptionIds: curCusProduct!.subscription_ids || [],
|
||||
disableFreeTrial: config.disableTrial,
|
||||
@@ -228,7 +228,7 @@ export const handleUpgradeFlow = async ({
|
||||
: undefined,
|
||||
code: "updated_product_successfully",
|
||||
message: `Successfully updated product`,
|
||||
})
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
res.status(200).json({
|
||||
|
||||
@@ -74,7 +74,7 @@ export const handleUpgradeFlowSchedule = async ({
|
||||
// pro, pro -> free, pro -> premium, pro (need to cancel initial schedule)
|
||||
if (newCurPhaseIndex == newItems.phases.length - 1) {
|
||||
logger.info(
|
||||
`UPGRADE FLOW: no subsequent phases, releasing schedule ${schedule?.id}`
|
||||
`UPGRADE FLOW: no subsequent phases, releasing schedule ${schedule?.id}`,
|
||||
);
|
||||
await stripeCli.subscriptionSchedules.release(schedule!.id);
|
||||
await CusProductService.updateByStripeScheduledId({
|
||||
@@ -91,7 +91,7 @@ export const handleUpgradeFlowSchedule = async ({
|
||||
(cp) =>
|
||||
cp.id !== curCusProduct?.id &&
|
||||
cp.subscription_ids?.includes(curSub.id) &&
|
||||
ACTIVE_STATUSES.includes(cp.status)
|
||||
ACTIVE_STATUSES.includes(cp.status),
|
||||
)
|
||||
.every((cp) => cp.canceled) && isFreeProduct(prices);
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ export const priceToNewPreviewItem = ({
|
||||
const applyRewards = rewards?.filter(
|
||||
(r) =>
|
||||
r.discount_config?.price_ids?.includes(price.id) ||
|
||||
r.discount_config?.apply_to_all
|
||||
r.discount_config?.apply_to_all,
|
||||
);
|
||||
|
||||
for (const reward of applyRewards ?? []) {
|
||||
|
||||
@@ -125,7 +125,7 @@ export const handlePublicAttachErrors = async ({
|
||||
|
||||
// 1. If on paid plan, not allowed to switch product
|
||||
const curProductFree = isFreeProduct(
|
||||
curCusProduct?.customer_prices.map((cp: any) => cp.price) || [] // if no current product...
|
||||
curCusProduct?.customer_prices.map((cp: any) => cp.price) || [], // if no current product...
|
||||
);
|
||||
|
||||
if (!curProductFree) {
|
||||
@@ -180,7 +180,7 @@ export const checkStripeConnections = async ({
|
||||
env,
|
||||
customer,
|
||||
logger,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ export const checkStripeConnections = async ({
|
||||
env,
|
||||
product,
|
||||
logger,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
await Promise.all(batchProductUpdates);
|
||||
@@ -236,7 +236,7 @@ export const createStripePrices = async ({
|
||||
logger,
|
||||
internalEntityId: attachParams.internalEntityId,
|
||||
useCheckout,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
await Promise.all(batchPriceUpdates);
|
||||
|
||||
@@ -46,7 +46,7 @@ const getProductsForAttach = async ({
|
||||
if (prod.is_add_on) continue;
|
||||
|
||||
let otherProd = products.find(
|
||||
(p) => p.group === prod.group && !p.is_add_on && p.id !== prod.id
|
||||
(p) => p.group === prod.group && !p.is_add_on && p.id !== prod.id,
|
||||
);
|
||||
|
||||
if (otherProd && !otherProd.is_add_on && !isOneOff(prod.prices)) {
|
||||
|
||||
@@ -40,7 +40,7 @@ export const getRewards = async ({
|
||||
|
||||
for (const reward of rewardArray) {
|
||||
const corresponding = rewards.find(
|
||||
(r) => r.id === reward || r.promo_codes.some((c) => c.code === reward)
|
||||
(r) => r.id === reward || r.promo_codes.some((c) => c.code === reward),
|
||||
);
|
||||
|
||||
if (!corresponding) {
|
||||
|
||||
@@ -81,7 +81,7 @@ export const isMainTrialBranch = ({
|
||||
(cp) =>
|
||||
cp.id !== curMainProduct!.id &&
|
||||
ACTIVE_STATUSES.includes(cp.status) &&
|
||||
cp.subscription_ids?.includes(subId)
|
||||
cp.subscription_ids?.includes(subId),
|
||||
);
|
||||
|
||||
if (otherCusProductsOnSub.length > 1) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user