geekcodev/max-php-client
A PHP client for the MAX messaging platform API, offering convenient methods to authenticate, send messages, and handle updates within Laravel or plain PHP applications. Simplifies integration with MAX bots and services.
hash (or with hash: null) threw InvalidResponseException, the webhook controller returned 400 invalid_payload, and the update was dropped entirely and never redelivered once MAX exhausted retries. The official OpenAPI spec declares hash nullable and non-required; our client had tightened it to mandatory in bdfc828. Net effect: "user shares contact → operator never sees it" was a live production defect, not a hypothetical. ContactPhoneExtractor::fromVcf() only delivers business value if the payload survives fromArray() first — this release makes the primitive reachable in the wild.RequestFlowService::telFromVcf() in i2tech) with two user-visible wins: folded vCard lines are now unfolded (previously long numbers were silently truncated at the line break and persisted truncated), and property matching is case-insensitive (tel: vs TEL). Combined with the hash fix, the "contact arrives → CRM record exists" path is now unbroken end-to-end. Fund as a bug-fix ticket, not a dependency bump.ContactVerifier::verify() returns false for empty hash (fail-closed, unchanged), so those numbers arrive unverified. Decide explicitly: can an unverified number create a lead / trigger a callback, or does it land in a review queue? Before v1.1.11 this question was moot because the payload never arrived; now it is a daily occurrence.PRODID:ez-vcard 0.10.3, Russian number, no leading +). International formats remain unverified. Multi-country support is an open backlog item with a soft-launch / canary — not a solved problem.Attachment::$filename now carries the uploaded file's real name (read from top level, falling back to payload). Previously the admin UI showed the service name getfile because the DTO carried no name and consumers derived it from the download URL's last segment. Cheap, visible quality win in the operator experience.suspended no longer throws (previously an unhandled enum value). Video attachments now expose width/height/thumbnail/duration plus rendition streams (mp4_1080 … mp4_144, hls); audio exposes transcription; callbacks expose timestamp/user with a callback.user fallback for Update::$user; subscriptions expose time. Each of these is a product surface that was silently blank — thumbnails in the operator inbox, playback quality selection, transcription-based search, callback attribution. Size them as follow-ups; they are now free to build.ContactAttachmentPayload has no vcf_phone; outbound payloads do. Correct consumer pattern remains $contact->vcfPhone ?? ContactPhoneExtractor::fromVcf((string) $contact->vcfInfo). Platform ask: why one-directional? Document in integration onboarding.TEL is returned; X-TEL ignored. No TYPE=cell preference, no multi-number contacts, no fallback when the first number is a fax/landline. Also newly flagged upstream: FailedUserDetails::$userId === 0 is a sentinel for "absent," so a real user_id: 0 is indistinguishable and drops out of toArray(); FileAttachmentPayload still reads legacy file_size, which the official schema does not confirm (size is now also available). Both deferred to v2.0.0 — note them as accepted limitations.@deprecated from addChatMembers() / AddChatMembersResult, explicitly cancelling the v1.1.6 line item that listed it for removal before v2.0.0. If a roadmap or migration doc says "remove before v2," correct it. Conversely, VideoUrls::$mp4 and $preview remain legacy and are slated for removal in v2.0.0 — that removal warning is now explicit.Adopt when:
400 invalid_payload. That is the headline of this release.vcf_info yourself — the truncation and case-sensitivity bugs are fixed for free, and v1.1.11 additionally removes a silent payload-drop path.vcf_info and the number out of journals (OWASP A09), with logging left to the caller. Appropriate for regulated environments.suspended chats.composer audit clean.Look elsewhere or plan to compensate when:
dev → main, the tag, the GitHub Release and the Packagist publish happen only on explicit request. Do not plan a sprint against it until the tag exists; pin a commit or wait.TYPE-aware selection ("prefer mobile"), or X-TEL handling — not supported.match (ChatStatus) without a default. Adding ChatStatus::Suspended is the release's only behavioral change and will surface as an unhandled-match error at runtime. This is a small but real migration task — audit for match over ChatStatus and add default.integration smoke group did not run this cycle (no MAX_API_TOKEN). Coverage is fixture + manual run — budget manual QA and a staged rollout.To executives (one paragraph): A patch release to our MAX messenger client that fixes a customer-visible failure: contacts shared in chat were being rejected at webhook ingest with 400 and silently dropped, so operators never saw them and CRM never captured the number. It also removes our hand-written parsing code, which had been writing truncated phone numbers into the database — a data-integrity defect with direct CRM and lead-matching consequences. Alongside the fix we gain file names on attachments (operators were seeing getfile), video thumbnails/quality renditions, audio transcripts, and correct handling of suspended chats. There are no breaking API changes — all additions are optional trailing constructor parameters plus one enum value — and test coverage is essentially complete (99.8% of lines). Two things need explicit decisions from us rather than assumptions: whether unverified phone numbers may create leads, and who owns E.164 normalization. Note the release is staged but not yet tagged or published, so scheduling should follow the tag.
To engineering: New in v1.1.11, all additive in trailing constructor positions unless noted:
ContactAttachmentPayload::fromArray() now tolerates missing/null hash (?? ''); wrong type still throws InvalidResponseException. toArray() omits empty hash so round-trips don't emit hash: "". ContactVerifier::verify() with empty hash still returns false (fail-closed) — signature semantics unchanged; MaxContactService untouched.ChatStatus::Suspended added. Audit every exhaustive match (ChatStatus) and add default. This is the only behavioral change in the release.Attachment::$filename (new trailing optional ctor param): fromArray() reads top level first, then payload; toArray() writes top level, omits null. Wrong type throws. FileAttachmentPayload::$size added; legacy file_size retained.max-openapi@9ef38a6 / official x-official-schema: v0.0.33: Subscription::$time; Callback::$timestamp/$user; Update::$user now also falls back to callback.user; VideoAttachmentPayload::$width/$height/$thumbnail and AudioAttachmentPayload::$transcription parsed from both top level and payload (top-level priority on read, written into payload); VideoUrls gains mp4_1080/720/480/360/240/144 and hls (legacy mp4/preview kept, removed in v2.0.0); FailedUserDetails parses both spec form {error_code, user_ids[]} and prod form {user_id, error} with (int $userId, string $error) ctor preserved and relaxed requiredness (0/'' on absence).@deprecated removed from addChatMembers() / AddChatMembersResult — cancels the v1.1.6 deprecation list item. Don't schedule removal before v2.0.0.$contact->vcfPhone ?? ContactPhoneExtractor::fromVcf((string) $contact->vcfInfo); inbound payloads omit vcf_phone. Extractor still logs nothing and throws nothing — vcf_info is PII; log in the caller.composer audit 0 vulnerabilities. New regression tests in AttachmentTest, WebhookHandlerTest (full webhook request with hash-less contact decodes; toArray() emits no empty hash), DtoCoverageTest. Integration smoke group not run (no MAX_API_TOKEN) — cover with manual QA.How can I help you explore Laravel packages today?