# Event Form Production Fixes - Complete Summary

**Last Updated:** 2026-01-28

## All Issues Fixed

### Issue 1: Headers Set Before Includes ✅ FIXED

**Problem:** Headers were set BEFORE includes, causing "headers already sent" errors if includes output anything.

**Fix:** Moved headers to AFTER includes, matching working APIs (`collect-lead.php`, `submit-template.php`).

### Issue 2: Wrong Contact ID Extraction ✅ FIXED

**Problem:** Code was treating `inlineMessage` (success message) as contact ID.

**Root Cause:** HubSpot Forms API v3 doesn't return `contactId` - only returns `redirectUri` and `inlineMessage`.

**Fix:** Removed incorrect `inlineMessage` as contactId. Now only uses actual contact ID fields or fetches by email.

### Issue 3: Wrong API Endpoint for Contact Lookup ✅ FIXED

**Problem:** `fetchContactIdByEmail()` was using wrong endpoint format:

- Used: `GET /crm/v3/objects/contacts?email=...` (doesn't exist)
- Should use: `POST /crm/v3/objects/contacts/search` with filterGroups

**Fix:** Updated to use correct POST search endpoint with filterGroups.

### Issue 4: Missing Directories ✅ FIXED

**Problem:** Attempted to create directories defensively, but can't change permissions in production.

**Fix:** Removed directory creation. Logger handles missing directories gracefully.

### Issue 5: Output Buffering at Start ✅ FIXED

**Problem:** Output buffering at file start could conflict with production environment.

**Fix:** Removed output buffering at start. Only use in `outputJsonResponse()` helper.

### Issue 6: Lock Directory Path ✅ FIXED

**Problem:** Lock directory path was `/../../writable/locks` but diagnostics expected `/../locks`.

**Fix:** Changed to `/../locks` to match diagnostics.

## Critical Fixes Summary

### 1. Header Placement (CRITICAL)

```php
// ✅ CORRECT: Includes FIRST
require_once __DIR__ . '/../config/hubspot-config.php';
require_once __DIR__ . '/../config/hubspot-api-helpers.php';
require_once __DIR__ . '/../helpers/logger.php';

// THEN headers
header('Content-Type: application/json; charset=utf-8');
header('X-Content-Type-Options: nosniff');
header('Cache-Control: no-cache, must-revalidate');
```

### 2. Contact ID Extraction (CRITICAL)

```php
// ✅ CORRECT: Don't use inlineMessage as contactId
if (isset($response['contactId'])) {
    $contactId = $response['contactId'];
} elseif (isset($response['contact'])) {
    $contactId = $response['contact']['id'] ?? null;
}
// NOTE: inlineMessage is NOT a contact ID - it's just a success message

// Always fetch by email to verify
$fetchedContactId = fetchContactIdByEmail($email);
```

### 3. Contact Lookup API (CRITICAL)

```php
// ✅ CORRECT: Use POST /search endpoint
$searchUrl = HUBSPOT_API_BASE_URL . '/crm/v3/objects/contacts/search';
$searchData = [
    'filterGroups' => [
        [
            'filters' => [
                [
                    'propertyName' => 'email',
                    'operator' => 'EQ',
                    'value' => strtolower(trim($email))
                ]
            ]
        ]
    ],
    'properties' => ['email', 'id'],
    'limit' => 1
];
$result = makeHubSpotAPICall($searchUrl, 'POST', ..., json_encode($searchData), ...);
```

## Testing

### Test Scripts Created

1. **`v2/scripts/test-production-submission.php`**
   - Tests response headers
   - Tests JSON validity
   - Tests CORB issues

2. **`v2/scripts/test-event-api-endpoint.php`**
   - Tests actual API endpoint
   - Tests contact ID extraction
   - Tests success condition

### Diagnostics Endpoint

**URL:** `https://www.ordio.com/v2/api/event-lead-capture-diagnostics.php?password=elc-diagnostic-2026&test=true`

**Current Status:**

- ✅ API token configured
- ✅ Form GUID correct
- ✅ Portal ID correct
- ✅ API connectivity works (HTTP 200)
- ✅ Test submission works (HTTP 200, inlineMessage returned)
- ⚠️ Directories don't exist (expected - can't create in production)
- ⚠️ Lock directory doesn't exist (expected - can't create in production)

