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', {