diff --git a/README.md b/README.md
index 3f6cece..e698d1a 100644
--- a/README.md
+++ b/README.md
@@ -58,6 +58,12 @@ go build
## Breaking Changes (For builds before this date)
+### 29 August 2024
+
+Using stripe customer id instead of stripe subscription id
+
+Existing customer subscription id will be removed from database, so have to manually find every customer with the same email and fill in their ids
+
### 23 July 2024
Transition from using ipbase to using geoipupdate for geolocation.
diff --git a/frontend/drizzle/0018_living_taskmaster.sql b/frontend/drizzle/0018_living_taskmaster.sql
new file mode 100644
index 0000000..831689c
--- /dev/null
+++ b/frontend/drizzle/0018_living_taskmaster.sql
@@ -0,0 +1,2 @@
+ALTER TABLE "user" ADD COLUMN "stripe_customer_id" varchar(255);--> statement-breakpoint
+ALTER TABLE "user" DROP COLUMN IF EXISTS "stripe_subscription";
\ No newline at end of file
diff --git a/frontend/drizzle/0019_chief_mephisto.sql b/frontend/drizzle/0019_chief_mephisto.sql
new file mode 100644
index 0000000..349370d
--- /dev/null
+++ b/frontend/drizzle/0019_chief_mephisto.sql
@@ -0,0 +1 @@
+DROP TABLE "stripe_session";
\ No newline at end of file
diff --git a/frontend/drizzle/meta/0018_snapshot.json b/frontend/drizzle/meta/0018_snapshot.json
new file mode 100644
index 0000000..9aade0d
--- /dev/null
+++ b/frontend/drizzle/meta/0018_snapshot.json
@@ -0,0 +1,461 @@
+{
+ "id": "227300d6-3473-4b34-90c7-15aa5e594784",
+ "prevId": "5014f286-7c9c-411b-a03e-16a05b37caa0",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.email_verification_token": {
+ "name": "email_verification_token",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(255)",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.project": {
+ "name": "project",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "uuid": {
+ "name": "uuid",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "qr_background": {
+ "name": "qr_background",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'#ffffff'"
+ },
+ "qr_foreground": {
+ "name": "qr_foreground",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'#000000'"
+ },
+ "domain_status": {
+ "name": "domain_status",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'verified'"
+ },
+ "enable_custom_domain": {
+ "name": "enable_custom_domain",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "custom_ip": {
+ "name": "custom_ip",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "custom_domain_id": {
+ "name": "custom_domain_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "custom_domain": {
+ "name": "custom_domain",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.session": {
+ "name": "session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(255)",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.setting": {
+ "name": "setting",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "qr_background": {
+ "name": "qr_background",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "qr_foreground": {
+ "name": "qr_foreground",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.shortener": {
+ "name": "shortener",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "link": {
+ "name": "link",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ios": {
+ "name": "ios",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "ios_link": {
+ "name": "ios_link",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "android": {
+ "name": "android",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "android_link": {
+ "name": "android_link",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "code": {
+ "name": "code",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "active": {
+ "name": "active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "shortener_code_unique": {
+ "name": "shortener_code_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "code"
+ ]
+ }
+ }
+ },
+ "public.stripe_session": {
+ "name": "stripe_session",
+ "schema": "",
+ "columns": {
+ "session_id": {
+ "name": "session_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expired": {
+ "name": "expired",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "uuid": {
+ "name": "uuid",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "gen_random_uuid()"
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "google_id": {
+ "name": "google_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "username": {
+ "name": "username",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "plan": {
+ "name": "plan",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'free'"
+ },
+ "stripe_customer_id": {
+ "name": "stripe_customer_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_email_unique": {
+ "name": "user_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ }
+ },
+ "public.visitor": {
+ "name": "visitor",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "shortener_id": {
+ "name": "shortener_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "country_code": {
+ "name": "country_code",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "country": {
+ "name": "country",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "city": {
+ "name": "city",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "device_type": {
+ "name": "device_type",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "device_vendor": {
+ "name": "device_vendor",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "os": {
+ "name": "os",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "browser": {
+ "name": "browser",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/frontend/drizzle/meta/0019_snapshot.json b/frontend/drizzle/meta/0019_snapshot.json
new file mode 100644
index 0000000..37b47aa
--- /dev/null
+++ b/frontend/drizzle/meta/0019_snapshot.json
@@ -0,0 +1,432 @@
+{
+ "id": "b2f02cb7-53e4-4077-9cea-ad5e3b062f51",
+ "prevId": "227300d6-3473-4b34-90c7-15aa5e594784",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.email_verification_token": {
+ "name": "email_verification_token",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(255)",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.project": {
+ "name": "project",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "uuid": {
+ "name": "uuid",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "qr_background": {
+ "name": "qr_background",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'#ffffff'"
+ },
+ "qr_foreground": {
+ "name": "qr_foreground",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'#000000'"
+ },
+ "domain_status": {
+ "name": "domain_status",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'verified'"
+ },
+ "enable_custom_domain": {
+ "name": "enable_custom_domain",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "custom_ip": {
+ "name": "custom_ip",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "custom_domain_id": {
+ "name": "custom_domain_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "custom_domain": {
+ "name": "custom_domain",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.session": {
+ "name": "session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "varchar(255)",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.setting": {
+ "name": "setting",
+ "schema": "",
+ "columns": {
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "qr_background": {
+ "name": "qr_background",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "qr_foreground": {
+ "name": "qr_foreground",
+ "type": "varchar(7)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ },
+ "public.shortener": {
+ "name": "shortener",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "link": {
+ "name": "link",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ios": {
+ "name": "ios",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "ios_link": {
+ "name": "ios_link",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "android": {
+ "name": "android",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "android_link": {
+ "name": "android_link",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "code": {
+ "name": "code",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "active": {
+ "name": "active",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": true
+ },
+ "project_id": {
+ "name": "project_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "shortener_code_unique": {
+ "name": "shortener_code_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "code"
+ ]
+ }
+ }
+ },
+ "public.user": {
+ "name": "user",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "uuid": {
+ "name": "uuid",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "gen_random_uuid()"
+ },
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "google_id": {
+ "name": "google_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "username": {
+ "name": "username",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password": {
+ "name": "password",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "plan": {
+ "name": "plan",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'free'"
+ },
+ "stripe_customer_id": {
+ "name": "stripe_customer_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_email_unique": {
+ "name": "user_email_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ }
+ },
+ "public.visitor": {
+ "name": "visitor",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "serial",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "shortener_id": {
+ "name": "shortener_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "country_code": {
+ "name": "country_code",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "country": {
+ "name": "country",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "city": {
+ "name": "city",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "device_type": {
+ "name": "device_type",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "device_vendor": {
+ "name": "device_vendor",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "os": {
+ "name": "os",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ },
+ "browser": {
+ "name": "browser",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "''"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {}
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/frontend/drizzle/meta/_journal.json b/frontend/drizzle/meta/_journal.json
index d3efb2b..8924676 100644
--- a/frontend/drizzle/meta/_journal.json
+++ b/frontend/drizzle/meta/_journal.json
@@ -127,6 +127,20 @@
"when": 1724731642381,
"tag": "0017_mysterious_marten_broadcloak",
"breakpoints": true
+ },
+ {
+ "idx": 18,
+ "version": "7",
+ "when": 1724920767007,
+ "tag": "0018_living_taskmaster",
+ "breakpoints": true
+ },
+ {
+ "idx": 19,
+ "version": "7",
+ "when": 1724923406366,
+ "tag": "0019_chief_mephisto",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/frontend/src/lib/db/schema.ts b/frontend/src/lib/db/schema.ts
index fe7ad82..2d5e7d4 100644
--- a/frontend/src/lib/db/schema.ts
+++ b/frontend/src/lib/db/schema.ts
@@ -24,7 +24,7 @@ export const user = pgTable('user', {
.notNull()
.$type<'free' | 'pro' | 'owner'>()
.default('free'),
- stripeSubscription: varchar('stripe_subscription', { length: 255 }),
+ stripeCustomerId: varchar('stripe_customer_id', { length: 255 }),
})
export const shortener = pgTable('shortener', {
@@ -117,12 +117,6 @@ export const emailVerificationToken = pgTable(
},
)
-export const stripeSession = pgTable('stripe_session', {
- session_id: varchar('session_id', { length: 255 }).notNull(),
- userId: integer('user_id').notNull(),
- expired: boolean('expired').notNull().default(false),
-})
-
// relations
export const userRelations = relations(user, ({ one, many }) => ({
shortener: many(shortener),
@@ -175,13 +169,3 @@ export const settingRelations = relations(setting, ({ one }) => ({
references: [user.id],
}),
}))
-
-export const stripeSessionRelations = relations(
- stripeSession,
- ({ one }) => ({
- user: one(user, {
- fields: [stripeSession.userId],
- references: [user.id],
- }),
- }),
-)
diff --git a/frontend/src/routes/(app)/dashboard/billing/+page.server.ts b/frontend/src/routes/(app)/dashboard/billing/+page.server.ts
index e16faf3..f722d82 100644
--- a/frontend/src/routes/(app)/dashboard/billing/+page.server.ts
+++ b/frontend/src/routes/(app)/dashboard/billing/+page.server.ts
@@ -28,10 +28,16 @@ export const actions = {
const user = event.locals.user
- if (!user.stripeSubscription) return { form }
+ if (!user.stripeCustomerId) return { form }
- const subscription = await stripe.subscriptions.update(
- user.stripeSubscription,
+ const subscription = await stripe.subscriptions.list({
+ customer: user.stripeCustomerId,
+ price: env.PRIVATE_PRO_PLAN_PRICE_ID,
+ limit: 1,
+ })
+
+ const cancelSubscription = await stripe.subscriptions.update(
+ subscription.data[0].id,
{ cancel_at_period_end: true },
)
diff --git a/frontend/src/routes/(app)/dashboard/billing/plan/pro/+page.server.ts b/frontend/src/routes/(app)/dashboard/billing/plan/pro/+page.server.ts
index 5024b33..9131fbe 100644
--- a/frontend/src/routes/(app)/dashboard/billing/plan/pro/+page.server.ts
+++ b/frontend/src/routes/(app)/dashboard/billing/plan/pro/+page.server.ts
@@ -2,19 +2,39 @@ import { redirect } from '@sveltejs/kit'
import type { PageServerLoad } from './$types'
import Stripe from 'stripe'
import { db } from '$lib/db'
-import { stripeSession } from '$lib/db/schema'
import { env } from '$env/dynamic/private'
+import { user as userTable } from '$lib/db/schema'
+import { eq } from 'drizzle-orm'
export const load = (async (events) => {
- if (events.locals.user.stripeSubscription) {
+ if (events.locals.user.plan !== 'free') {
redirect(301, '/dashboard/billing')
}
if (!events.locals.user.email_verified) {
redirect(301, '/dashboard/billing')
}
+
+ const user = events.locals.user
+
const stripe = new Stripe(env.PRIVATE_STRIPE_SECRET_KEY)
+
+ let stripeCustomerId = events.locals.user.stripeCustomerId
+
+ if (!stripeCustomerId) {
+ const customer = await stripe.customers.create({
+ email: user.email,
+ })
+ stripeCustomerId = customer.id
+ await db
+ .update(userTable)
+ .set({
+ stripeCustomerId: customer.id,
+ })
+ .where(eq(userTable.id, user.id))
+ }
+
const session = await stripe.checkout.sessions.create({
- customer_email: events.locals.user.email,
+ customer: stripeCustomerId,
line_items: [
{ price: env.PRIVATE_PRO_PLAN_PRICE_ID, quantity: 1 },
],
@@ -30,11 +50,5 @@ export const load = (async (events) => {
redirect(301, '/dashboard/billing')
}
- await db.insert(stripeSession).values({
- session_id: session.id,
- userId: events.locals.user.id,
- expired: false,
- })
-
redirect(301, session.url)
}) satisfies PageServerLoad
diff --git a/frontend/src/routes/(app)/dashboard/billing/plan/pro/success/+page.server.ts b/frontend/src/routes/(app)/dashboard/billing/plan/pro/success/+page.server.ts
index 53abbaa..9e0cee2 100644
--- a/frontend/src/routes/(app)/dashboard/billing/plan/pro/success/+page.server.ts
+++ b/frontend/src/routes/(app)/dashboard/billing/plan/pro/success/+page.server.ts
@@ -2,7 +2,7 @@ import { redirect } from '@sveltejs/kit'
import type { PageServerLoad } from './$types'
import Stripe from 'stripe'
import { db } from '$lib/db'
-import { user, stripeSession } from '$lib/db/schema'
+import { user } from '$lib/db/schema'
import { eq } from 'drizzle-orm'
import { env } from '$env/dynamic/private'
@@ -17,27 +17,14 @@ export const load = (async ({ url }) => {
session.status === 'complete' &&
session.payment_status === 'paid'
) {
- const stripe_session = await db.query.stripeSession.findFirst({
- where: (stripeSession, { eq }) =>
- eq(stripeSession.session_id, session_id),
- with: {
- user: true,
- },
- })
- if (!stripe_session) redirect(301, '/dashboard/billing')
+ if (!session.customer) redirect(301, '/dashboard/billing')
await db
.update(user)
.set({
plan: 'pro',
- stripeSubscription: session.subscription?.toString(),
})
- .where(eq(user.id, stripe_session.user.id))
-
- await db
- .update(stripeSession)
- .set({ expired: true })
- .where(eq(stripeSession.session_id, session_id))
+ .where(eq(user.stripeCustomerId, session.customer?.toString()))
redirect(301, '/dashboard/billing')
}
diff --git a/frontend/src/routes/(app)/dashboard/billing/plan/pro/success/+page.svelte b/frontend/src/routes/(app)/dashboard/billing/plan/pro/success/+page.svelte
new file mode 100644
index 0000000..2e65c9e
--- /dev/null
+++ b/frontend/src/routes/(app)/dashboard/billing/plan/pro/success/+page.svelte
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+ Payment Success
+
+ You have subscribed to the pro plan, thank you for the
+ support.
+
+
+
+
+
+
+
diff --git a/frontend/src/routes/(auth)/signup/(components)/form.svelte b/frontend/src/routes/(auth)/signup/(components)/form.svelte
index 0e0ae68..1c42e14 100644
--- a/frontend/src/routes/(auth)/signup/(components)/form.svelte
+++ b/frontend/src/routes/(auth)/signup/(components)/form.svelte
@@ -20,6 +20,7 @@
if (result.status === 200) {
toast.success(
'Welcome to kon.sh, a verification email has been sent to your mailbox',
+ { duration: 10000 },
)
}
},
diff --git a/frontend/src/routes/webhook/stripe/+server.ts b/frontend/src/routes/webhook/stripe/+server.ts
index 2f78fdb..b985b4d 100644
--- a/frontend/src/routes/webhook/stripe/+server.ts
+++ b/frontend/src/routes/webhook/stripe/+server.ts
@@ -27,7 +27,10 @@ export const POST: RequestHandler = async (event) => {
.update(user)
.set({ plan: 'free' })
.where(
- eq(user.stripeSubscription, stripeEvent.data.object.id),
+ eq(
+ user.stripeCustomerId,
+ stripeEvent.data.object.customer.toString(),
+ ),
)
}
return new Response('Success', { status: 200 })