# Module: zohoapi (`b2g_zohoapi/`)
Purpose: Zoho CRM integration pack — receives registration payloads from usermgmt and upserts Accounts, Contacts and `Portal_Registration_Module` records in Zoho CRM. Pure HTTP integration. Owner: Sanjay.
Entry points: `web/index.php` → `config/web.php`; `modules/apizoho/controllers/SupplierController.php` (`POST apizoho/supplier/create`), `StatusController.php` (`index`, `ping`); services in `components/` (`ZohoAuthService`, `ZohoApiClient`, `ZohoHttp`, `ZohoSupplierService`, `ZohoOrganizationService`, `ZohoPortal*Service`); console `commands/ZohoController.php` (`php yii zoho/exchange-grant|test-auth|fields|layouts|clear-token|push-payload`). Full design: `ZOHO_INTEGRATION.md`.
Data: none — no `db` component is registered (`config/db-*.php` is loaded but unused). usermgmt owns the DB side (`membercompanymst_tbl.mcm_regfrom`, `zohoapidtls_tbl` audit rows in `bgi_b2g_reg_*`).
Calls: Zoho Accounts (`accountsUrl`) + Zoho CRM API v8 (`apiBaseUrl`), settings in `params['zoho']`, field map `config/zoho-fieldmap.php`. No sibling calls. Inbound: usermgmt `ZohoSync` console child via `ZOHOAPI_BASE_URL` → usermgmt `zohoapiUrl`.
Env selection: `APP_ENV` first, else hostname (`demo.blu2green.earth`→demo, `blu2green.earth`→prod, else local) — see `config/web.php`, which loads `params-{env}.php` directly. `config/params.php` + `config/env.php` (merge `params-common.php`, map `blu2green.earth`→uat) are used only by `config/console.php`, so web and console can resolve different params.
Invariants:
- Auth is JWT Bearer (`HttpBearerAuth` + `components/JwtHelper.php`, `controllers/BaseApiController.php`); `jwtSecret` must match usermgmt's or every call 401s.
- Zoho `layouts` ids are org-specific — re-list (`php yii zoho/layouts`) after switching sandbox ↔ production org.
- For NIBE/JSRS-origin companies (`mcm_regfrom=1`) an existing CRM Account must never be overwritten beyond the B2G registration fields; record matching requires exactly one hit.
- Never copy the Zoho client secret / refresh token committed in params files.
Gotchas:
- 2026-10-05: prod registrations landed in the Zoho sandbox org because the deployed tier loaded sandbox literals (web.php comment). The comment says prod values come from `ZOHO_*` env vars read by `params-local.php`, but that file has no `getenv()` calls (verify which env/params prod actually loads).
- Access token cached in Yii `FileCache` (~55 min TTL) — per-container; console and web must keep the same cache config to share it. Grant codes are single-use, and a failed exchange burns them.
- Zoho search index lags creation by up to ~2 min; duplicate pushes are suppressed on the usermgmt side (`dedupeWindowSeconds`).
- `log` has only a runtime `FileTarget` — no stderr mirror, so app errors likely don't reach CloudWatch (verify). `sslVerify` is `false` in `params-local.php`.
- `vendor/` not committed: `setup.sh`/`setup.bat` run `composer install`; the Dockerfile `deps` stage does it for images.
Prod debugging: CloudWatch `/ecs/b2g-prod/zohoapi` (demo: `/ecs/b2g-demo/zohoapi`); health `GET /healthz` on :80; ECS service `zohoapi`, task family `b2g-zohoapi` / `b2g-demo-zohoapi`. Runbooks: `docs/runbooks/`.
