From 906d0cd5be5104b34e10e1c844df0770b440aa24 Mon Sep 17 00:00:00 2001 From: derrickobedgiu1 Date: Tue, 23 Apr 2024 20:11:35 +0300 Subject: [PATCH] Add Multi Products --- README.md | 44 ++++++++++ src/Message/MultiProduct/Action.php | 27 +++++++ src/Message/MultiProduct/Row.php | 18 +++++ src/Message/MultiProduct/Section.php | 34 ++++++++ src/Message/MultiProductMessage.php | 65 +++++++++++++++ .../RequestMultiProductMessage.php | 40 ++++++++++ src/WhatsAppCloudApi.php | 28 +++++++ tests/Integration/WhatsAppCloudApiTest.php | 43 ++++++++++ tests/Unit/WhatsAppCloudApiTest.php | 80 +++++++++++++++++++ 9 files changed, 379 insertions(+) create mode 100644 src/Message/MultiProduct/Action.php create mode 100644 src/Message/MultiProduct/Row.php create mode 100644 src/Message/MultiProduct/Section.php create mode 100644 src/Message/MultiProductMessage.php create mode 100644 src/Request/MessageRequest/RequestMultiProductMessage.php diff --git a/README.md b/README.md index 49da677..d099cdd 100644 --- a/README.md +++ b/README.md @@ -297,6 +297,49 @@ $whatsapp_cloud_api->sendButton( ); ``` +### Send Multi Product Message +```php +'), + new Row(''), + // etc +]; + +$rows_section_2 = [ + new Row(''), + new Row(''), + new Row(''), + // etc +]; + +$sections = [ + new Section('Section 1', $rows_section_1), + new Section('Section 2', $rows_section_2), +]; + +$action = new Action($sections); +$catalog_id = ''; +$header = 'Grocery Collections'; +$body = 'Hello! Thanks for your interest. Here\'s what we can offer you under our grocery collection. Thank you for shopping with us.'; +$footer = 'Subject to T&C'; + +$whatsapp_cloud_api->sendMultiProduct( + '', + $catalog_id, + $action, + $header, + $body, + $footer // optional +); +``` + ### Replying messages You can reply a previous sent message: @@ -458,6 +501,7 @@ Fields list: https://developers.facebook.com/docs/whatsapp/cloud-api/reference/b - Send Contacts - Send Lists - Send Buttons +- Send Multi Product Message - Upload media resources to WhatsApp servers - Download media resources from WhatsApp servers - Mark messages as read diff --git a/src/Message/MultiProduct/Action.php b/src/Message/MultiProduct/Action.php new file mode 100644 index 0000000..530a41f --- /dev/null +++ b/src/Message/MultiProduct/Action.php @@ -0,0 +1,27 @@ +sections = $sections; + } + + public function sections(): array + { + $result = []; + + foreach ($this->sections as $section) { + $result[] = [ + 'title' => $section->title(), + 'product_items' => $section->rows(), + ]; + } + + return $result; + } +} diff --git a/src/Message/MultiProduct/Row.php b/src/Message/MultiProduct/Row.php new file mode 100644 index 0000000..ef66589 --- /dev/null +++ b/src/Message/MultiProduct/Row.php @@ -0,0 +1,18 @@ +product_retailer_id = $product_retailer_id; + } + + public function product_retailer_id(): string + { + return $this->product_retailer_id; + } +} diff --git a/src/Message/MultiProduct/Section.php b/src/Message/MultiProduct/Section.php new file mode 100644 index 0000000..69cc375 --- /dev/null +++ b/src/Message/MultiProduct/Section.php @@ -0,0 +1,34 @@ +title = $title; + $this->rows = $rows; + } + + public function title(): string + { + return $this->title; + } + + public function rows(): array + { + $result = []; + + foreach ($this->rows as $row) { + $result[] = [ + 'product_retailer_id' => $row->product_retailer_id(), + ]; + } + + return $result; + } +} diff --git a/src/Message/MultiProductMessage.php b/src/Message/MultiProductMessage.php new file mode 100644 index 0000000..c42e8e7 --- /dev/null +++ b/src/Message/MultiProductMessage.php @@ -0,0 +1,65 @@ +header = $header; + $this->body = $body; + $this->catalog_id = $catalog_id; + $this->footer = $footer; + $this->action = $action; + + parent::__construct($to, $reply_to); + } + + public function header(): array + { + return [ + 'type' => 'text', + 'text' => $this->header, + ]; + } + + public function body(): string + { + return $this->body; + } + + public function catalog_id(): int + { + return $this->catalog_id; + } + + public function footer(): ?string + { + return $this->footer; + } + + public function sections(): array + { + return $this->action->sections(); + } +} diff --git a/src/Request/MessageRequest/RequestMultiProductMessage.php b/src/Request/MessageRequest/RequestMultiProductMessage.php new file mode 100644 index 0000000..6dad6b1 --- /dev/null +++ b/src/Request/MessageRequest/RequestMultiProductMessage.php @@ -0,0 +1,40 @@ + $this->message->messagingProduct(), + 'recipient_type' => $this->message->recipientType(), + 'to' => $this->message->to(), + 'type' => 'interactive', + 'interactive' => [ + 'type' => $this->message->type(), + 'header' => $this->message->header(), + 'body' => ['text' => $this->message->body()], + 'action' => [ + 'catalog_id' => $this->message->catalog_id(), + 'sections' => $this->message->sections(), + ], + ], + ]; + + if ($this->message->footer()) { + $body['interactive']['footer'] = ['text' => $this->message->footer()]; + } + + if ($this->message->replyTo()) { + $body['context']['message_id'] = $this->message->replyTo(); + } + + return $body; + } +} diff --git a/src/WhatsAppCloudApi.php b/src/WhatsAppCloudApi.php index d8842e0..19abe1a 100644 --- a/src/WhatsAppCloudApi.php +++ b/src/WhatsAppCloudApi.php @@ -7,6 +7,7 @@ use Netflie\WhatsAppCloudApi\Message\Contact\Phone; use Netflie\WhatsAppCloudApi\Message\CtaUrl\Header; use Netflie\WhatsAppCloudApi\Message\Media\MediaID; +use Netflie\WhatsAppCloudApi\Message\MultiProduct\Action as MultiProductAction; use Netflie\WhatsAppCloudApi\Message\OptionsList\Action; use Netflie\WhatsAppCloudApi\Message\Template\Component; @@ -395,6 +396,33 @@ public function uploadMedia(string $file_path): Response return $this->client->uploadMedia($request); } + /** + * Sends a message with multiple products to a user. + * + * @param string $to The WhatsApp ID or phone number for the person you want to send a message to. + * @param int $catalog_id The ID of the catalog where the products are located. + * @param MultiProductAction $action The contents of the catalog products to be sent. + * @param string $header The header of the message. + * @param string $body The body of the message. + * @param string|null $footer The footer of the message. + * + * @return Response The response object containing the result of the API request. + * + * @throws Response\ResponseException If the API request fails. + */ + public function sendMultiProduct(string $to, int $catalog_id, MultiProductAction $action, string $header, string $body, ?string $footer = '') + { + $message = new Message\MultiProductMessage($to, $catalog_id, $action, $header, $body, $footer, $this->reply_to); + $request = new Request\MessageRequest\RequestMultiProductMessage( + $message, + $this->app->accessToken(), + $this->app->fromPhoneNumberId(), + $this->timeout + ); + + return $this->client->sendMessage($request); + } + /** * Download a media file (image, audio, video...) from Facebook servers. * diff --git a/tests/Integration/WhatsAppCloudApiTest.php b/tests/Integration/WhatsAppCloudApiTest.php index 65a49ac..d8245bf 100644 --- a/tests/Integration/WhatsAppCloudApiTest.php +++ b/tests/Integration/WhatsAppCloudApiTest.php @@ -10,6 +10,9 @@ use Netflie\WhatsAppCloudApi\Message\CtaUrl\TitleHeader; use Netflie\WhatsAppCloudApi\Message\Media\LinkID; use Netflie\WhatsAppCloudApi\Message\Media\MediaObjectID; +use Netflie\WhatsAppCloudApi\Message\MultiProduct\Action as MultiProductAction; +use Netflie\WhatsAppCloudApi\Message\MultiProduct\Row as MultiProductRow; +use Netflie\WhatsAppCloudApi\Message\MultiProduct\Section as MultiProductSection; use Netflie\WhatsAppCloudApi\Message\OptionsList\Action; use Netflie\WhatsAppCloudApi\Message\OptionsList\Row; use Netflie\WhatsAppCloudApi\Message\OptionsList\Section; @@ -304,6 +307,46 @@ public function test_send_catalog_message() $this->assertEquals(false, $response->isError()); } + public function test_send_multi_product() + { + $this->markTestIncomplete( + 'This test should send a real catalog ID and product SKU ID.' + ); + + $rows_section_1 = [ + new MultiProductRow(''), + // can add more + ]; + + $rows_section_2 = [ + new MultiProductRow(''), + // can add more + ]; + + $sections = [ + new MultiProductSection('Section 1', $rows_section_1), + new MultiProductSection('Section 2', $rows_section_2), + ]; + + $action = new MultiProductAction($sections); + $catalog_id = ''; + $header = 'Grocery Collections'; + $body = 'Hello! Thanks for your interest. Here\'s what we can offer you under our grocery collection. Thank you for shopping with us.'; + $footer = 'Subject to T&C'; + + $response = $this->whatsapp_app_cloud_api->sendMultiProduct( + WhatsAppCloudApiTestConfiguration::$to_phone_number_id, + $catalog_id, + $action, + $header, + $body, + $footer // optional + ); + + $this->assertEquals(200, $response->httpStatusCode()); + $this->assertEquals(false, $response->isError()); + } + public function test_send_reply_buttons() { $buttonRows = [ diff --git a/tests/Unit/WhatsAppCloudApiTest.php b/tests/Unit/WhatsAppCloudApiTest.php index afabca0..660f6e1 100644 --- a/tests/Unit/WhatsAppCloudApiTest.php +++ b/tests/Unit/WhatsAppCloudApiTest.php @@ -14,6 +14,9 @@ use Netflie\WhatsAppCloudApi\Message\CtaUrl\TitleHeader; use Netflie\WhatsAppCloudApi\Message\Media\LinkID; use Netflie\WhatsAppCloudApi\Message\Media\MediaObjectID; +use Netflie\WhatsAppCloudApi\Message\MultiProduct\Action as MultiProductAction; +use Netflie\WhatsAppCloudApi\Message\MultiProduct\Row as MultiProductRow; +use Netflie\WhatsAppCloudApi\Message\MultiProduct\Section as MultiProductSection; use Netflie\WhatsAppCloudApi\Message\OptionsList\Action; use Netflie\WhatsAppCloudApi\Message\OptionsList\Row; use Netflie\WhatsAppCloudApi\Message\OptionsList\Section; @@ -1229,6 +1232,83 @@ public function test_send_catalog_message() $this->assertEquals(false, $response->isError()); } + public function test_send_multi_product() + { + $to = $this->faker->phoneNumber; + $url = $this->buildMessageRequestUri(); + $header = $this->faker->text(20); + $message = $this->faker->text(1024); + $footer = $this->faker->text(60); + $catalog_id = $this->faker->randomNumber(); + + $rows1 = [ + ['product_retailer_id' => $this->faker->uuid], + ['product_retailer_id' => $this->faker->uuid], + ['product_retailer_id' => $this->faker->uuid], + ]; + + $rows2 = [ + ['product_retailer_id' => $this->faker->uuid], + ['product_retailer_id' => $this->faker->uuid], + ]; + + + $sections = [ + ['title' => $this->faker->text, 'product_items' => $rows1], + ['title' => $this->faker->text, 'product_items' => $rows2], + ]; + $actions = ['catalog_id' => $catalog_id, 'sections' => $sections]; + + $body = [ + 'messaging_product' => 'whatsapp', + 'recipient_type' => 'individual', + 'to' => $to, + 'type' => 'interactive', + 'interactive' => [ + 'type' => 'product_list', + 'header' => ['type' => 'text', 'text' => $header], + 'body' => ['text' => $message], + 'footer' => ['text' => $footer], + 'action' => $actions, + ], + ]; + + $headers = [ + 'Authorization' => 'Bearer ' . $this->access_token, + ]; + + $this->client_handler + ->postJsonData($url, $body, $headers, Argument::type('int')) + ->shouldBeCalled() + ->willReturn(new RawResponse($headers, $this->successfulMessageNodeResponse(), 200)); + + $actionSections = []; + + foreach ($actions['sections'] as $section) { + $sectionRows = []; + + foreach ($section['product_items'] as $row) { + $sectionRows[] = new MultiProductRow($row['product_retailer_id']); + } + + $actionSections[] = new MultiProductSection($section['title'], $sectionRows); + } + + $response = $this->whatsapp_app_cloud_api->sendMultiProduct( + $to, + $catalog_id, + new MultiProductAction($actionSections), + $header, + $message, + $footer + ); + + $this->assertEquals(200, $response->httpStatusCode()); + $this->assertEquals(json_decode($this->successfulMessageNodeResponse(), true), $response->decodedBody()); + $this->assertEquals($this->successfulMessageNodeResponse(), $response->body()); + $this->assertEquals(false, $response->isError()); + } + public function test_mark_a_message_as_read() { $url = $this->buildMessageRequestUri();