fix: resolve subscription ID lookup and checkout URL issues

- Fix getBaseUrl() ReferenceError by changing to resolveBaseUrl(req) for free-to-paid upgrades
- Add subscription lookup by customer email when dodoSubscriptionId is missing
- Log critical issues when paid users have no subscription ID in database or Dodo
- Return clear error message to contact support when subscription cannot be found
- Prevent silent failures in plan changes from settings page
This commit is contained in:
southseact-3d
2026-02-12 11:09:45 +00:00
parent c762470fa9
commit ae871df0a0
2 changed files with 71 additions and 2 deletions

View File

@@ -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' },

View File

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