sonnenglas/takealot-php-sdk
Composer 安装命令:
composer require sonnenglas/takealot-php-sdk
包简介
Framework-agnostic PHP SDK for the Takealot Marketplace API (sales, offers, transactions, balances, returns, shipments) with continuation-token pagination and 429 retry handling.
README 文档
README
Framework-agnostic PHP SDK for the Takealot Marketplace API — read seller sales, offers, transactions, balances, returns, shipments, facilities, and account information from your Takealot Seller account.
Features
- Framework-agnostic. Plain PSR-18 (HTTP client), PSR-17 (factories), and PSR-7 (messages). No Laravel, no Symfony, no global state — drop it into any modern PHP project.
- Read-only and safe. The SDK wraps the Marketplace API's
GETendpoints only. It never creates, updates, or disables anything on your account — so there is no risk of an accidental price change or a disabled offer. - Type-safe DTOs. Readonly response objects with strict shape validation. Malformed upstream payloads fail loudly instead of leaking untyped arrays through your code.
- Continuation-token pagination, two ways. Fetch a single
Pageand follow the token yourself, oriterate()a lazyGeneratorthat walks every page transparently. - Automatic rate-limit handling. HTTP 429 responses are retried
automatically, honouring the
Retry-Afterheader, with exponential backoff as a fallback. Configurable viamaxRetries. - Typed exception hierarchy.
400,401/403,404, and429each map to a distinct exception subclass — no need togrepon status codes. - Hardened by default. 8 MiB response body cap, JSON depth limit of 64, and
PSR-18 transport-error wrapping that scrubs the
X-API-Keyheader from leaked messages. - PHPStan level 9. Strict types throughout, zero static-analysis errors.
Why this SDK?
Takealot does not publish an official PHP SDK at the time of writing. This
package is built against the current public
Takealot Marketplace API at
https://marketplace-api.takealot.com/v1, targets modern PHP (8.2+), and stays
out of your dependency-injection container by design.
It is the integration layer SONNENGLAS uses to pull our South African marketplace sales and settlement data into our own systems.
Requirements
- PHP 8.2 or newer.
- A PSR-18 HTTP client. Guzzle 7 is the
recommended default and is automatically discovered via
php-http/discovery.
Installation
composer require sonnenglas/takealot-php-sdk
If you do not already have a PSR-18 client installed:
composer require guzzlehttp/guzzle
php-http/discovery auto-wires any installed PSR-18 client and PSR-17
factories — you only need to inject them manually if you want to override
defaults (timeouts, retry middleware, a mock client in tests, etc.).
Quick start
1. Construct the client
Generate an API key in the Takealot Seller Portal under Settings → API Access, then construct the client with it. Everything else is optional.
use Sonnenglas\Takealot\Client; $client = new Client(apiKey: getenv('TAKEALOT_API_KEY'));
The key is sent as the X-API-Key header on every request. Only one API key
can be active per seller account at a time.
2. List a page of sales
$page = $client->sales()->list([ 'order_date__gte' => '2026-06-01', // inclusive, YYYY-MM-DD, SAST 'order_date__lte' => '2026-06-30', 'limit' => 100, // 1–100, default 20 'include_count' => true, ]); foreach ($page->items as $sale) { printf( "Order %d · %s · R%d · %s\n", $sale->orderId, $sale->sku, $sale->sellingPrice, $sale->salesRegion ?? 'unknown', ); } echo "Matching sales: " . ($page->count ?? 'n/a') . "\n"; echo $page->hasMore() ? "More pages available.\n" : "Last page.\n";
3. Iterate every sale across pages
iterate() follows the continuation_token for you, so you never have to
manage paging by hand:
$totalUnits = 0; foreach ($client->sales()->iterate(['order_date__gte' => '2026-06-01']) as $sale) { $totalUnits += $sale->quantity; } echo "Units sold in June: {$totalUnits}\n";
4. Read your account balance
$balances = $client->balances()->get()->balances; // nested ?Balances breakdown printf( "Available for payout: R%.2f (held back R%.2f)\n", $balances?->available ?? 0.0, $balances?->heldBack ?? 0.0, );
Documentation
- Documentation home
- Guides
- API reference
- Runnable examples — see
examples/
Supported endpoints
The SDK targets the seller-facing read surface of the Marketplace API.
Every collection endpoint supports continuation-token pagination, fields
selection, and (where applicable) expands.
| Resource | Endpoint(s) | Accessor | Operations |
|---|---|---|---|
| Sales | GET /sales |
$client->sales() |
list(), iterate() |
| Offers | GET /offers, GET /offers/{id}, GET /offers/by_sku/{sku}, GET /offers/by_barcode/{barcode} |
$client->offers() |
list(), iterate(), getById(), getBySku(), getByBarcode() |
| Transactions | GET /transactions |
$client->transactions() |
list(), iterate() |
| Balances | GET /balances |
$client->balances() |
get() |
| Returns | GET /returns, GET /returns/{id} |
$client->returns() |
list(), iterate(), getById() |
| Shipments | GET /shipments, GET /shipments/{id} |
$client->shipments() |
list(), iterate(), getById() |
| Facilities | GET /facilities/get_enabled_regions |
$client->facilities() |
enabledRegions() |
| Seller | GET /seller |
$client->seller() |
get() |
Single-item lookups (getById(), getBySku(), getByBarcode(),
returns()->getById(), shipments()->getById()) return the DTO, or null when
the item does not exist (HTTP 404) — they do not throw on a missing item.
Write operations are out of scope. The Marketplace API exposes offer-management endpoints (
POST /offers,PATCH /offers/{id},POST /offers/batch). This SDK deliberately does not wrap them — it is a read-only client. Call them directly if you need to mutate offers.
Pagination
Collection endpoints return a Page:
$page = $client->sales()->list(['limit' => 50]); $page->items; // list<Sale> $page->continuationToken; // string|null — the token for the next page $page->count; // int|null — total matches (only with include_count=true) $page->hasMore(); // bool — true when continuationToken !== null
To walk every page manually, feed the token back in. When a continuation token is supplied, all other query parameters are ignored by the API — pass the token alone:
$params = ['order_date__gte' => '2026-06-01', 'limit' => 100]; do { $page = $client->sales()->list($params); foreach ($page->items as $sale) { // ... } $params = ['continuation_token' => $page->continuationToken]; } while ($page->hasMore());
Or let the SDK do it for you with iterate() — see the
pagination guide.
Rate limiting & retries
The client retries HTTP 429 responses automatically:
- It reads the
Retry-Afterheader (integer seconds or an HTTP-date) and waits that long before retrying, capped at 60 seconds per attempt. - If no usable
Retry-Afteris present, it falls back to exponential backoff (1s, 2s, 4s, …, capped at 60s). - After
maxRetries(default3) exhausted attempts, it throwsRateLimitException, which exposes$retryAfterfor your own scheduling.
Tune or disable retries via the constructor:
// More patient: up to 5 retries. $client = new Client(apiKey: $key, maxRetries: 5); // Fail fast: no automatic retries. $client = new Client(apiKey: $key, maxRetries: 0);
See the rate-limiting guide for details and for how to inject a no-op sleeper in tests.
Error handling
All SDK exceptions extend Sonnenglas\Takealot\Exceptions\TakealotException.
| HTTP | Exception | When |
|---|---|---|
| 400 | ValidationException |
Invalid query parameters or filters. |
| 401 / 403 | AuthenticationException |
Missing, invalid, or deactivated API key. |
| 404 | NotFoundException |
A missing resource. Single-item lookups catch this and return null instead. |
| 429 | RateLimitException ($retryAfter) |
Rate limit hit after automatic retries were exhausted. |
| other | ApiException |
Any other 4xx/5xx, oversized body, or malformed JSON. |
| — | TransportException |
The PSR-18 client failed (DNS / TCP / TLS / timeout). statusCode is 0. |
ApiException (and therefore every subclass) carries $statusCode and the
decoded $responseBody:
use Sonnenglas\Takealot\Exceptions\AuthenticationException; use Sonnenglas\Takealot\Exceptions\RateLimitException; use Sonnenglas\Takealot\Exceptions\TakealotException; try { $page = $client->sales()->list(['order_date__gte' => '2026-06-01']); } catch (AuthenticationException $e) { // Check that TAKEALOT_API_KEY is set and still active in the Seller Portal. throw $e; } catch (RateLimitException $e) { sleep($e->retryAfter ?? 5); // ...retry... } catch (TakealotException $e) { error_log("Takealot API error ({$e->getCode()}): {$e->getMessage()}"); throw $e; }
See the error-handling guide for the full decision matrix.
Testing
The SDK is built test-first against php-http/mock-client, so you can exercise
every code path without touching the live API. Swap in a mock PSR-18 client and
a no-op Sleeper to make retry tests instant:
use GuzzleHttp\Psr7\HttpFactory; use GuzzleHttp\Psr7\Response; use Http\Mock\Client as MockClient; use Sonnenglas\Takealot\Client; $mock = new MockClient(); $mock->addResponse(new Response(200, [], json_encode([ 'items' => [/* ...sale rows... */], 'limit' => 20, ], JSON_THROW_ON_ERROR))); $factory = new HttpFactory(); $client = new Client( apiKey: 'key_test', httpClient: $mock, requestFactory: $factory, ); $page = $client->sales()->list(['limit' => 20]);
Run the SDK's own suite with:
composer test # PHPUnit composer phpstan # PHPStan level 9 composer check # phpstan + test together
Versioning
This project follows Semantic Versioning 2.0.0. See CHANGELOG.md for the release history.
Contributing
Pull requests, issues, and discussions are welcome. Please read CONTRIBUTING.md before opening a PR — it covers the dev setup, coding standards, and the test-first workflow used throughout the codebase.
Security vulnerabilities should be reported privately — see SECURITY.md.
License
Released under the MIT License.
Credits
Built and maintained by SONNENGLAS.
If this SDK saves you time, please consider starring the GitHub repository.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 0
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-23