From 4aa6df1d958eff8c7eb17401b8c7a14b281e9ff1 Mon Sep 17 00:00:00 2001 From: John Yeo Date: Tue, 3 Jun 2025 08:52:43 +0100 Subject: [PATCH] fixed fingerprint across product versions and free trial search --- .../internal/customers/CusSearchService.ts | 21 +---- .../customers/products/AttachParams.ts | 1 + .../customers/products/CusProductService.ts | 37 ++++++--- .../customers/products/attachUtils.ts | 3 + .../products/free-trials/freeTrialUtils.ts | 83 +++---------------- server/tsconfig.json | 4 +- shared/models/productModels/productModels.ts | 1 + 7 files changed, 49 insertions(+), 101 deletions(-) diff --git a/server/src/internal/customers/CusSearchService.ts b/server/src/internal/customers/CusSearchService.ts index 56ab55c2a..f0a8f420d 100644 --- a/server/src/internal/customers/CusSearchService.ts +++ b/server/src/internal/customers/CusSearchService.ts @@ -1,20 +1,8 @@ import { DrizzleCli } from "@/db/initDrizzle.js"; -import { Client } from "pg"; + import { AppEnv, customers, CusProductStatus } from "@autumn/shared"; -import { SupabaseClient } from "@supabase/supabase-js"; -import { - and, - desc, - eq, - ilike, - or, - count, - lt, - inArray, - isNotNull, - gt, - sql, -} from "drizzle-orm"; + +import { and, desc, eq, ilike, or, lt, isNotNull, gt, sql } from "drizzle-orm"; import { customerProducts, products } from "@autumn/shared"; const customerFields = { internal_id: customers.internal_id, @@ -68,14 +56,13 @@ export class CusSearchService { filters.product_id ? eq(customerProducts.product_id, filters.product_id) : undefined, - filters.status ? eq(customerProducts.status, filters.status) : undefined, filters.status === "canceled" ? and(activeProdFilter, isNotNull(customerProducts.canceled_at)) : undefined, filters.status === "free_trial" ? and( - eq(customerProducts.status, CusProductStatus.Active), gt(customerProducts.trial_ends_at, Date.now()), + isNotNull(customerProducts.free_trial_id), ) : undefined, ); diff --git a/server/src/internal/customers/products/AttachParams.ts b/server/src/internal/customers/products/AttachParams.ts index b39cc26ac..f6ea86f2f 100644 --- a/server/src/internal/customers/products/AttachParams.ts +++ b/server/src/internal/customers/products/AttachParams.ts @@ -15,6 +15,7 @@ import { import { z } from "zod"; export type AttachParams = { + freeTrialIds: string[]; org: Organization; customer: Customer; products: FullProduct[]; diff --git a/server/src/internal/customers/products/CusProductService.ts b/server/src/internal/customers/products/CusProductService.ts index c8c2f4c9e..1633437b7 100644 --- a/server/src/internal/customers/products/CusProductService.ts +++ b/server/src/internal/customers/products/CusProductService.ts @@ -13,7 +13,16 @@ import { import { customerProducts } from "@autumn/shared"; -import { and, arrayContains, eq, inArray, or, sql } from "drizzle-orm"; +import { + and, + arrayContains, + eq, + inArray, + isNotNull, + ne, + or, + sql, +} from "drizzle-orm"; export const ACTIVE_STATUSES = [ CusProductStatus.Active, @@ -474,12 +483,14 @@ export class CusProductService { static async getByFingerprint({ db, - freeTrialId, + productId, + internalCustomerId, fingerprint, }: { db: DrizzleCli; - freeTrialId: string; - fingerprint: string; + productId: string; + internalCustomerId: string; + fingerprint?: string; }) { let data = await db .select() @@ -488,20 +499,22 @@ export class CusProductService { customers, eq(customerProducts.internal_customer_id, customers.internal_id), ) + .innerJoin( + products, + eq(customerProducts.internal_product_id, products.internal_id), + ) .where( and( - eq(customers.fingerprint, fingerprint), - eq(customerProducts.free_trial_id, freeTrialId), + or( + fingerprint ? eq(customers.fingerprint, fingerprint) : undefined, + eq(customers.internal_id, internalCustomerId), + ), + eq(products.id, productId), + isNotNull(customerProducts.free_trial_id), ), ); return data; - - // const { data, error } = await sb - // .from("customer_products") - // .select("*, customer:customers!inner(*)") - // .eq("free_trial_id", freeTrialId) - // .eq("customer.fingerprint", fingerprint); } static async getByTrialAndCustomer({ diff --git a/server/src/internal/customers/products/attachUtils.ts b/server/src/internal/customers/products/attachUtils.ts index f96f6edba..465988002 100644 --- a/server/src/internal/customers/products/attachUtils.ts +++ b/server/src/internal/customers/products/attachUtils.ts @@ -342,10 +342,12 @@ export const getFullCusProductData = async ({ if (!isCustom) { let freeTrial = null; let freeTrialProduct = products.find((p) => notNullish(p.free_trial)); + if (freeTrialProduct) { freeTrial = await getFreeTrialAfterFingerprint({ db, freeTrial: freeTrialProduct.free_trial, + productId: freeTrialProduct.id, fingerprint: customer.fingerprint, internalCustomerId: customer.internal_id, multipleAllowed: org.config.multiple_trials, @@ -432,6 +434,7 @@ export const getFullCusProductData = async ({ const uniqueFreeTrial = await getFreeTrialAfterFingerprint({ db, freeTrial: freeTrial, + productId: product.id, fingerprint: customer.fingerprint, internalCustomerId: customer.internal_id, multipleAllowed: org.config.multiple_trials, diff --git a/server/src/internal/products/free-trials/freeTrialUtils.ts b/server/src/internal/products/free-trials/freeTrialUtils.ts index b28d71f69..a79ddc547 100644 --- a/server/src/internal/products/free-trials/freeTrialUtils.ts +++ b/server/src/internal/products/free-trials/freeTrialUtils.ts @@ -81,64 +81,17 @@ export const freeTrialToStripeTimestamp = (freeTrial: FreeTrial | null) => { return Math.ceil(trialEnd.getTime() / 1000); }; -export const freeTrialToNumDays = (freeTrial: FreeTrial | null) => { - if (!freeTrial) return undefined; - return freeTrial.length; -}; - -export const trialFingerprintExists = async ({ - db, - freeTrialId, - fingerprint, -}: { - db: DrizzleCli; - freeTrialId: string; - fingerprint: string; -}) => { - const data = await CusProductService.getByFingerprint({ - db, - freeTrialId, - fingerprint, - }); - - if (data && data.length > 0) { - return true; - } - - return false; -}; - -export const trialWithCustomerExists = async ({ - db, - internalCustomerId, - freeTrialId, -}: { - db: DrizzleCli; - internalCustomerId: string; - freeTrialId: string; -}) => { - const data = await CusProductService.getByFingerprint({ - db, - freeTrialId, - fingerprint: internalCustomerId, - }); - - if (data && data.length > 0) { - return true; - } - - return false; -}; - export const getFreeTrialAfterFingerprint = async ({ db, freeTrial, + productId, fingerprint, internalCustomerId, multipleAllowed, }: { db: DrizzleCli; freeTrial: FreeTrial | null | undefined; + productId: string; fingerprint: string | null | undefined; internalCustomerId: string; multipleAllowed: boolean; @@ -150,31 +103,19 @@ export const getFreeTrialAfterFingerprint = async ({ } let uniqueFreeTrial: FreeTrial | null = freeTrial; - if (uniqueFreeTrial.unique_fingerprint && fingerprint) { - let exists = await trialFingerprintExists({ - db, - fingerprint, - freeTrialId: uniqueFreeTrial.id, - }); - if (exists) { - console.log("Free trial fingerprint exists"); - uniqueFreeTrial = null; - } - } + const data = await CusProductService.getByFingerprint({ + db, + productId, + internalCustomerId, + fingerprint: uniqueFreeTrial.unique_fingerprint ? fingerprint! : undefined, + }); - if (uniqueFreeTrial) { - // Check if same customer exists - let exists = await trialWithCustomerExists({ - db, - internalCustomerId, - freeTrialId: uniqueFreeTrial.id, - }); + const exists = data && data.length > 0; - if (exists) { - console.log("Free trial with customer exists"); - uniqueFreeTrial = null; - } + if (exists) { + console.log("Free trial fingerprint exists"); + uniqueFreeTrial = null; } return uniqueFreeTrial; diff --git a/server/tsconfig.json b/server/tsconfig.json index e9b5abb4d..12c0fac60 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -22,12 +22,14 @@ // "noEmit": true, "paths": { "@/*": ["src/*"], - "@shared/*": ["../shared/*"] + "@shared/*": ["../shared/*"], + "@scripts/*": ["scripts/*"] } }, "include": [ "src", "tests", + "scripts", "tests-old/utils.ts", "tests-old/global.ts", "../shared/db/allTables.ts", diff --git a/shared/models/productModels/productModels.ts b/shared/models/productModels/productModels.ts index d5582efb6..b665d1923 100644 --- a/shared/models/productModels/productModels.ts +++ b/shared/models/productModels/productModels.ts @@ -68,6 +68,7 @@ export const FullProductSchema = ProductSchema.extend({ ), free_trial: FreeTrialSchema.nullish(), free_trials: z.array(FreeTrialSchema).nullish(), + free_trial_ids: z.array(z.string()).nullish(), }); export type Product = z.infer;