129 lines
4.2 KiB
TypeScript
129 lines
4.2 KiB
TypeScript
import { sql, type InferInsertModel, type InferSelectModel } from "drizzle-orm";
|
|
import {
|
|
boolean,
|
|
foreignKey,
|
|
index,
|
|
jsonb,
|
|
numeric,
|
|
pgTable,
|
|
text,
|
|
} from "drizzle-orm/pg-core";
|
|
import { collatePgColumn } from "../../db/utils.js";
|
|
import { customers } from "../cusModels/cusTable.js";
|
|
import { entities } from "../cusModels/entityModels/entityTable.js";
|
|
import { freeTrials } from "../productModels/freeTrialModels/freeTrialTable.js";
|
|
import { products } from "../productModels/productTable.js";
|
|
|
|
export type CustomerProductProcessor = {
|
|
type: "stripe" | "revenuecat";
|
|
id: string;
|
|
};
|
|
|
|
export const customerProducts = pgTable(
|
|
"customer_products",
|
|
{
|
|
id: text().primaryKey().notNull(),
|
|
internal_customer_id: text("internal_customer_id").notNull(),
|
|
internal_product_id: text("internal_product_id").notNull(),
|
|
internal_entity_id: text("internal_entity_id"),
|
|
|
|
created_at: numeric({ mode: "number" }),
|
|
updated_at: numeric({ mode: "number" }),
|
|
status: text(),
|
|
processor: jsonb().$type<CustomerProductProcessor>(),
|
|
// processors: jsonb().$type<ExternalCusProductProcessors>(),
|
|
|
|
canceled: boolean("canceled").default(false),
|
|
canceled_at: numeric({ mode: "number" }),
|
|
ended_at: numeric({ mode: "number" }),
|
|
starts_at: numeric({ mode: "number" }),
|
|
access_starts_at: numeric({ mode: "number" }),
|
|
options: jsonb().array(),
|
|
product_id: text("product_id"),
|
|
free_trial_id: text("free_trial_id"),
|
|
trial_ends_at: numeric({ mode: "number" }),
|
|
billing_cycle_anchor_resets_at: numeric({ mode: "number" }),
|
|
collection_method: text("collection_method").default(
|
|
"charge_automatically",
|
|
),
|
|
subscription_ids: text("subscription_ids").array(),
|
|
scheduled_ids: text("scheduled_ids").array(),
|
|
quantity: numeric({ mode: "number" }).default(1),
|
|
|
|
is_custom: boolean("is_custom").default(false).notNull(),
|
|
|
|
// Optional...
|
|
customer_id: text("customer_id"),
|
|
entity_id: text("entity_id"),
|
|
billing_version: text("billing_version"),
|
|
|
|
api_version: numeric({ mode: "number" }),
|
|
api_semver: text("api_semver"),
|
|
|
|
external_id: text("external_id"),
|
|
|
|
// When the cusProduct was created via a Stripe checkout flow with
|
|
// enable_plan_immediately, this links the row to the pending checkout session
|
|
// so the webhook can patch in subscription_ids on completion (or expire on abandonment).
|
|
stripe_checkout_session_id: text("stripe_checkout_session_id"),
|
|
|
|
previous_customer_product_id: text("previous_customer_product_id"),
|
|
on_trial_end: text("on_trial_end"),
|
|
},
|
|
(table) => [
|
|
foreignKey({
|
|
columns: [table.free_trial_id],
|
|
foreignColumns: [freeTrials.id],
|
|
name: "customer_products_free_trial_id_fkey",
|
|
}),
|
|
foreignKey({
|
|
columns: [table.internal_customer_id],
|
|
foreignColumns: [customers.internal_id],
|
|
name: "customer_products_internal_customer_id_fkey",
|
|
})
|
|
.onUpdate("cascade")
|
|
.onDelete("cascade"),
|
|
foreignKey({
|
|
columns: [table.internal_product_id],
|
|
foreignColumns: [products.internal_id],
|
|
name: "customer_products_internal_product_id_fkey",
|
|
}),
|
|
foreignKey({
|
|
columns: [table.internal_entity_id],
|
|
foreignColumns: [entities.internal_id],
|
|
name: "customer_products_internal_entity_id_fkey",
|
|
}).onDelete("set null"),
|
|
index("idx_customer_products_customer_status").on(
|
|
table.internal_customer_id,
|
|
table.status,
|
|
),
|
|
index("idx_customer_products_on_internal_entity_id").on(
|
|
table.internal_entity_id,
|
|
),
|
|
index("idx_customer_products_on_internal_product_id").on(
|
|
table.internal_product_id,
|
|
),
|
|
index("idx_customer_products_subscription_ids").using(
|
|
"gin",
|
|
table.subscription_ids,
|
|
),
|
|
index("idx_customer_products_scheduled_ids").using(
|
|
"gin",
|
|
table.scheduled_ids,
|
|
),
|
|
index("idx_customer_products_stripe_checkout_session_id").on(
|
|
table.stripe_checkout_session_id,
|
|
),
|
|
index("idx_customer_products_revenuecat_processor")
|
|
.on(table.internal_customer_id)
|
|
.where(sql`(${table.processor} ->> 'type') = 'revenuecat'`),
|
|
],
|
|
);
|
|
|
|
collatePgColumn(customerProducts.id, "C");
|
|
|
|
export type CustomerProduct = InferSelectModel<typeof customerProducts>;
|
|
export type InsertCustomerProduct = InferInsertModel<typeof customerProducts>;
|
|
export type DbCustomerProduct = InferSelectModel<typeof customerProducts>;
|
|
export type InsertDbCustomerProduct = InferInsertModel<typeof customerProducts>;
|