docs: update CannaiQ API docs and client to use correct endpoints
- Changed endpoints from /dispensaries to /stores - Changed auth from Bearer token to X-API-Key header - Added new endpoint methods: getStoreProducts, getStoreProductMetrics, getStoreCompetitorSnapshot, getProductHistory, healthCheck, ping - Updated documentation with all available endpoints and example responses
This commit is contained in:
@@ -13,6 +13,9 @@ use Illuminate\Support\Facades\Log;
|
||||
* - Store metrics (pricing position, market share, trends)
|
||||
* - Product metrics (velocity, pricing history, competitor positioning)
|
||||
* - Competitor snapshots (out-of-stock, pricing, promotions)
|
||||
*
|
||||
* API Base URL: https://cannaiq.co/api/v1
|
||||
* Authentication: X-API-Key header (trusted origins and localhost bypass auth)
|
||||
*/
|
||||
class CannaiqClient
|
||||
{
|
||||
@@ -45,38 +48,69 @@ class CannaiqClient
|
||||
'Content-Type' => 'application/json',
|
||||
];
|
||||
|
||||
// Add API key if configured (not needed for trusted origins)
|
||||
// Add API key if configured (not needed for trusted origins *.cannabrands.app)
|
||||
if ($this->apiKey) {
|
||||
$headers['Authorization'] = 'Bearer '.$this->apiKey;
|
||||
$headers['X-API-Key'] = $this->apiKey;
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get store-level metrics (pricing position, market share, trends)
|
||||
* List all stores (paginated)
|
||||
*
|
||||
* @param string $externalStoreId CannaiQ store ID or slug
|
||||
* @param int $limit Number of stores to return
|
||||
* @param int $offset Pagination offset
|
||||
*/
|
||||
public function getStoreMetrics(string $externalStoreId): array
|
||||
public function listStores(int $limit = 50, int $offset = 0): array
|
||||
{
|
||||
try {
|
||||
$response = $this->http->get("/dispensaries/{$externalStoreId}");
|
||||
$response = $this->http->get('/stores', [
|
||||
'limit' => $limit,
|
||||
'offset' => $offset,
|
||||
]);
|
||||
|
||||
if ($response->successful()) {
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
Log::warning('CannaiQ: Failed to fetch store metrics', [
|
||||
'store_id' => $externalStoreId,
|
||||
Log::warning('CannaiQ: Failed to list stores', [
|
||||
'status' => $response->status(),
|
||||
'body' => $response->body(),
|
||||
]);
|
||||
|
||||
return ['error' => true, 'message' => 'Failed to fetch store metrics'];
|
||||
return ['error' => true, 'message' => 'Failed to list stores', 'stores' => []];
|
||||
} catch (\Exception $e) {
|
||||
Log::error('CannaiQ: Exception fetching store metrics', [
|
||||
'store_id' => $externalStoreId,
|
||||
Log::error('CannaiQ: Exception listing stores', [
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return ['error' => true, 'message' => $e->getMessage(), 'stores' => []];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get store details
|
||||
*
|
||||
* @param string $storeId CannaiQ store ID
|
||||
*/
|
||||
public function getStore(string $storeId): array
|
||||
{
|
||||
try {
|
||||
$response = $this->http->get("/stores/{$storeId}");
|
||||
|
||||
if ($response->successful()) {
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
Log::warning('CannaiQ: Failed to fetch store', [
|
||||
'store_id' => $storeId,
|
||||
'status' => $response->status(),
|
||||
]);
|
||||
|
||||
return ['error' => true, 'message' => 'Failed to fetch store'];
|
||||
} catch (\Exception $e) {
|
||||
Log::error('CannaiQ: Exception fetching store', [
|
||||
'store_id' => $storeId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
@@ -85,16 +119,83 @@ class CannaiqClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Get product-level metrics for a store
|
||||
* Get store performance metrics
|
||||
* Returns: product counts, brands, categories, price stats, stock health
|
||||
*
|
||||
* @param string $externalStoreId CannaiQ store ID or slug
|
||||
* @param string $storeId CannaiQ store ID
|
||||
*/
|
||||
public function getStoreMetrics(string $storeId): array
|
||||
{
|
||||
try {
|
||||
$response = $this->http->get("/stores/{$storeId}/metrics");
|
||||
|
||||
if ($response->successful()) {
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
Log::warning('CannaiQ: Failed to fetch store metrics', [
|
||||
'store_id' => $storeId,
|
||||
'status' => $response->status(),
|
||||
'body' => $response->body(),
|
||||
]);
|
||||
|
||||
return ['error' => true, 'message' => 'Failed to fetch store metrics'];
|
||||
} catch (\Exception $e) {
|
||||
Log::error('CannaiQ: Exception fetching store metrics', [
|
||||
'store_id' => $storeId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return ['error' => true, 'message' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get store's product catalog
|
||||
*
|
||||
* @param string $storeId CannaiQ store ID
|
||||
* @param int $limit Number of products to return
|
||||
* @param int $offset Pagination offset
|
||||
*/
|
||||
public function getStoreProductMetrics(string $externalStoreId, int $limit = 100, int $offset = 0): array
|
||||
public function getStoreProducts(string $storeId, int $limit = 100, int $offset = 0): array
|
||||
{
|
||||
try {
|
||||
$response = $this->http->get("/dispensaries/{$externalStoreId}/products", [
|
||||
$response = $this->http->get("/stores/{$storeId}/products", [
|
||||
'limit' => $limit,
|
||||
'offset' => $offset,
|
||||
]);
|
||||
|
||||
if ($response->successful()) {
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
Log::warning('CannaiQ: Failed to fetch store products', [
|
||||
'store_id' => $storeId,
|
||||
'status' => $response->status(),
|
||||
]);
|
||||
|
||||
return ['error' => true, 'message' => 'Failed to fetch store products', 'products' => []];
|
||||
} catch (\Exception $e) {
|
||||
Log::error('CannaiQ: Exception fetching store products', [
|
||||
'store_id' => $storeId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return ['error' => true, 'message' => $e->getMessage(), 'products' => []];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get product-level metrics with price changes since last crawl
|
||||
*
|
||||
* @param string $storeId CannaiQ store ID
|
||||
* @param int $limit Number of products to return
|
||||
* @param int $offset Pagination offset
|
||||
*/
|
||||
public function getStoreProductMetrics(string $storeId, int $limit = 100, int $offset = 0): array
|
||||
{
|
||||
try {
|
||||
$response = $this->http->get("/stores/{$storeId}/product-metrics", [
|
||||
'limit' => $limit,
|
||||
'offset' => $offset,
|
||||
]);
|
||||
@@ -104,14 +205,14 @@ class CannaiqClient
|
||||
}
|
||||
|
||||
Log::warning('CannaiQ: Failed to fetch product metrics', [
|
||||
'store_id' => $externalStoreId,
|
||||
'store_id' => $storeId,
|
||||
'status' => $response->status(),
|
||||
]);
|
||||
|
||||
return ['error' => true, 'message' => 'Failed to fetch product metrics', 'products' => []];
|
||||
} catch (\Exception $e) {
|
||||
Log::error('CannaiQ: Exception fetching product metrics', [
|
||||
'store_id' => $externalStoreId,
|
||||
'store_id' => $storeId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
@@ -120,41 +221,29 @@ class CannaiqClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Get competitor snapshot for a store's market area
|
||||
* Returns competitor pricing, out-of-stock items, and promo activity
|
||||
* Get competitor snapshot for a store
|
||||
* Returns: nearby competitors, price comparisons, brand overlap
|
||||
*
|
||||
* @param string $externalStoreId CannaiQ store ID or slug
|
||||
* @param string $storeId CannaiQ store ID
|
||||
*/
|
||||
public function getStoreCompetitorSnapshot(string $externalStoreId): array
|
||||
public function getStoreCompetitorSnapshot(string $storeId): array
|
||||
{
|
||||
try {
|
||||
// First get the store details to find competitors in the area
|
||||
$storeData = $this->getStoreMetrics($externalStoreId);
|
||||
|
||||
if (isset($storeData['error'])) {
|
||||
return $storeData;
|
||||
}
|
||||
|
||||
// Get all dispensaries (we'll filter by proximity on our end for now)
|
||||
// In future, CannaiQ may provide a dedicated competitor endpoint
|
||||
$response = $this->http->get('/dispensaries', [
|
||||
'limit' => 50,
|
||||
]);
|
||||
$response = $this->http->get("/stores/{$storeId}/competitor-snapshot");
|
||||
|
||||
if ($response->successful()) {
|
||||
$allStores = $response->json();
|
||||
|
||||
return [
|
||||
'target_store' => $storeData,
|
||||
'competitors' => $allStores['dispensaries'] ?? [],
|
||||
'snapshot_time' => now()->toIso8601String(),
|
||||
];
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
return ['error' => true, 'message' => 'Failed to fetch competitor data'];
|
||||
Log::warning('CannaiQ: Failed to fetch competitor snapshot', [
|
||||
'store_id' => $storeId,
|
||||
'status' => $response->status(),
|
||||
]);
|
||||
|
||||
return ['error' => true, 'message' => 'Failed to fetch competitor snapshot'];
|
||||
} catch (\Exception $e) {
|
||||
Log::error('CannaiQ: Exception fetching competitor snapshot', [
|
||||
'store_id' => $externalStoreId,
|
||||
'store_id' => $storeId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
@@ -162,56 +251,6 @@ class CannaiqClient
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get product details with price history
|
||||
*
|
||||
* @param int|string $productId CannaiQ product ID
|
||||
*/
|
||||
public function getProduct(int|string $productId): array
|
||||
{
|
||||
try {
|
||||
$response = $this->http->get("/products/{$productId}");
|
||||
|
||||
if ($response->successful()) {
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
return ['error' => true, 'message' => 'Failed to fetch product'];
|
||||
} catch (\Exception $e) {
|
||||
Log::error('CannaiQ: Exception fetching product', [
|
||||
'product_id' => $productId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return ['error' => true, 'message' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get historical price/stock snapshots for a product
|
||||
*
|
||||
* @param int|string $productId CannaiQ product ID
|
||||
*/
|
||||
public function getProductSnapshots(int|string $productId): array
|
||||
{
|
||||
try {
|
||||
$response = $this->http->get("/products/{$productId}/snapshots");
|
||||
|
||||
if ($response->successful()) {
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
return ['error' => true, 'message' => 'Failed to fetch snapshots', 'snapshots' => []];
|
||||
} catch (\Exception $e) {
|
||||
Log::error('CannaiQ: Exception fetching product snapshots', [
|
||||
'product_id' => $productId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return ['error' => true, 'message' => $e->getMessage(), 'snapshots' => []];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search products across all stores
|
||||
*
|
||||
@@ -240,7 +279,126 @@ class CannaiqClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Check API health
|
||||
* Get product details
|
||||
*
|
||||
* @param int|string $productId CannaiQ product ID
|
||||
*/
|
||||
public function getProduct(int|string $productId): array
|
||||
{
|
||||
try {
|
||||
$response = $this->http->get("/products/{$productId}");
|
||||
|
||||
if ($response->successful()) {
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
return ['error' => true, 'message' => 'Failed to fetch product'];
|
||||
} catch (\Exception $e) {
|
||||
Log::error('CannaiQ: Exception fetching product', [
|
||||
'product_id' => $productId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return ['error' => true, 'message' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get product price/stock history
|
||||
*
|
||||
* @param int|string $productId CannaiQ product ID
|
||||
*/
|
||||
public function getProductHistory(int|string $productId): array
|
||||
{
|
||||
try {
|
||||
$response = $this->http->get("/products/{$productId}/history");
|
||||
|
||||
if ($response->successful()) {
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
return ['error' => true, 'message' => 'Failed to fetch product history', 'history' => []];
|
||||
} catch (\Exception $e) {
|
||||
Log::error('CannaiQ: Exception fetching product history', [
|
||||
'product_id' => $productId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return ['error' => true, 'message' => $e->getMessage(), 'history' => []];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List all brands
|
||||
*/
|
||||
public function listBrands(): array
|
||||
{
|
||||
try {
|
||||
$response = $this->http->get('/brands');
|
||||
|
||||
if ($response->successful()) {
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
return ['error' => true, 'message' => 'Failed to list brands', 'brands' => []];
|
||||
} catch (\Exception $e) {
|
||||
Log::error('CannaiQ: Exception listing brands', [
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return ['error' => true, 'message' => $e->getMessage(), 'brands' => []];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get brand details and products
|
||||
*
|
||||
* @param string $brandName Brand name/slug
|
||||
*/
|
||||
public function getBrand(string $brandName): array
|
||||
{
|
||||
try {
|
||||
$response = $this->http->get("/brands/{$brandName}");
|
||||
|
||||
if ($response->successful()) {
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
return ['error' => true, 'message' => 'Failed to fetch brand'];
|
||||
} catch (\Exception $e) {
|
||||
Log::error('CannaiQ: Exception fetching brand', [
|
||||
'brand' => $brandName,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return ['error' => true, 'message' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List all categories
|
||||
*/
|
||||
public function listCategories(): array
|
||||
{
|
||||
try {
|
||||
$response = $this->http->get('/categories');
|
||||
|
||||
if ($response->successful()) {
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
return ['error' => true, 'message' => 'Failed to list categories', 'categories' => []];
|
||||
} catch (\Exception $e) {
|
||||
Log::error('CannaiQ: Exception listing categories', [
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return ['error' => true, 'message' => $e->getMessage(), 'categories' => []];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check API health (full system health)
|
||||
*/
|
||||
public function healthCheck(): array
|
||||
{
|
||||
@@ -259,4 +417,24 @@ class CannaiqClient
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check basic API health
|
||||
*/
|
||||
public function ping(): array
|
||||
{
|
||||
try {
|
||||
$response = $this->http->get('/health');
|
||||
|
||||
return [
|
||||
'healthy' => $response->successful(),
|
||||
'data' => $response->json(),
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
return [
|
||||
'healthy' => false,
|
||||
'error' => $e->getMessage(),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ CannaiQ is the Marketing Intelligence Engine that powers competitive analysis, p
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| **Bearer Token** | `Authorization: Bearer <api_key>` |
|
||||
| **X-API-Key Header** | `X-API-Key: <api_key>` |
|
||||
| **Trusted Origins** | No auth needed for whitelisted domains |
|
||||
|
||||
### Trusted Origins (No Auth Required)
|
||||
@@ -19,27 +19,86 @@ CannaiQ is the Marketing Intelligence Engine that powers competitive analysis, p
|
||||
## Base URL
|
||||
|
||||
```
|
||||
https://cannaiq.co/api/v1/
|
||||
https://cannaiq.co/api/v1
|
||||
```
|
||||
|
||||
## Available Endpoints
|
||||
|
||||
### Store Endpoints
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/dispensaries` | GET | List all stores |
|
||||
| `/dispensaries/:id` | GET | Store details |
|
||||
| `/dispensaries/:id/products` | GET | Products for a store |
|
||||
| `/products` | GET | Search products across stores |
|
||||
| `/products/:id` | GET | Product details with price history |
|
||||
| `/products/:id/snapshots` | GET | Historical price/stock data |
|
||||
| `/health/full` | GET | System health status |
|
||||
| `/stores` | GET | List all stores (paginated) |
|
||||
| `/stores/:id` | GET | Get store details |
|
||||
| `/stores/:id/products` | GET | Get store's product catalog |
|
||||
| `/stores/:id/metrics` | GET | Store performance metrics (product counts, brands, categories, price stats, stock health) |
|
||||
| `/stores/:id/product-metrics` | GET | Product-level metrics with price changes since last crawl |
|
||||
| `/stores/:id/competitor-snapshot` | GET | Competitive intelligence (nearby competitors, price comparisons, brand overlap) |
|
||||
|
||||
### Product Endpoints
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/products` | GET | Search products across all stores |
|
||||
| `/products/:id` | GET | Get product details |
|
||||
| `/products/:id/history` | GET | Get product price/stock history |
|
||||
|
||||
### Brand Endpoints
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/brands` | GET | List all brands |
|
||||
| `/brands/:name` | GET | Get brand details and products |
|
||||
|
||||
### Category Endpoints
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/categories` | GET | List all categories |
|
||||
|
||||
### Health Endpoints
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
|----------|--------|-------------|
|
||||
| `/health` | GET | Basic health check |
|
||||
| `/health/full` | GET | Full system health status |
|
||||
|
||||
## Example Requests
|
||||
|
||||
### List Products for a Dispensary
|
||||
### List Stores
|
||||
|
||||
```bash
|
||||
curl "https://cannaiq.co/api/v1/dispensaries/123/products?limit=50"
|
||||
curl "https://cannaiq.co/api/v1/stores?limit=50&offset=0"
|
||||
```
|
||||
|
||||
### Get Store Metrics
|
||||
|
||||
```bash
|
||||
curl "https://cannaiq.co/api/v1/stores/123/metrics"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"store_name": "Green Leaf Dispensary",
|
||||
"products_count": 245,
|
||||
"brands_count": 42,
|
||||
"categories": ["flower", "concentrates", "edibles"],
|
||||
"average_price": 38.50,
|
||||
"pricing_position": "competitive",
|
||||
"market_share": 12.5,
|
||||
"stock_health": {
|
||||
"in_stock": 220,
|
||||
"out_of_stock": 25,
|
||||
"low_stock": 15
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Get Store Product Metrics
|
||||
|
||||
```bash
|
||||
curl "https://cannaiq.co/api/v1/stores/123/product-metrics?limit=100"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
@@ -51,28 +110,75 @@ curl "https://cannaiq.co/api/v1/dispensaries/123/products?limit=50"
|
||||
"name": "Blue Dream 1g",
|
||||
"brand_name": "Select",
|
||||
"category": "flower",
|
||||
"price": 35.00,
|
||||
"current_price": 35.00,
|
||||
"original_price": 40.00,
|
||||
"price_change": -5.00,
|
||||
"in_stock": true,
|
||||
"thc_percent": 24.5
|
||||
}
|
||||
],
|
||||
"total": 150,
|
||||
"limit": 50,
|
||||
"offset": 0
|
||||
"meta": {
|
||||
"total": 245,
|
||||
"limit": 100,
|
||||
"offset": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Get Product with Price History
|
||||
### Get Competitor Snapshot
|
||||
|
||||
```bash
|
||||
curl "https://cannaiq.co/api/v1/products/456"
|
||||
curl "https://cannaiq.co/api/v1/stores/123/competitor-snapshot"
|
||||
```
|
||||
|
||||
### Get Historical Snapshots
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"target_store": {
|
||||
"id": 123,
|
||||
"name": "Green Leaf Dispensary",
|
||||
"average_price": 38.50
|
||||
},
|
||||
"competitors": [
|
||||
{
|
||||
"id": 456,
|
||||
"name": "Competitor Store",
|
||||
"slug": "competitor-store",
|
||||
"distance": 2.5,
|
||||
"products_count": 180,
|
||||
"average_price": 42.00
|
||||
}
|
||||
],
|
||||
"snapshot_time": "2025-01-15T10:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### Get Product with History
|
||||
|
||||
```bash
|
||||
curl "https://cannaiq.co/api/v1/products/456/snapshots"
|
||||
curl "https://cannaiq.co/api/v1/products/456/history"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"product": {
|
||||
"id": 456,
|
||||
"name": "Blue Dream 1g"
|
||||
},
|
||||
"history": [
|
||||
{
|
||||
"date": "2025-01-15",
|
||||
"price": 35.00,
|
||||
"in_stock": true
|
||||
},
|
||||
{
|
||||
"date": "2025-01-14",
|
||||
"price": 40.00,
|
||||
"in_stock": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Rate Limits
|
||||
@@ -108,28 +214,58 @@ curl "https://cannaiq.co/api/v1/products/456/snapshots"
|
||||
### Laravel Service Example
|
||||
|
||||
```php
|
||||
// app/Services/CannaiQ/CannaiQService.php
|
||||
// app/Services/Cannaiq/CannaiqClient.php
|
||||
|
||||
namespace App\Services\CannaiQ;
|
||||
namespace App\Services\Cannaiq;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class CannaiQService
|
||||
class CannaiqClient
|
||||
{
|
||||
protected string $baseUrl = 'https://cannaiq.co/api/v1';
|
||||
protected ?string $apiKey;
|
||||
|
||||
public function getDispensaryProducts(int $dispensaryId, int $limit = 50): array
|
||||
public function __construct()
|
||||
{
|
||||
$response = Http::get("{$this->baseUrl}/dispensaries/{$dispensaryId}/products", [
|
||||
'limit' => $limit,
|
||||
]);
|
||||
$this->apiKey = config('services.cannaiq.api_key');
|
||||
}
|
||||
|
||||
protected function getHeaders(): array
|
||||
{
|
||||
$headers = [
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/json',
|
||||
];
|
||||
|
||||
if ($this->apiKey) {
|
||||
$headers['X-API-Key'] = $this->apiKey;
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
public function getStoreMetrics(string $storeId): array
|
||||
{
|
||||
$response = Http::withHeaders($this->getHeaders())
|
||||
->get("{$this->baseUrl}/stores/{$storeId}/metrics");
|
||||
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
public function getProductSnapshots(int $productId): array
|
||||
public function getStoreProductMetrics(string $storeId, int $limit = 100): array
|
||||
{
|
||||
$response = Http::get("{$this->baseUrl}/products/{$productId}/snapshots");
|
||||
$response = Http::withHeaders($this->getHeaders())
|
||||
->get("{$this->baseUrl}/stores/{$storeId}/product-metrics", [
|
||||
'limit' => $limit,
|
||||
]);
|
||||
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
public function getStoreCompetitorSnapshot(string $storeId): array
|
||||
{
|
||||
$response = Http::withHeaders($this->getHeaders())
|
||||
->get("{$this->baseUrl}/stores/{$storeId}/competitor-snapshot");
|
||||
|
||||
return $response->json();
|
||||
}
|
||||
@@ -143,10 +279,10 @@ Since data refreshes every 4 hours, cache responses for 1-2 hours:
|
||||
```php
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
$products = Cache::remember(
|
||||
"cannaiq:dispensary:{$id}:products",
|
||||
$metrics = Cache::remember(
|
||||
"cannaiq:store:{$storeId}:metrics",
|
||||
now()->addHours(2),
|
||||
fn () => $this->cannaiQ->getDispensaryProducts($id)
|
||||
fn () => $this->cannaiqClient->getStoreMetrics($storeId)
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user