## Files Modified

- `v2/api/event-lead-capture.php` - All fixes applied
- `v2/js/event-form.js` - Improved JSON parsing
- `v2/scripts/test-production-submission.php` - Production testing script
- `v2/scripts/test-event-api-endpoint.php` - Endpoint testing script

## Next Steps

1. **Deploy to Production**
   - Deploy updated `event-lead-capture.php`
   - Verify syntax: `php -l v2/api/event-lead-capture.php`

2. **Test in Production**

   ```bash
   php v2/scripts/test-event-api-endpoint.php https://www.ordio.com
   ```

3. **Verify in Browser**
   - Submit form in browser DevTools
   - Check Network tab for API response
   - Verify response has `contactId` in data
   - Check for CORB errors

4. **Verify in HubSpot**
   - Check HubSpot contacts
   - Verify contact was created
   - Verify all fields are correct

5. **Monitor Logs**
   - Check for contact ID warnings
   - Verify submissions are being logged
   - Check for any errors

## Expected Behavior After Fix

1. **Form Submission:**
   - Frontend submits form data
   - API receives request
   - Headers are set correctly (after includes)
   - HubSpot API is called
   - Response is checked for errors
   - Contact ID is fetched by email (using correct endpoint)
   - Success response includes contactId

2. **Success Response:**

   ```json
   {
     "success": true,
     "message": "Contact saved successfully",
     "data": {
       "contactId": "12345678",
       "eventName": "Intergastra 2026",
       "httpCode": 200,
       "contactExists": false
     }
   }
   ```

3. **If Contact ID Not Found:**
   - Warning logged (contact may be created asynchronously)
   - Success still returned (HubSpot Forms API v3 may create contacts async)
   - Contact should appear in HubSpot shortly

## Troubleshooting

### If Contact ID Still Missing

1. **Check HubSpot:**
   - Search for email in HubSpot
   - Verify contact was created
   - Check creation timestamp

2. **Check Logs:**
   - Look for "Contact ID not found" warnings
   - Check if fetchContactIdByEmail is being called
   - Verify search endpoint is working

3. **Test Contact Lookup:**
   ```bash
   # Test if contact lookup works
   curl -X POST "https://api.hubapi.com/crm/v3/objects/contacts/search" \
     -H "Authorization: Bearer YOUR_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"filterGroups":[{"filters":[{"propertyName":"email","operator":"EQ","value":"test@example.com"}]}],"properties":["email","id"],"limit":1}'
   ```

### If Form Still Shows Success But No Contact

1. **Check API Response:**
   - Open DevTools → Network tab
   - Find `/v2/api/event-lead-capture.php` request
   - Check response body
   - Verify `contactId` is present

2. **Check HubSpot Workflows:**
   - Verify workflows aren't deleting contacts
   - Check if contacts are being archived
   - Verify form is configured correctly

3. **Check Field Mappings:**
   - Verify all required fields are mapped
   - Check if any fields are causing validation errors
   - Verify custom fields exist in HubSpot

## Related Documentation

- `docs/systems/forms/EVENT_FORM_IMPLEMENTATION.md` - Complete implementation guide
- `docs/systems/forms/EVENT_FORM_PRODUCTION_FIXES.md` - Production fixes
- `docs/systems/forms/EVENT_FORM_CRITICAL_FIX.md` - Contact ID fix details
- `docs/systems/forms/EVENT_FORM_PRODUCTION_FIXES_V2.md` - Header placement fix
