diff --git a/chat/server.js b/chat/server.js index 7be6c75..612deac 100644 --- a/chat/server.js +++ b/chat/server.js @@ -6787,21 +6787,62 @@ async function ensureDodoCustomer(user) { // Check if any customers were returned if (existingCustomers?.items && existingCustomers.items.length > 0) { - const existingCustomer = existingCustomers.items[0]; - const customerId = existingCustomer?.customer_id || existingCustomer?.id || ''; + // Log warning if multiple customers found for same email + if (existingCustomers.items.length > 1) { + log('WARNING: Multiple Dodo customers found for same email', { + userId: user.id, + email: email, + customerCount: existingCustomers.items.length, + customerIds: existingCustomers.items.map(c => c.customer_id || c.id) + }); + } + + // Find the customer with active subscriptions, or use the first one + let selectedCustomer = existingCustomers.items[0]; + + // If multiple customers, try to find one with active subscriptions + if (existingCustomers.items.length > 1) { + for (const customer of existingCustomers.items) { + const customerId = customer?.customer_id || customer?.id; + if (customerId) { + try { + const subscriptions = await dodoRequest('/subscriptions', { + method: 'GET', + query: { customer_id: customerId, status: 'active' } + }); + if (subscriptions?.items && subscriptions.items.length > 0) { + selectedCustomer = customer; + log('Selected customer with active subscriptions', { + userId: user.id, + email: email, + customerId: customerId, + activeSubscriptions: subscriptions.items.length + }); + break; + } + } catch (err) { + // Continue to next customer + } + } + } + } + + const customerId = selectedCustomer?.customer_id || selectedCustomer?.id || ''; log('DEBUG: Found customer in items', { userId: user.id, customerId: customerId, - customerKeys: Object.keys(existingCustomer || {}), - rawCustomer: JSON.stringify(existingCustomer).substring(0, 500) + totalCustomers: existingCustomers.items.length, + customerKeys: Object.keys(selectedCustomer || {}) }); + if (customerId) { user.dodoCustomerId = customerId; await persistUsersDb(); log('Found existing Dodo customer by email', { userId: user.id, email: email, - customerId: customerId + customerId: customerId, + totalFound: existingCustomers.items.length }); return customerId; } @@ -13004,6 +13045,14 @@ async function handleTopupCheckout(req, res) { const unitAmount = applyTopupDiscount(baseAmount, discount); const returnUrl = `${resolveBaseUrl(req)}/topup`; const orderId = `topup_${randomUUID()}`; + + // Ensure we have a Dodo customer ID before creating checkout + const customerId = await ensureDodoCustomer(user); + if (!customerId) { + log('Failed to get or create Dodo customer for topup', { userId: user.id, email: user.email }); + return sendJson(res, 500, { error: 'Unable to initialize customer for checkout' }); + } + const checkoutBody = { product_cart: [{ product_id: pack.productId, @@ -13011,12 +13060,14 @@ async function handleTopupCheckout(req, res) { amount: unitAmount, }], customer: { + customer_id: customerId, // Pass existing customer ID to prevent duplicates email: user.billingEmail || user.email, name: user.billingEmail || user.email, }, metadata: { type: 'topup', orderId, + customerId, userId: String(user.id), tokens: String(pack.tokens), tier: String(pack.tier), @@ -13436,10 +13487,17 @@ async function handlePaygCheckout(req, res) { } const amount = Math.max(MIN_PAYMENT_AMOUNT, Math.ceil((payg.billableTokens * payg.pricePerUnit) / PAYG_UNIT_TOKENS)); - const returnUrl = `${resolveBaseUrl(req)}/settings`; - const orderId = `payg_${randomUUID()}`; + const returnUrl = `${resolveBaseUrl(req)}/settings`; + const orderId = `payg_${randomUUID()}`; - try { + // Ensure we have a Dodo customer ID before creating checkout + const customerId = await ensureDodoCustomer(user); + if (!customerId) { + log('Failed to get or create Dodo customer for PAYG', { userId: user.id, email: user.email }); + return sendJson(res, 500, { error: 'Unable to initialize customer for checkout' }); + } + + try { const checkoutSession = await dodoRequest('/checkouts', { method: 'POST', body: { @@ -13449,6 +13507,7 @@ async function handlePaygCheckout(req, res) { amount, }], customer: { + customer_id: customerId, // Pass existing customer ID to prevent duplicates email: user.billingEmail || user.email, name: user.billingEmail || user.email, }, @@ -13462,6 +13521,7 @@ async function handlePaygCheckout(req, res) { currency: String(currency), amount: String(amount), month: String(payg.month), + customerId, }, }, }); @@ -13658,6 +13718,15 @@ async function handleSubscriptionCheckout(req, res) { // Create a unique session ID for this checkout const checkoutSessionId = randomUUID(); + + // Ensure we have a Dodo customer ID before creating checkout to prevent duplicate customers + const customerId = await ensureDodoCustomer(user); + if (!customerId) { + log('Failed to get or create Dodo customer for checkout', { userId: user.id, email: user.email }); + return sendJson(res, 500, { error: 'Unable to initialize customer for checkout' }); + } + + log('Using existing Dodo customer for checkout', { userId: user.id, customerId, email: user.email }); // Create checkout session with enhanced metadata for session tracking const returnUrl = `${resolveBaseUrl(req)}/apps`; @@ -13667,6 +13736,7 @@ async function handleSubscriptionCheckout(req, res) { quantity: 1, }], customer: { + customer_id: customerId, // Pass existing customer ID to prevent duplicates email: user.billingEmail || user.email, name: user.billingEmail || user.email, }, @@ -13680,6 +13750,7 @@ async function handleSubscriptionCheckout(req, res) { currency: String(currency), amount: String(product.price), inline: String(isInline), + customerId: customerId, // Track which customer ID was used }, settings: { allow_payment_methods: ['card'], @@ -13829,7 +13900,26 @@ async function handleSubscriptionConfirm(req, res, url) { user.billingCycle = pending.billingCycle; user.subscriptionCurrency = pending.currency; user.subscriptionRenewsAt = computeRenewalDate(pending.billingCycle); - user.dodoCustomerId = checkout?.customer_id || user.dodoCustomerId; + + // Validate customer ID consistency to prevent duplicate customer issues + const checkoutCustomerId = checkout?.customer_id; + const storedCustomerId = user.dodoCustomerId; + + if (checkoutCustomerId && storedCustomerId && checkoutCustomerId !== storedCustomerId) { + log('WARNING: Checkout returned different customer_id than stored', { + userId: user.id, + email: user.email, + storedCustomerId: storedCustomerId, + checkoutCustomerId: checkoutCustomerId, + subscriptionId: checkout?.subscription_id, + pendingCustomerId: pending.metadata?.customerId + }); + + // Check if we should use the new customer ID (if it has subscriptions) + user.dodoCustomerId = checkoutCustomerId; + } else if (checkoutCustomerId) { + user.dodoCustomerId = checkoutCustomerId; + } if (checkout?.subscription_id) { user.dodoSubscriptionId = checkout.subscription_id;