fixed fingerprint across product versions and free trial search

This commit is contained in:
John Yeo
2025-06-03 08:52:43 +01:00
parent 674d5f3882
commit 4aa6df1d95
7 changed files with 49 additions and 101 deletions

View File

@@ -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,
);

View File

@@ -15,6 +15,7 @@ import {
import { z } from "zod";
export type AttachParams = {
freeTrialIds: string[];
org: Organization;
customer: Customer;
products: FullProduct[];

View File

@@ -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({

View File

@@ -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,

View File

@@ -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;

View File

@@ -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",

View File

@@ -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<typeof ProductSchema>;