get('customers', $filters); } public function products(array $filters = []): array { return $this->get('products', $filters); } public function product(int|string $id): array { return $this->get("products/{$id}"); } public function createProduct(array $data): array { return $this->send('post', 'products', $data); } /** * Bulk-create products (one CRM INSERT per call). Caller batches large * catalogs; the CRM caps each request at 500 rows. * * @param array> $products */ public function bulkCreateProducts(array $products): array { return $this->send('post', 'products/bulk', ['products' => array_values($products)]); } public function updateProduct(int|string $id, array $data): array { return $this->send('patch', "products/{$id}", $data); } public function deleteProduct(int|string $id): array { return $this->send('delete', "products/{$id}", []); } public function pushTimeline(array $data): array { return $this->post('timeline', $data); } /** @return array */ public function loyaltyProgram(): array { return $this->get('loyalty/program'); } /** * @return array */ public function customerLoyalty(int|string $customerId, ?int $amountMinor = null): array { $query = []; if ($amountMinor !== null) { $query['amount_minor'] = $amountMinor; } return $this->get("customers/{$customerId}/loyalty", $query); } /** * @param array{customer_id: int, subtotal_minor: int, redeem_points?: int} $data * @return array */ public function loyaltyQuote(array $data): array { return $this->post('loyalty/quote', $data); } /** * @param array{customer_id: int, subtotal_minor: int, redeem_points: int, external_ref: string, source?: string, description?: string} $data * @return array */ public function loyaltyRedeem(array $data): array { return $this->post('loyalty/redeem', $data); } /** * @param array{customer_id: int, amount_minor: int, external_ref: string, source?: string, description?: string} $data * @return array */ public function loyaltyEarn(array $data): array { return $this->post('loyalty/earn', $data); } /** * @param array{external_ref: string, source?: string} $data * @return array */ public function loyaltyReverse(array $data): array { return $this->post('loyalty/reverse', $data); } private function client(): PendingRequest { return Http::baseUrl((string) config('crm.url')) ->withToken((string) config('crm.key')) ->acceptJson() ->asJson() ->connectTimeout(10) ->timeout(20); } private function get(string $path, array $query = []): array { return $this->handle(fn () => $this->client()->get($path, [...$query, 'owner' => $this->owner])); } private function post(string $path, array $data): array { return $this->handle(fn () => $this->client()->post($path, [...$data, 'owner' => $this->owner])); } private function send(string $method, string $path, array $data): array { return $this->handle(fn () => $this->client()->{$method}($path, [...$data, 'owner' => $this->owner])); } private function handle(callable $request): array { try { $response = $request(); } catch (ConnectionException) { throw ValidationException::withMessages([ 'crm' => ['Could not reach the CRM service. Please try again in a moment.'], ]); } if ($response->failed()) { if ($response->status() === 422) { $message = (string) ($response->json('message') ?? collect((array) $response->json('errors'))->flatten()->first() ?? 'CRM validation failed.'); throw ValidationException::withMessages(['crm' => [$message]]); } abort($response->status() === 404 ? 404 : 502, 'CRM service error.'); } return (array) $response->json(); } }