Recipes
PHP integration
A retry-safe client using Guzzle.
A minimal Guzzle-based client. Install Guzzle with
composer require guzzlehttp/guzzle.
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
class KanalClient
{
private Client $http;
public function __construct(
private string $apiKey,
private int $storeId,
) {
$this->http = new Client([
'base_uri' => 'https://api.getkanal.com',
'timeout' => 10,
]);
}
private function request(string $method, string $path, array $body = []): ?array
{
$attempt = 0;
do {
try {
$res = $this->http->request($method, "/api/v1/stores/{$this->storeId}{$path}", [
'headers' => [
'Authorization' => "Bearer {$this->apiKey}",
'Content-Type' => 'application/json',
],
'json' => $body ?: null,
'http_errors' => true,
]);
return $res->getStatusCode() === 204
? null
: json_decode((string) $res->getBody(), true);
} catch (RequestException $e) {
$status = $e->getResponse()?->getStatusCode() ?? 0;
// Retry 429 and 5xx — safe because writes are idempotent.
if (($status === 429 || $status >= 500) && $attempt < 4) {
usleep((2 ** $attempt) * 500_000); // 0.5s, 1s, 2s, 4s
$attempt++;
continue;
}
throw $e; // 4xx ≠ 429: fix the payload/credentials, do not retry.
}
} while ($attempt <= 4);
return null;
}
public function upsertOrder(array $order): ?array
{
return $this->request('POST', '/orders', $order);
}
public function updateOrder(string $externalId, array $patch): ?array
{
return $this->request('PATCH', '/orders/' . rawurlencode($externalId), $patch);
}
public function upsertCheckout(array $checkout): ?array
{
return $this->request('POST', '/checkouts', $checkout);
}
public function upsertCustomer(array $customer): ?array
{
return $this->request('POST', '/customers', $customer);
}
public function upsertShipment(array $shipment): ?array
{
return $this->request('POST', '/shipments', $shipment);
}
}<?php
$kanal = new KanalClient(getenv('KANAL_API_KEY'), 123);
$kanal->upsertOrder([
'external_id' => 'ORDER-1001',
'placed_at' => (new DateTime())->format(DateTime::ATOM),
'total_price' => 49.90,
'currency' => 'EUR',
'customer' => ['phone' => '+33612345678', 'first_name' => 'Marie'],
]);Load the API key from the environment or your secrets manager — never commit it to source control.