geekcodev/laravel-max-client
A Laravel client for the MAX messaging platform that simplifies bot integration: configure credentials, send messages, alerts, and notifications, and call the MAX API through a clean, framework-friendly PHP wrapper for Laravel services, jobs, and apps. Built to keep MAX bot calls simple inside Larav...
Minimum path to a working setup on v1.2.2:
composer update geekcodev/max-php-client — the adapter now requires core ^1.1.10. The public API is stable, so no code changes are needed just to upgrade.config/max.php → webapp.session.contact_confirmed (env MAX_WEBAPP_SESSION_CONTACT_CONFIRMED, default contact_confirmed)config/max.php → webhook.update_types (env MAX_WEBHOOK_UPDATE_TYPES, CSV string or array)php artisan max:subscribe <your-webhook-url>. A POST to an existing URL updates the subscription in place — one call is enough.php artisan max:subscriptions and confirm you now see 13 update types (was 6). This step is the single most common source of "I upgraded and nothing changed."First use case — mini-app contact confirmation. Front-end calls window.WebApp.requestContact(), posts the payload (authDate, phone, userId, hash) to a route protected by the max.webapp middleware. In the controller:
$contact = $this->contacts->resolve($authDate, $phone, $userId, $hash);
if ($contact === null) {
abort(403);
}
$this->session->confirm($request, $contact->userId);
Read identity anywhere downstream with WebAppSession::userId($request) / chatId($request) / contactConfirmed($request, $userId). All three classes are container singletons — inject them, don't new them.
Contact verification as a guard, not a middleware. WebAppContactService::resolve() returns a ?WebAppContact (normalized phone, userId, authDate) or null; verify() returns a bool. Use resolve when you need the normalized phone (e.g. to match against a CRM record), use verify when you only gate an action. Keep the verification inside the controller/service that owns the business decision — it isn't a request-level concern.
Session lifecycle around confirmation. WebAppSession::confirm() regenerates the session id (session-fixation defence, mirroring the integration project's WebAppAuthController) and sets the flag. Call it exactly once after a successful resolve(). Use forget() on logout/unlink so a stale confirmation doesn't survive a user switch. Treat contactConfirmed() as "this user id confirmed a contact in this session," not "this session has any confirmed contact" — the value stored is the user_id, and the check is an equality.
Subscription tuning per host. Defaults to 13 types (Config::DEFAULT_WEBHOOK_UPDATE_TYPES): messages, callbacks, bot lifecycle, plus message_edited, message_removed, dialog_cleared, dialog_removed, chat_title_changed, user_added, user_removed. Override per host with MAX_WEBHOOK_UPDATE_TYPES=message_created,message_callback when a deployment genuinely only cares about a subset. Parsing is forgiving: CSV or array, whitespace trimmed, duplicates and non-strings dropped, empty values fall back to the default (you cannot subscribe to nothing).
Two development modes, one codebase. max:listen (Long Polling) for local/dev — no public URL, no signature checking, but it ignores webhook.update_types entirely. Webhook mode in staging/prod honours the filter. Be aware that behavior you observe locally with max:listen is not filtered behavior.
Chat membership is prepared, not wired. user_added / user_removed are now in the default subscription but PersistMaxChatListener returns early on them (default => null). If you need member sync today, add your own listener for those types; don't wait for the built-in one.
Bridge contact ≠ chat button contact. window.WebApp.requestContact() (mini-app bridge, verified via authDate/phone/userId HMAC) is a completely separate mechanism from the in-chat request_contact button (vcf_info + token HMAC). PersistMaxUserPhoneListener writes to max_users.phone only for the chat-button path. If you expect the bridge flow to populate that column, you'll need to do it yourself after resolve().
contact_confirmed (bare). If you're porting working code that used e.g. max.webapp.contact_confirmed, either set MAX_WEBAPP_SESSION_CONTACT_CONFIRMED=max.webapp.contact_confirmed or accept that previously confirmed sessions re-authenticate. Decide deliberately — silent re-auth prompts in production are worse than a conscious choice.confirm() regenerates the session id. Anything holding the old id (in-flight AJAX with CSRF token, polling loop keyed on session) will 419/401. Sequence front-end state so the confirmation response completes before the next authenticated call.+ and separators) before verification, so the front-end may send +7 (999) 123-45-67 or 79991234567 — both verify. Don't normalize yourself and re-sign; you'll just introduce a mismatch class of bugs.max_age = 0 disables replay protection. Useful for tests, dangerous in prod. The auth_date freshness check is your only replay defence on the bridge flow.warning with the fact and user_id only — no phone, no hash (OWASP A09). Don't "helpfully" add the payload to your own logs; you'll undo the privacy posture of the release.WebAppContext::verifyContact(). The optional facade method from the plan was dropped to keep WebAppContext narrow. Inject WebAppContactService directly; don't hunt for a facade shortcut that doesn't exist.webhook.update_types is a delivery filter, not a listener whitelist. Types excluded there are never delivered; types included but unhandled (today: user_added, user_removed) arrive and are silently dropped by the default listener. Both failure modes are silent — audit your listeners against the configured list whenever you change it.user_added/user_removed registered alongside PersistMaxChatListener; the persistence layer for chats already exists, so you're adding a handler, not a subsystem.^1.1.8 → ^1.1.10). If you pin core elsewhere or have a lockfile from before the security release, composer update may resolve differently than expected — run composer why geekcodev/max-php-client before debugging adapter behavior.How can I help you explore Laravel packages today?