fix: sync subscription tier on admin override (refs #58)

Add adminOverrideTier() method to SubscriptionsService that atomically
updates both subscriptions.tier and user_profiles.subscription_tier
using database transactions.

Changes:
- SubscriptionsRepository: Add updateTierByUserId() and
  createForAdminOverride() methods with transaction support
- SubscriptionsService: Add adminOverrideTier() method with transaction
  wrapping for atomic dual-table updates
- UsersController: Replace userProfileService.updateSubscriptionTier()
  with subscriptionsService.adminOverrideTier()

This ensures admin tier changes properly sync to both database tables,
fixing the Settings page "Current Plan" display mismatch.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Eric Gullickson
2026-01-19 09:03:50 -06:00
parent 5707391864
commit 2c0cbd5bf7
3 changed files with 193 additions and 6 deletions

View File

@@ -728,6 +728,67 @@ export class SubscriptionsService {
return 'free';
}
/**
* Admin override of subscription tier (bypasses Stripe)
* Updates both subscriptions.tier and user_profiles.subscription_tier atomically
* Creates subscription record if user doesn't have one
*
* Note: Admin feature depends on Subscriptions for tier management
* This is intentional - admin has oversight capabilities
*/
async adminOverrideTier(userId: string, newTier: SubscriptionTier): Promise<Subscription> {
const pool = this.repository.getPool();
const client = await pool.connect();
try {
await client.query('BEGIN');
logger.info('Admin overriding subscription tier', { userId, newTier });
// Check if user has a subscription record
let subscription = await this.repository.findByUserId(userId);
if (!subscription) {
// Create subscription record for user (admin override bypasses Stripe)
logger.info('Creating subscription record for admin override', { userId, newTier });
subscription = await this.repository.createForAdminOverride(userId, newTier, client);
} else {
// Update existing subscription tier
const updated = await this.repository.updateTierByUserId(userId, newTier, client);
if (!updated) {
throw new Error('Failed to update subscription tier');
}
subscription = updated;
}
// Sync tier to user_profiles table (within same transaction)
await client.query(
'UPDATE user_profiles SET subscription_tier = $1 WHERE auth0_sub = $2',
[newTier, userId]
);
await client.query('COMMIT');
logger.info('Admin subscription tier override complete', {
userId,
newTier,
subscriptionId: subscription.id,
});
return subscription;
} catch (error: any) {
await client.query('ROLLBACK');
logger.error('Failed to admin override subscription tier', {
userId,
newTier,
error: error.message,
});
throw error;
} finally {
client.release();
}
}
/**
* Get invoices for a user's subscription
*/