feat(billing): add dual formulas, disable 10yuan gift, retry auto-renew
Memind CI / Test, build, and release guards (push) Successful in 3m43s
Memind CI / Test, build, and release guards (push) Successful in 3m43s
Turn off the low-balance 10 CNY signup bonus while keeping the 5 CNY registration credit, split metering into independent formula A/B scopes, and retry expired auto-renew subscriptions after recharge or hourly worker. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+34
-8
@@ -129,15 +129,19 @@ export function createUserAuth(pool, options = {}) {
|
||||
const { storageRoot, publicBaseUrl } = resolveMindSpaceRuntimeConfig(h5Root, env);
|
||||
const skillCatalog = listPlatformSkillCatalog(h5Root);
|
||||
const defaultSignupBalanceCents = Number(options.defaultSignupBalanceCents ?? 500);
|
||||
const lowBalanceGiftThresholdCents = Number(options.lowBalanceGiftThresholdCents ?? 100);
|
||||
const lowBalanceGiftAmountCents = Number(options.lowBalanceGiftAmountCents ?? 1000);
|
||||
const lowBalanceGiftThresholdCents = Number(
|
||||
options.lowBalanceGiftThresholdCents ?? env.H5_LOW_BALANCE_GIFT_THRESHOLD_CENTS ?? 100,
|
||||
);
|
||||
const lowBalanceGiftAmountCents = Number(
|
||||
options.lowBalanceGiftAmountCents ?? env.H5_LOW_BALANCE_GIFT_AMOUNT_CENTS ?? 0,
|
||||
);
|
||||
const sessionTtlMs = Number(options.sessionTtlMs ?? 7 * 24 * 60 * 60 * 1000);
|
||||
const loginMaxFailures = Number(options.loginMaxFailures ?? 5);
|
||||
const loginFailureWindowMs = Number(options.loginFailureWindowMs ?? 5 * 60 * 1000);
|
||||
const persistSessions = options.persistSessions !== false && Boolean(pool);
|
||||
let rechargeNotifier =
|
||||
typeof options.onRechargeNotification === 'function' ? options.onRechargeNotification : null;
|
||||
const subscriptionService = options.subscriptionService ?? null;
|
||||
let subscriptionService = options.subscriptionService ?? null;
|
||||
const provisionUserDataSpace = typeof options.provisionUserDataSpace === 'function'
|
||||
? options.provisionUserDataSpace
|
||||
: null;
|
||||
@@ -190,7 +194,7 @@ export function createUserAuth(pool, options = {}) {
|
||||
const getUserById = async (userId) => {
|
||||
const [rows] = await pool.query(
|
||||
`SELECT u.id, u.username, u.slug, u.email, u.display_name, u.role, u.status,
|
||||
u.plan_type, u.workspace_root,
|
||||
u.plan_type, u.workspace_root, u.billing_formula,
|
||||
s.quota_bytes, s.used_bytes, s.reserved_bytes,
|
||||
w.balance_cents, w.tokens_used,
|
||||
(SELECT COALESCE(SUM(ABS(amount_cents)), 0)
|
||||
@@ -218,6 +222,7 @@ export function createUserAuth(pool, options = {}) {
|
||||
status: row.status,
|
||||
planType: row.plan_type ?? 'free',
|
||||
workspaceRoot: row.workspace_root,
|
||||
billingFormula: row.billing_formula === 'B' ? 'B' : 'A',
|
||||
balanceCents,
|
||||
totalCreditCents: balanceCents + spentCents,
|
||||
tokensUsed: Number(row.tokens_used ?? 0),
|
||||
@@ -447,7 +452,7 @@ export function createUserAuth(pool, options = {}) {
|
||||
(id, username, slug, email, display_name, salt, password_hash, password_algorithm,
|
||||
role, status, plan_type, workspace_root, low_balance_gift_eligible, low_balance_gift_granted_at,
|
||||
created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'user', 'active', 'free', ?, 1, NULL, ?, ?)`,
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'user', 'active', 'free', ?, 0, NULL, ?, ?)`,
|
||||
[
|
||||
userId,
|
||||
normalized,
|
||||
@@ -900,7 +905,7 @@ export function createUserAuth(pool, options = {}) {
|
||||
);
|
||||
const [rows] = await pool.query(
|
||||
`SELECT u.id, u.username, u.slug, u.email, u.display_name, u.role, u.status,
|
||||
u.plan_type, u.workspace_root,
|
||||
u.plan_type, u.workspace_root, u.billing_formula,
|
||||
s.quota_bytes, s.used_bytes, s.reserved_bytes,
|
||||
u.created_at, u.updated_at, w.balance_cents, w.tokens_used
|
||||
FROM h5_users u
|
||||
@@ -1038,6 +1043,14 @@ export function createUserAuth(pool, options = {}) {
|
||||
fields.push('role = ?');
|
||||
values.push(patch.role === 'admin' ? 'admin' : 'user');
|
||||
}
|
||||
if (patch.billingFormula !== undefined) {
|
||||
const formula = String(patch.billingFormula).trim().toUpperCase();
|
||||
if (!['A', 'B'].includes(formula)) {
|
||||
return { ok: false, message: '计量公式只能是 A 或 B' };
|
||||
}
|
||||
fields.push('billing_formula = ?');
|
||||
values.push(formula);
|
||||
}
|
||||
|
||||
if (fields.length > 0) {
|
||||
fields.push('updated_at = ?');
|
||||
@@ -1272,6 +1285,16 @@ export function createUserAuth(pool, options = {}) {
|
||||
]);
|
||||
}
|
||||
if (ownsConnection) await conn.commit();
|
||||
if (subscriptionService?.processAutoRenewalsForUser) {
|
||||
try {
|
||||
await subscriptionService.processAutoRenewalsForUser(userId);
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
'Post-recharge auto-renew retry failed:',
|
||||
err instanceof Error ? err.message : String(err),
|
||||
);
|
||||
}
|
||||
}
|
||||
const updated = await getUserById(userId);
|
||||
if (rechargeNotifier) {
|
||||
try {
|
||||
@@ -1336,7 +1359,7 @@ export function createUserAuth(pool, options = {}) {
|
||||
}
|
||||
const tokenState = normalizeTokenState(tokenStateRaw);
|
||||
const config = billingConfigService?.getEffectiveBillingConfig
|
||||
? await billingConfigService.getEffectiveBillingConfig()
|
||||
? await billingConfigService.getEffectiveBillingConfig({ userId })
|
||||
: loadBillingConfig(env);
|
||||
const normalizedRequestId = requestId ? String(requestId).trim() || null : null;
|
||||
const now = Date.now();
|
||||
@@ -2747,7 +2770,7 @@ export function createUserAuth(pool, options = {}) {
|
||||
(id, username, slug, email, display_name, salt, password_hash, password_algorithm,
|
||||
role, status, plan_type, workspace_root, signup_source, low_balance_gift_eligible,
|
||||
low_balance_gift_granted_at, created_at, updated_at)
|
||||
VALUES (?, ?, ?, NULL, ?, ?, ?, ?, 'user', 'active', 'free', ?, 'wechat', 1, NULL, ?, ?)`,
|
||||
VALUES (?, ?, ?, NULL, ?, ?, ?, ?, 'user', 'active', 'free', ?, 'wechat', 0, NULL, ?, ?)`,
|
||||
[
|
||||
userId,
|
||||
normalized,
|
||||
@@ -3079,6 +3102,9 @@ export function createUserAuth(pool, options = {}) {
|
||||
setRechargeNotifier(callback) {
|
||||
rechargeNotifier = typeof callback === 'function' ? callback : null;
|
||||
},
|
||||
setSubscriptionService(service) {
|
||||
subscriptionService = service ?? null;
|
||||
},
|
||||
findWechatUserByOpenid,
|
||||
getWechatAgentRoute,
|
||||
upsertWechatAgentRoute,
|
||||
|
||||
Reference in New Issue
Block a user