fix(billing): complete billing module — fix tier change, platform support, merchant portal
- Fix admin tier change: resolve tier_code→tier_id in update_subscription(), delegate to billing_service.change_tier() for Stripe-connected subs - Add platform support to admin tiers page: platform column, filter dropdown, platform selector in create/edit modal, platform_name in tier API response - Filter used platforms in create subscription modal on merchant detail page - Enrich merchant portal API responses with tier code, tier_name, platform_name - Add eager-load of platform relationship in get_merchant_subscription() - Remove stale store_name/store_code references from merchant templates - Add merchant tier change endpoint (POST /change-tier) and tier selector UI replacing broken requestUpgrade() button - Fix subscription detail link to use platform_id instead of sub.id Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,8 @@ function adminSubscriptionTiers() {
|
||||
tiers: [],
|
||||
stats: null,
|
||||
includeInactive: false,
|
||||
platforms: [],
|
||||
filterPlatformId: '',
|
||||
|
||||
// Feature management
|
||||
features: [],
|
||||
@@ -51,7 +53,8 @@ function adminSubscriptionTiers() {
|
||||
stripe_product_id: '',
|
||||
stripe_price_monthly_id: '',
|
||||
is_active: true,
|
||||
is_public: true
|
||||
is_public: true,
|
||||
platform_id: null
|
||||
},
|
||||
|
||||
async init() {
|
||||
@@ -67,7 +70,8 @@ function adminSubscriptionTiers() {
|
||||
await Promise.all([
|
||||
this.loadTiers(),
|
||||
this.loadStats(),
|
||||
this.loadFeatures()
|
||||
this.loadFeatures(),
|
||||
this.loadPlatforms()
|
||||
]);
|
||||
tiersLog.info('=== SUBSCRIPTION TIERS PAGE INITIALIZED ===');
|
||||
} catch (error) {
|
||||
@@ -92,6 +96,7 @@ function adminSubscriptionTiers() {
|
||||
params.append('include_inactive', this.includeInactive);
|
||||
if (this.sortBy) params.append('sort_by', this.sortBy);
|
||||
if (this.sortOrder) params.append('sort_order', this.sortOrder);
|
||||
if (this.filterPlatformId) params.append('platform_id', this.filterPlatformId);
|
||||
|
||||
const data = await apiClient.get(`/admin/subscriptions/tiers?${params}`);
|
||||
this.tiers = data.tiers || [];
|
||||
@@ -125,6 +130,22 @@ function adminSubscriptionTiers() {
|
||||
}
|
||||
},
|
||||
|
||||
async loadPlatforms() {
|
||||
try {
|
||||
const data = await apiClient.get('/admin/platforms');
|
||||
this.platforms = (data.platforms || []).map(p => ({ id: p.id, name: p.name }));
|
||||
tiersLog.info(`Loaded ${this.platforms.length} platforms`);
|
||||
} catch (error) {
|
||||
tiersLog.error('Failed to load platforms:', error);
|
||||
}
|
||||
},
|
||||
|
||||
getPlatformName(platformId) {
|
||||
if (!platformId) return 'Global';
|
||||
const platform = this.platforms.find(p => p.id === platformId);
|
||||
return platform ? platform.name : `Platform #${platformId}`;
|
||||
},
|
||||
|
||||
openCreateModal() {
|
||||
this.editingTier = null;
|
||||
this.formData = {
|
||||
@@ -137,7 +158,8 @@ function adminSubscriptionTiers() {
|
||||
stripe_product_id: '',
|
||||
stripe_price_monthly_id: '',
|
||||
is_active: true,
|
||||
is_public: true
|
||||
is_public: true,
|
||||
platform_id: null
|
||||
};
|
||||
this.showModal = true;
|
||||
},
|
||||
@@ -154,7 +176,8 @@ function adminSubscriptionTiers() {
|
||||
stripe_product_id: tier.stripe_product_id || '',
|
||||
stripe_price_monthly_id: tier.stripe_price_monthly_id || '',
|
||||
is_active: tier.is_active,
|
||||
is_public: tier.is_public
|
||||
is_public: tier.is_public,
|
||||
platform_id: tier.platform_id || null
|
||||
};
|
||||
this.showModal = true;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user