Harden QuickBooks OAuth, prevent webhook replays, separate payroll permissions

QBO organization ID now protected via HttpOnly cookie, webhook replays deduplicated, customer/employee IDs validated, and payroll visibility separated from profile editing.

Security & Reliability

QuickBooks Online integration has been hardened against several silent-failure modes:

OAuth state protection — Organization IDs no longer pass through Intuit's OAuth state parameter. They now live in an HttpOnly cookie and are re-verified in the callback, keeping your org ID out of Intuit's logs and Referer headers.

Webhook replay safety — Duplicate webhook deliveries are now deduplicated using an idempotency key derived from realm ID and entity timestamps. A captured-and-replayed delivery is silently dropped, while genuine later changes still process.

Match wizard validation — When linking a member to a QBO employee or client to a QBO customer, the system now confirms the chosen QBO ID actually exists in your realm before saving. Hand-crafted or stale IDs are rejected immediately rather than breaking every future sync silently.

Permissions

Payroll visibility is now gated by a dedicated payroll:read permission (owner and admin only), separate from member profile editing rights (member:update). You can now grant wage visibility independently from profile-edit capability.