Add BSUID support#245
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds Business‑Scoped User ID (BSUID) support across outbound message sending (via a new optional recipient field) and improves webhook customer identification by supporting user_id/parent_user_id in inbound payloads.
Changes:
- Add optional BSUID
recipientsupport toMessage/MessageRequestand mostWhatsAppCloudApi::send*()methods. - Refactor message request bodies to share common fields (
to/recipient/context) viaMessageRequest::build(). - Extend webhook
Customerto supportuser_id(BSUID) and add tests/docs for BSUID scenarios.
Reviewed changes
Copilot reviewed 44 out of 44 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Unit/WhatsAppCloudApiTest.php | Updates existing unit tests for new method signatures and adds recipient-only send tests + missing-recipient validation test. |
| tests/Unit/WebHook/NotificationFactoryTest.php | Adds tests for BSUID-only webhook payloads and WA ID precedence when both IDs exist. |
| tests/Integration/WhatsAppCloudApiTest.php | Updates integration tests to match the new sendContact() signature. |
| src/WhatsAppCloudApi.php | Adds optional recipient parameter across send methods and wires it into message objects. |
| src/WebHook/Notification/Support/CustomerIdType.php | Introduces a small type wrapper for customer identifier type (wa_id vs user_id). |
| src/WebHook/Notification/Support/Customer.php | Expands customer identity model to include WA ID vs BSUID and additional profile fields. |
| src/WebHook/Notification/MessageNotificationFactory.php | Populates the extended Customer model from webhook payloads (including BSUID fields). |
| src/Request/MessageRequest/RequestVideoMessage.php | Switches to shared request-body builder and then adds video-specific payload. |
| src/Request/MessageRequest/RequestTextMessage.php | Switches to shared request-body builder and then adds text-specific payload. |
| src/Request/MessageRequest/RequestTemplateMessage.php | Switches to shared request-body builder and then adds template-specific payload. |
| src/Request/MessageRequest/RequestStickerMessage.php | Switches to shared request-body builder and then adds sticker-specific payload. |
| src/Request/MessageRequest/RequestSingleProductMessage.php | Switches to shared request-body builder and then adds single-product payload. |
| src/Request/MessageRequest/RequestReactionMessage.php | Switches to shared request-body builder and then adds reaction-specific payload. |
| src/Request/MessageRequest/RequestOptionsListMessage.php | Switches to shared request-body builder and then adds list-interactive payload. |
| src/Request/MessageRequest/RequestMultiProductMessage.php | Switches to shared request-body builder and then adds multi-product payload. |
| src/Request/MessageRequest/RequestLocationRequestMessage.php | Switches to shared request-body builder and then adds location-request interactive payload. |
| src/Request/MessageRequest/RequestLocationMessage.php | Switches to shared request-body builder and then adds location-specific payload. |
| src/Request/MessageRequest/RequestImageMessage.php | Switches to shared request-body builder and then adds image-specific payload. |
| src/Request/MessageRequest/RequestDocumentMessage.php | Switches to shared request-body builder and then adds document-specific payload. |
| src/Request/MessageRequest/RequestCtaUrlMessage.php | Switches to shared request-body builder and then adds CTA URL interactive payload. |
| src/Request/MessageRequest/RequestContactMessage.php | Switches to shared request-body builder and then adds contact payload. |
| src/Request/MessageRequest/RequestCatalogMessage.php | Switches to shared request-body builder and then adds catalog interactive payload. |
| src/Request/MessageRequest/RequestButtonReplyMessage.php | Switches to shared request-body builder and then adds button-interactive payload. |
| src/Request/MessageRequest/RequestAudioMessage.php | Switches to shared request-body builder and then adds audio-specific payload. |
| src/Request/MessageRequest.php | Adds centralized body()/build() to include to/recipient and reply context consistently. |
| src/Message/VideoMessage.php | Allows nullable $to and threads optional $recipient into the base Message. |
| src/Message/TextMessage.php | Allows nullable $to and threads optional $recipient into the base Message. |
| src/Message/TemplateMessage.php | Allows nullable $to and threads optional $recipient into the base Message. |
| src/Message/StickerMessage.php | Allows nullable $to and threads optional $recipient into the base Message. |
| src/Message/SingleProductMessage.php | Allows nullable $to and threads optional $recipient into the base Message. |
| src/Message/ReactionMessage.php | Allows nullable $to and threads optional $recipient into the base Message. |
| src/Message/OptionsListMessage.php | Allows nullable $to and threads optional $recipient into the base Message. |
| src/Message/MultiProductMessage.php | Allows nullable $to and threads optional $recipient into the base Message. |
| src/Message/Message.php | Adds recipient field and enforces that at least one of to/recipient is provided. |
| src/Message/LocationRequestMessage.php | Allows nullable $to and threads optional $recipient into the base Message. |
| src/Message/LocationMessage.php | Allows nullable $to and threads optional $recipient into the base Message. |
| src/Message/ImageMessage.php | Allows nullable $to and threads optional $recipient into the base Message. |
| src/Message/DocumentMessage.php | Allows nullable $to and threads optional $recipient into the base Message. |
| src/Message/CtaUrlMessage.php | Allows nullable $to and threads optional $recipient into the base Message. |
| src/Message/ContactMessage.php | Allows nullable $to and threads optional $recipient into the base Message. |
| src/Message/CatalogMessage.php | Allows nullable $to and threads optional $recipient into the base Message. |
| src/Message/ButtonReplyMessage.php | Allows nullable $to and threads optional $recipient into the base Message. |
| src/Message/AudioMessage.php | Adds recipient support to audio messages (but with a different parameter order vs other message types). |
| README.md | Documents how to use BSUID recipient support and updates sendContact() example usage. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public function sendContact(?string $to, ContactName $name, ?string $recipient = null, Phone ...$phone): Response | ||
| { | ||
| $message = new Message\ContactMessage($to, $name, $this->reply_to, ...$phone); | ||
| $message = new Message\ContactMessage($to, $name, $this->reply_to, $recipient, ...$phone); |
There was a problem hiding this comment.
sendContact() is now a breaking change for existing callers: the third positional argument used to be the first Phone, but it is now ?string $recipient, so calls like sendContact($to, $name, $phone) will throw a TypeError. To keep backward compatibility, consider avoiding inserting a new parameter before the variadic (e.g., introduce a new method for BSUID contacts, or accept a union for the 3rd argument and detect whether it's a Phone vs recipient string, shifting arguments accordingly).
| public function __construct(?string $to, MediaID $id, ?string $recipient, ?string $reply_to) | ||
| { | ||
| $this->id = $id; | ||
|
|
||
| parent::__construct($to, $reply_to); | ||
| parent::__construct($to, $recipient, $reply_to); | ||
| } |
There was a problem hiding this comment.
AudioMessage’s constructor parameter order and defaults are inconsistent with the other Message subclasses (recipient is required in the signature and comes before $reply_to). This makes it easy to accidentally swap $recipient/$reply_to and is also a backwards-incompatible change for anyone instantiating AudioMessage directly. Consider aligning it with the common pattern used elsewhere (e.g., $reply_to first, and $recipient optional at the end).
| * @param bool Determines if show a preview box for URLs contained in the text message. | ||
| * | ||
| * @throws Response\ResponseException | ||
| */ | ||
| public function sendTextMessage(string $to, string $text, bool $preview_url = false): Response | ||
| public function sendTextMessage(?string $to, string $text, bool $preview_url = false, ?string $recipient = null): Response |
There was a problem hiding this comment.
The PHPDoc for sendTextMessage() no longer matches the method signature: $to is now nullable and there is a new optional $recipient parameter, but the docblock still documents only the old parameters. Please update the docblock to reflect the new signature so IDEs/static analysis and generated docs stay accurate.
No description provided.