PHP port of the QuickBooks CloudEvents webhook sample. Implements OAuth 2.0, signature-validated webhooks, CloudEvents parsing, in-memory storage, dashboard UI, and helper endpoints.
- PHP 8.2+
- Composer
- Ngrok (or similar) for webhook tunneling
composer install
cp .env.example .env
# fill QB_CLIENT_ID, QB_CLIENT_SECRET, QB_REDIRECT_URI, QB_ENVIRONMENT, WEBHOOKS_VERIFIER_TOKEN, BASE_URLcomposer start
ngrok http 5001Use the ngrok URL (e.g., https://your-ngrok-id.ngrok-free.app) as BASE_URL/QB_REDIRECT_URI and when visiting the app. Configure QuickBooks webhooks to https://<ngrok>/webhooks and redirect URI to https://<ngrok>/callback (avoid mixing localhost with ngrok in one flow). The dashboard and /events.json default to showing the last 24 hours; adjust via the hours filter or ?hours= query.
GET /home (connect/disconnect)GET /dashboarddashboard with stats and events (auto-refresh)GET /oauth/connectstart OAuthGET /callbackhandle OAuth redirectGET /oauth/disconnectrevoke & clear sessionPOST /webhooksvalidated webhook endpoint (intuit-signature required)POST /webhooks/testtest endpoint without signatureGET /webhooks/details/{index}view stored eventGET /events.jsonJSON feed of events + statsPOST /api/quickbooks/{entity}fetch entity by id (requires auth, body{ \"id\": \"123\" })GET /healthhealth check
- Webhook signatures use HMAC-SHA256 with
WEBHOOKS_VERIFIER_TOKENover the raw payload and compared to theintuit-signatureheader. - Webhooks are kept in memory (FIFO, max 100). Restarting the app clears them.
- OAuth tokens/realm are stored in session.
- For local debugging,
DISABLE_STATE_CHECKdefaults totrue(state check disabled). This is only for development. For production, set it tofalseand follow QBO OAuth2 CSRF guidelines (state validation on, consistent host/port for connect/callback).
composer test- If OAuth fails, confirm redirect URI matches the QuickBooks app configuration.
- For signature errors, ensure the verifier token matches the webhook configuration and that you forward the raw body unmodified.