diff --git a/stripe_to_invoice/app/api/stripe/webhook/route.ts b/stripe_to_invoice/app/api/stripe/webhook/route.ts index 9612248..8d7255c 100644 --- a/stripe_to_invoice/app/api/stripe/webhook/route.ts +++ b/stripe_to_invoice/app/api/stripe/webhook/route.ts @@ -162,10 +162,10 @@ export async function POST(req: NextRequest) { console.log("👤 [WEBHOOK] Customer name:", name); - // --- Contact (email-only is fine for v1) + // --- Contact resolution: email → name → create with ZWS retry (up to 5 times) console.log("🔍 [WEBHOOK] Looking for existing contact with email:", email); - const contactsResponse = await xero.accountingApi.getContacts( + let contactsResponse = await xero.accountingApi.getContacts( xeroConn.tenantId, undefined, `EmailAddress=="${email}"` @@ -173,15 +173,75 @@ export async function POST(req: NextRequest) { let contact = contactsResponse.body.contacts?.[0]; + // If not found by email, search by name + if (!contact) { + console.log("🔍 [WEBHOOK] Email not found, searching by name:", name); + contactsResponse = await xero.accountingApi.getContacts( + xeroConn.tenantId, + undefined, + `Name=="${name}"` + ); + contact = contactsResponse.body.contacts?.[0]; + + if (contact) { + console.log("✅ [WEBHOOK] Found existing contact by name:", contact.contactID); + } + } else { + console.log("✅ [WEBHOOK] Found existing contact by email:", contact.contactID); + } + + // If still not found, create new contact (with retry logic for duplicate names) if (!contact) { console.log("➕ [WEBHOOK] Creating new contact"); - const created = await xero.accountingApi.createContacts( - xeroConn.tenantId, - { contacts: [{ name, emailAddress: email }] } - ); - contact = created.body.contacts?.[0]; - } else { - console.log("✅ [WEBHOOK] Found existing contact:", contact.contactID); + let attempt = 0; + const maxAttempts = 5; + + while (attempt < maxAttempts && !contact) { + let contactName = name; + + // Add zero-width spaces for uniqueness on retries + if (attempt > 0) { + contactName = name + "\u200B".repeat(attempt); + console.log(`🔄 [WEBHOOK] Retry attempt ${attempt}/${maxAttempts - 1} with ${attempt} zero-width space(s)`); + } + + try { + const created = await xero.accountingApi.createContacts( + xeroConn.tenantId, + { contacts: [{ name: contactName, emailAddress: email }] } + ); + contact = created.body.contacts?.[0]; + + if (contact) { + console.log(`✅ [WEBHOOK] Contact created successfully on attempt ${attempt + 1}:`, contact.contactID); + break; + } + } catch (err: any) { + console.log(`⚠️ [WEBHOOK] Attempt ${attempt + 1} failed:`, err.message); + + // Try to fetch existing contact by name + if (attempt < maxAttempts - 1) { + const existingByName = await xero.accountingApi.getContacts( + xeroConn.tenantId, + undefined, + `Name=="${name}"` + ); + + if (existingByName.body.contacts?.length) { + contact = existingByName.body.contacts[0]; + console.log("✅ [WEBHOOK] Reusing existing contact with duplicate name:", contact.contactID); + break; + } + } + } + + attempt++; + } + + if (!contact) { + console.error("❌ [WEBHOOK] Failed to create or reuse contact after 5 attempts"); + throw new Error("Failed to create Xero contact after multiple attempts"); + } } if (!contact?.contactID) {