diff --git a/src/Client.php b/src/Client.php index 58e5ff3..7d70962 100644 --- a/src/Client.php +++ b/src/Client.php @@ -18,6 +18,7 @@ use RetailCrm\Api\Interfaces\RequestTransformerInterface; use RetailCrm\Api\Interfaces\ResponseTransformerInterface; use RetailCrm\Api\ResourceGroup\Api; use RetailCrm\Api\ResourceGroup\Costs; +use RetailCrm\Api\ResourceGroup\CustomerInteraction; use RetailCrm\Api\ResourceGroup\Customers; use RetailCrm\Api\ResourceGroup\CustomersCorporate; use RetailCrm\Api\ResourceGroup\CustomFields; @@ -67,6 +68,9 @@ class Client /** @var \RetailCrm\Api\ResourceGroup\CustomFields */ public $customFields; + /** @var \RetailCrm\Api\ResourceGroup\CustomerInteraction */ + public $customerInteraction; + /** @var \RetailCrm\Api\ResourceGroup\Customers */ public $customers; @@ -187,6 +191,14 @@ class Client $eventDispatcher, $logger ); + $this->customerInteraction = new CustomerInteraction( + $url, + $httpClient, + $requestTransformer, + $responseTransformer, + $eventDispatcher, + $logger + ); $this->customers = new Customers( $url, $httpClient, diff --git a/src/Enum/BySite.php b/src/Enum/BySite.php new file mode 100644 index 0000000..e4b887a --- /dev/null +++ b/src/Enum/BySite.php @@ -0,0 +1,22 @@ +") + * @JMS\SerializedName("droppedAt") + */ + public $droppedAt; + + /** + * @var DateTime + * + * @JMS\Type("DateTime<'Y-m-d H:i:sP'>") + * @JMS\SerializedName("clearedAt") + */ + public $clearedAt; + + /** + * @var string + * + * @JMS\Type("string") + * @JMS\SerializedName("link") + */ + public $link; + + /** + * @var \RetailCrm\Api\Model\Entity\CustomerInteraction\CartCustomer + * + * @JMS\Type("RetailCrm\Api\Model\Entity\CustomerInteraction\CartCustomer") + * @JMS\SerializedName("customer") + */ + public $customer; + + /** + * @var \RetailCrm\Api\Model\Entity\CustomerInteraction\CartItem[] + * + * @JMS\Type("array") + * @JMS\SerializedName("items") + */ + public $items; + + /** + * @var \RetailCrm\Api\Model\Entity\CustomerInteraction\CartOrder + * + * @JMS\Type("RetailCrm\Api\Model\Entity\CustomerInteraction\CartOrder") + * @JMS\SerializedName("order") + */ + public $order; +} diff --git a/src/Model/Entity/CustomerInteraction/CartCustomer.php b/src/Model/Entity/CustomerInteraction/CartCustomer.php new file mode 100644 index 0000000..f5079e3 --- /dev/null +++ b/src/Model/Entity/CustomerInteraction/CartCustomer.php @@ -0,0 +1,61 @@ +by = $by; + } + + if ('' !== $siteBy) { + $this->siteBy = $siteBy; + } + } +} diff --git a/src/Model/Request/CustomerInteraction/CustomerInteractionCartClearRequest.php b/src/Model/Request/CustomerInteraction/CustomerInteractionCartClearRequest.php new file mode 100644 index 0000000..3534af1 --- /dev/null +++ b/src/Model/Request/CustomerInteraction/CustomerInteractionCartClearRequest.php @@ -0,0 +1,39 @@ +clearedAt = new DateTime('now'); + * $cart->customer = new CartCustomer(); + * $cart->customer->id = 4770; + * $cart->customer->browserId = 'browserId'; + * $cart->customer->gaClientId = 'gaClientId'; + * + * $request = new CustomerInteractionCartClearRequest(); + * $request->cart = $cart; + * $request->siteBy = BySite::CODE; + * + * try { + * $response = $client->customerInteraction->cartClear('testSite', $request); + * } catch (ApiExceptionInterface $exception) { + * echo sprintf( + * 'Error from RetailCRM API (status code: %d): %s', + * $exception->getStatusCode(), + * $exception->getMessage() + * ); + * + * if (count($exception->getErrorResponse()->errors) > 0) { + * echo PHP_EOL . 'Errors: ' . implode(', ', $exception->getErrorResponse()->errors); + * } + * + * return; + * } + * ``` + * + * @param string|int $site + * @param \RetailCrm\Api\Model\Request\CustomerInteraction\CustomerInteractionCartClearRequest $request + * + * @return \RetailCrm\Api\Model\Response\SuccessResponse + * @throws \RetailCrm\Api\Interfaces\ApiExceptionInterface + * @throws \RetailCrm\Api\Interfaces\ClientExceptionInterface + * @throws \RetailCrm\Api\Exception\Api\AccountDoesNotExistException + * @throws \RetailCrm\Api\Exception\Api\ApiErrorException + * @throws \RetailCrm\Api\Exception\Api\MissingCredentialsException + * @throws \RetailCrm\Api\Exception\Api\MissingParameterException + * @throws \RetailCrm\Api\Exception\Api\ValidationException + * @throws \RetailCrm\Api\Exception\Client\HandlerException + * @throws \RetailCrm\Api\Exception\Client\HttpClientException + */ + public function cartClear($site, CustomerInteractionCartClearRequest $request): SuccessResponse + { + /** @var SuccessResponse $response */ + $response = $this->sendRequest( + RequestMethod::POST, + 'customer-interaction/' . $site . '/cart/clear', + $request, + SuccessResponse::class + ); + return $response; + } + + /** + * Makes POST "/api/v5/customer-interaction/{site}/cart/set" request. + * + * Example: + * ```php + * use RetailCrm\Api\Enum\BySite; + * use RetailCrm\Api\Factory\SimpleClientFactory; + * use RetailCrm\Api\Interfaces\ApiExceptionInterface; + * use RetailCrm\Api\Model\Entity\CustomerInteraction\Cart; + * use RetailCrm\Api\Model\Entity\CustomerInteraction\CartCustomer; + * use RetailCrm\Api\Model\Entity\CustomerInteraction\CartItem; + * use RetailCrm\Api\Model\Entity\Orders\Items\Offer; + * use RetailCrm\Api\Model\Request\CustomerInteraction\CustomerInteractionCartSetRequest; + * + * $client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'apiKey'); + * + * $customer = new CartCustomer(); + * $customer->id = 4770; + * + * $cartItem = new CartItem(); + * $cartItem->price = 24.99; + * $cartItem->quantity = 2; + * $cartItem->offer = new Offer(); + * $cartItem->offer->externalId = '47701234567890'; + * + * $cart = new Cart(); + * $cart->items[] = $cartItem; + * $cart->customer = $customer; + * + * $request = new CustomerInteractionCartSetRequest(); + * $request->cart = $cart; + * $request->siteBy = BySite::CODE; + * + * try { + * $response = $client->customerInteraction->cartSet('testSite', $request); + * } catch (ApiExceptionInterface $exception) { + * echo sprintf( + * 'Error from RetailCRM API (status code: %d): %s', + * $exception->getStatusCode(), + * $exception->getMessage() + * ); + * + * if (count($exception->getErrorResponse()->errors) > 0) { + * echo PHP_EOL . 'Errors: ' . implode(', ', $exception->getErrorResponse()->errors); + * } + * + * return; + * } + * ``` + * + * @param string|int $site + * @param \RetailCrm\Api\Model\Request\CustomerInteraction\CustomerInteractionCartSetRequest $request + * + * @return \RetailCrm\Api\Model\Response\SuccessResponse + * @throws \RetailCrm\Api\Interfaces\ApiExceptionInterface + * @throws \RetailCrm\Api\Interfaces\ClientExceptionInterface + * @throws \RetailCrm\Api\Exception\Api\AccountDoesNotExistException + * @throws \RetailCrm\Api\Exception\Api\ApiErrorException + * @throws \RetailCrm\Api\Exception\Api\MissingCredentialsException + * @throws \RetailCrm\Api\Exception\Api\MissingParameterException + * @throws \RetailCrm\Api\Exception\Api\ValidationException + * @throws \RetailCrm\Api\Exception\Client\HandlerException + * @throws \RetailCrm\Api\Exception\Client\HttpClientException + */ + public function cartSet($site, CustomerInteractionCartSetRequest $request): SuccessResponse + { + /** @var SuccessResponse $response */ + $response = $this->sendRequest( + RequestMethod::POST, + 'customer-interaction/' . $site . '/cart/set', + $request, + SuccessResponse::class + ); + return $response; + } + + /** + * Makes GET "/api/v5/customer-interaction/{site}/cart/{customerId}" request. + * + * Example: + * ```php + * use RetailCrm\Api\Enum\ByIdentifier; + * use RetailCrm\Api\Enum\BySite; + * use RetailCrm\Api\Factory\SimpleClientFactory; + * use RetailCrm\Api\Interfaces\ApiExceptionInterface; + * use RetailCrm\Api\Model\Request\ByAndSiteByRequest; + * + * $client = SimpleClientFactory::createClient('https://test.retailcrm.pro', 'apiKey'); + * + * try { + * $response = $client->customerInteraction->cartGet( + * 'testSite', + * 4200, + * new ByAndSiteByRequest(ByIdentifier::ID, BySite::CODE) + * ); + * } catch (ApiExceptionInterface $exception) { + * echo sprintf( + * 'Error from RetailCRM API (status code: %d): %s', + * $exception->getStatusCode(), + * $exception->getMessage() + * ); + * + * if (count($exception->getErrorResponse()->errors) > 0) { + * echo PHP_EOL . 'Errors: ' . implode(', ', $exception->getErrorResponse()->errors); + * } + * + * return; + * } + * + * echo 'Cart: ' . print_r($response->cart, true); + * ``` + * + * @param string|int $site + * @param string|int $customerId + * @param \RetailCrm\Api\Model\Request\ByAndSiteByRequest|null $request + * + * @return \RetailCrm\Api\Model\Response\CustomerInteraction\CustomerInteractionCartGetResponse + * @throws \RetailCrm\Api\Interfaces\ApiExceptionInterface + * @throws \RetailCrm\Api\Interfaces\ClientExceptionInterface + * @throws \RetailCrm\Api\Exception\Api\AccountDoesNotExistException + * @throws \RetailCrm\Api\Exception\Api\ApiErrorException + * @throws \RetailCrm\Api\Exception\Api\MissingCredentialsException + * @throws \RetailCrm\Api\Exception\Api\MissingParameterException + * @throws \RetailCrm\Api\Exception\Api\ValidationException + * @throws \RetailCrm\Api\Exception\Client\HandlerException + * @throws \RetailCrm\Api\Exception\Client\HttpClientException + */ + public function cartGet( + $site, + $customerId, + ?ByAndSiteByRequest $request = null + ): CustomerInteractionCartGetResponse { + /** @var CustomerInteractionCartGetResponse $response */ + $response = $this->sendRequest( + RequestMethod::GET, + 'customer-interaction/' . $site . '/cart/' . $customerId, + $request, + CustomerInteractionCartGetResponse::class + ); + return $response; + } +} diff --git a/tests/src/ResourceGroup/CustomerInteractionTest.php b/tests/src/ResourceGroup/CustomerInteractionTest.php new file mode 100644 index 0000000..ada4a06 --- /dev/null +++ b/tests/src/ResourceGroup/CustomerInteractionTest.php @@ -0,0 +1,135 @@ +clearedAt = new DateTime('now'); + $cart->customer = new CartCustomer(); + $cart->customer->id = 4770; + $cart->customer->browserId = 'browserId'; + $cart->customer->gaClientId = 'gaClientId'; + + $request = new CustomerInteractionCartClearRequest(); + $request->cart = $cart; + $request->siteBy = BySite::CODE; + + $mock = static::createApiMockBuilder('customer-interaction/testSite/cart/clear'); + $mock->matchMethod(RequestMethod::POST) + ->matchBody(static::encodeForm($request)) + ->reply(200) + ->withBody($json); + + $client = TestClientFactory::createClient($mock->getClient()); + $response = $client->customerInteraction->cartClear('testSite', $request); + + self::assertModelEqualsToResponse($json, $response); + } + + public static function testCustomerInteractionCartSet(): void + { + $json = <<<'EOF' +{ + "success": true +} +EOF; + + $customer = new CartCustomer(); + $customer->id = 4770; + + $cartItem = new CartItem(); + $cartItem->price = 24.99; + $cartItem->quantity = 2; + $cartItem->offer = new Offer(); + $cartItem->offer->externalId = '47701234567890'; + + $cart = new Cart(); + $cart->items[] = $cartItem; + $cart->customer = $customer; + + $request = new CustomerInteractionCartSetRequest(); + $request->cart = $cart; + $request->siteBy = BySite::CODE; + + $mock = static::createApiMockBuilder('customer-interaction/testSite/cart/set'); + $mock->matchMethod(RequestMethod::POST) + ->matchBody(static::encodeForm($request)) + ->reply(200) + ->withBody($json); + + $client = TestClientFactory::createClient($mock->getClient()); + $response = $client->customerInteraction->cartSet('testSite', $request); + + self::assertModelEqualsToResponse($json, $response); + } + + public static function testCustomerInteractionCartGet(): void + { + $json = <<<'EOF' +{ + "success": true, + "cart": { + "externalId": "12345678901234", + "droppedAt": "2024-05-08 15:10:30-05:00", + "link": "https:/shop.myshopify.com/recover?key=1234", + "items": [ + { + "id": "1", + "offer": { + "displayName": "Some sample product", + "id": 11, + "externalId": "01234567890123", + "name": "Some sample product", + "vatRate": "20.00", + "properties": [], + "unit": { + "code": "pc", + "name": "Piece", + "sym": "pc" + } + }, + "quantity": 2, + "price": 24.99 + } + ], + "currency": "EUR" + } +} +EOF; + $request = new ByAndSiteByRequest(ByIdentifier::ID, BySite::CODE); + + $mock = static::createApiMockBuilder('customer-interaction/testSite/cart/4770'); + $mock->matchMethod(RequestMethod::GET) + ->matchQuery(static::encodeFormArray($request)) + ->reply(200) + ->withBody($json); + + $client = TestClientFactory::createClient($mock->getClient()); + $response = $client->customerInteraction->cartGet('testSite', 4770, $request); + + self::assertModelEqualsToResponse($json, $response); + } +}