diff --git a/chat/public/apps.html b/chat/public/apps.html index 6c040f3..d2ca4a5 100644 --- a/chat/public/apps.html +++ b/chat/public/apps.html @@ -2247,7 +2247,7 @@ } const tourSteps = [ - { target: null, color: '#008060' }, + { target: '#apps-content', color: '#008060' }, { target: '#create-new-app', color: '#5A31F4' }, { target: '#upload-app-btn', color: '#F59E0B' }, { target: '#browse-templates-btn', color: '#10B981' }, diff --git a/chat/server.js b/chat/server.js index f530219..8e8f46a 100644 --- a/chat/server.js +++ b/chat/server.js @@ -12115,6 +12115,62 @@ async function handleAccountSettingsUpdate(req, res) { const isPaidToPaid = PAID_PLANS.has(user.plan) && PAID_PLANS.has(requestedPlan); const isFreeToPaid = user.plan === 'hobby' && PAID_PLANS.has(requestedPlan); + // Helper function to lookup subscription by customer + async function lookupSubscriptionByCustomer(user) { + try { + const customerId = await ensureDodoCustomer(user); + if (!customerId) return null; + + const subscriptions = await dodoRequest('/subscriptions', { + method: 'GET', + query: { customer_id: customerId, status: 'active' } + }); + + if (subscriptions?.items && subscriptions.items.length > 0) { + const activeSub = subscriptions.items[0]; + log('Found missing subscription ID via Dodo lookup', { + userId: user.id, + email: user.email, + subscriptionId: activeSub.subscription_id || activeSub.id, + customerId + }); + return activeSub.subscription_id || activeSub.id; + } + return null; + } catch (error) { + log('Failed to lookup subscription by customer', { + userId: user.id, + email: user.email, + error: String(error) + }); + return null; + } + } + + // If paid user is missing dodoSubscriptionId, try to look it up + if ((isPaidToFree || isPaidToPaid) && !user.dodoSubscriptionId && PAID_PLANS.has(user.plan)) { + const foundSubscriptionId = await lookupSubscriptionByCustomer(user); + if (foundSubscriptionId) { + user.dodoSubscriptionId = foundSubscriptionId; + await persistUsersDb(); + log('Recovered missing subscription ID', { + userId: user.id, + email: user.email, + subscriptionId: foundSubscriptionId + }); + } else { + // Log the issue for investigation + log('CRITICAL: Paid user missing dodoSubscriptionId and no active subscription found in Dodo', { + userId: user.id, + email: user.email, + currentPlan: user.plan, + requestedPlan: requestedPlan, + billingStatus: user.billingStatus, + subscriptionRenewsAt: user.subscriptionRenewsAt + }); + } + } + // Schedule cancellation at period end when changing from paid to free hobby plan // User keeps premium features until the end of their billing period if (isPaidToFree && user.dodoSubscriptionId) { @@ -12171,6 +12227,19 @@ async function handleAccountSettingsUpdate(req, res) { return sendJson(res, 400, { error: error.message || 'Unable to change subscription plan' }); } } + // Handle paid-to-free or paid-to-paid when subscription ID is still missing after lookup + else if ((isPaidToFree || isPaidToPaid) && !user.dodoSubscriptionId) { + log('ERROR: Cannot change plan - paid user missing dodoSubscriptionId after lookup', { + userId: user.id, + email: user.email, + currentPlan: user.plan, + requestedPlan: requestedPlan, + changeType: isPaidToFree ? 'paid_to_free' : 'paid_to_paid' + }); + return sendJson(res, 400, { + error: 'Subscription ID not found. Please contact support to resolve this issue.' + }); + } // For free-to-paid upgrades, create a checkout session and return the URL else if (isFreeToPaid || (!user.dodoSubscriptionId && PAID_PLANS.has(requestedPlan))) { try { @@ -12217,7 +12286,7 @@ async function handleAccountSettingsUpdate(req, res) { }, }; - const returnUrl = `${getBaseUrl()}/subscription-success`; + const returnUrl = `${resolveBaseUrl(req)}/subscription-success`; checkoutBody.return_url = returnUrl; const checkoutSession = await dodoRequest('/checkouts', {