Pro/business

This commit is contained in:
Timo Knuth
2026-07-06 00:18:06 +02:00
parent 0b9c8d2a8f
commit 91bd940edc
10 changed files with 384 additions and 40 deletions

163
manual-billing-sql.txt Normal file
View File

@@ -0,0 +1,163 @@
Manual billing SQL for QR Master
================================
Use these statements for manual plan changes in the database.
Replace USER_EMAIL_HERE before running.
Do not use a naked UPDATE like:
UPDATE "User" SET "plan" = 'PRO' WHERE email = '...';
That changes the plan but creates no billing log. Always use the matching full
transaction below so UserLifecycleLog gets the alert entry.
Upgrade user to PRO
===================
BEGIN;
WITH old_user AS (
SELECT *
FROM "User"
WHERE email = 'USER_EMAIL_HERE'
FOR UPDATE
),
updated_user AS (
UPDATE "User"
SET
"plan" = 'PRO',
"lifecycleStage" = 'paid',
"lastScoredAt" = NOW(),
"lastQualifiedAt" = NOW(),
"updatedAt" = NOW()
WHERE "id" = (SELECT "id" FROM old_user)
RETURNING *
)
INSERT INTO "UserLifecycleLog" (
"id",
"userId",
"fromStage",
"toStage",
"fitScore",
"intentScore",
"leadScore",
"reason",
"createdAt"
)
SELECT
CONCAT('manual_', MD5(updated_user."id" || clock_timestamp()::TEXT || random()::TEXT)),
updated_user."id",
old_user."lifecycleStage",
'paid',
updated_user."fitScore",
updated_user."intentScore",
updated_user."leadScore",
'subscription_created',
NOW()
FROM updated_user
JOIN old_user ON old_user."id" = updated_user."id";
COMMIT;
Upgrade user to BUSINESS
========================
BEGIN;
WITH old_user AS (
SELECT *
FROM "User"
WHERE email = 'USER_EMAIL_HERE'
FOR UPDATE
),
updated_user AS (
UPDATE "User"
SET
"plan" = 'BUSINESS',
"lifecycleStage" = 'paid',
"lastScoredAt" = NOW(),
"lastQualifiedAt" = NOW(),
"updatedAt" = NOW()
WHERE "id" = (SELECT "id" FROM old_user)
RETURNING *
)
INSERT INTO "UserLifecycleLog" (
"id",
"userId",
"fromStage",
"toStage",
"fitScore",
"intentScore",
"leadScore",
"reason",
"createdAt"
)
SELECT
CONCAT('manual_', MD5(updated_user."id" || clock_timestamp()::TEXT || random()::TEXT)),
updated_user."id",
old_user."lifecycleStage",
'paid',
updated_user."fitScore",
updated_user."intentScore",
updated_user."leadScore",
'subscription_created',
NOW()
FROM updated_user
JOIN old_user ON old_user."id" = updated_user."id";
COMMIT;
Set user to FREE after deabo / subscription ended
=================================================
BEGIN;
WITH old_user AS (
SELECT *
FROM "User"
WHERE email = 'USER_EMAIL_HERE'
FOR UPDATE
),
updated_user AS (
UPDATE "User"
SET
"plan" = 'FREE',
"stripeSubscriptionId" = NULL,
"stripePriceId" = NULL,
"stripeCurrentPeriodEnd" = NULL,
"lifecycleStage" = CASE
WHEN COALESCE("leadScore", 0) >= 70 THEN 'upgrade_candidate'
WHEN COALESCE("leadScore", 0) >= 55 THEN 'hot'
WHEN COALESCE("leadScore", 0) >= 30 THEN 'warm'
WHEN "activationAt" IS NOT NULL THEN 'activated'
ELSE 'cold'
END,
"lastScoredAt" = NOW(),
"updatedAt" = NOW()
WHERE "id" = (SELECT "id" FROM old_user)
RETURNING *
)
INSERT INTO "UserLifecycleLog" (
"id",
"userId",
"fromStage",
"toStage",
"fitScore",
"intentScore",
"leadScore",
"reason",
"createdAt"
)
SELECT
CONCAT('manual_', MD5(updated_user."id" || clock_timestamp()::TEXT || random()::TEXT)),
updated_user."id",
old_user."lifecycleStage",
updated_user."lifecycleStage",
updated_user."fitScore",
updated_user."intentScore",
updated_user."leadScore",
'subscription_deleted',
NOW()
FROM updated_user
JOIN old_user ON old_user."id" = updated_user."id";
COMMIT;