164 lines
3.4 KiB
Plaintext
164 lines
3.4 KiB
Plaintext
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;
|