Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Max Client Laravel Package

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...

View on GitHub
Deep Wiki
Context7

Getting Started

Minimum path to a working setup on v1.2.2:

  1. 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.
  2. Review the two config surfaces this release touches:
    • 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)
  3. Re-subscribe if you are upgrading from < 1.2.2: php artisan max:subscribe <your-webhook-url>. A POST to an existing URL updates the subscription in place — one call is enough.
  4. Verify with 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.

Implementation Patterns

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().

Gotchas and Tips

  • The re-subscribe step is not optional. The package does not mutate existing subscriptions. The symptom — "upgraded, but edits/deletes/clears don't arrive" — looks like a release bug and isn't. This is a behavioral change shipped in a patch release (owner's explicit decision to ship both plans as v1.2.2); treat it as a minor-version review in your release process, not a drop-in patch.
  • Session flag key was renamed. Default is now 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.
  • Signature is over digits, not the raw string. The core normalizes the phone (strips + 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.
  • Logging is deliberately minimal. On rejection the service logs 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.
  • No 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.
  • Extension point for the gap: the clean place to add member sync is a listener on user_added/user_removed registered alongside PersistMaxChatListener; the persistence layer for chats already exists, so you're adding a handler, not a subsystem.
  • Core upgrade is a real constraint flip (^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.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle