Download the PHP package starmile/partner-sdk without Composer
On this page you can find all versions of the php package starmile/partner-sdk. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download starmile/partner-sdk
More information about starmile/partner-sdk
Files in starmile/partner-sdk
Package partner-sdk
Short Description Official PHP SDK for the Starmile Partner API — cross-border logistics integration (catalogue, orders, status pool, inbound events).
License MIT
Homepage https://api.starmile.io
Informations about the package partner-sdk
Starmile Partner SDK (PHP)
Official PHP SDK for the Starmile Partner API — the partner-facing integration surface of the Starmile cross-border logistics platform. It wraps every Partner API capability behind a small, typed, dependency-free client: catalogue lookup, order intake and management, the pull-based status pool, and inbound lifecycle events.
- No runtime dependencies beyond
ext-curlandext-json— installs cleanly on legacy stacks. - Wide PHP support: 7.1 → 8.4.
- Automatic OAuth2 — tokens are fetched, cached, and refreshed for you.
- Typed exceptions mapped from HTTP status codes.
- Fluent builders for orders, parcels, products, and events, with local validation of event types and fields before anything hits the network.
This package is a thin, faithful client over the HTTP API. The HTTP API remains the canonical contract; the public reference lives at api.starmile.io.
Installation
Requires PHP 7.1+ with the curl and json extensions.
Authentication
The Partner API uses OAuth2 client credentials. Your organization's back office
issues you a client_id and client_secret (a Partner API credential). The SDK
exchanges them for a short-lived bearer token at POST /oauth/token and attaches
it to every call — you never touch the token endpoint directly.
Keep the client_secret server-side. Read it (and the base URL) from the
environment — never hardcode credentials or per-environment hosts.
Configuration options
Client::create($clientId, $clientSecret, $options) accepts:
| Option | Default | Description |
|---|---|---|
base_url |
https://api.starmile.io |
API host. Point at your sandbox/staging outside production. |
scopes |
all granted | Subset of the credential's scopes to request on the token. |
http_client |
bundled cURL client | Any HttpClientInterface (Guzzle/Symfony/PSR-18 adapter, a mock). |
token_storage |
in-memory | A TokenStorageInterface to share one token across processes. |
verify_tls |
true |
TLS verification (keep on in production). |
connect_timeout |
10 |
Connection timeout (seconds). |
timeout |
30 |
Total request timeout (seconds). |
user_agent |
starmile-partner-sdk-php/x |
Override the User-Agent. |
max_attempts |
3 |
Total attempts for safe (GET) calls on transient failure. 1 disables auto-retry. |
retry_base_delay_ms |
200 |
Base backoff between retries (exponential + jitter). |
retry_max_delay_ms |
5000 |
Cap on the computed backoff. |
Capabilities
The Partner API surface is reached through four resource groups, each gated by the scopes on your credential.
| Resource | Scope(s) | Endpoints |
|---|---|---|
$starmile->catalogue() |
catalogue:read |
GET /api/v1/services, GET /api/v1/rates |
$starmile->orders() |
orders:create, orders:update, orders:cancel |
POST /api/v1/orders, PATCH /api/v1/orders/{order}/parcels/{parcel}, POST /api/v1/orders/{order}/parcels/{parcel}/cancel, POST /api/v1/orders/{order}/cancel |
$starmile->statusPool() |
status:read |
GET /api/v1/partner/changes |
$starmile->events() |
events:transport, events:pudo, events:customs, leg:handoff |
POST /api/v1/partner/events |
Catalogue
Each rate's service_id is a Service's own id (the same value you send on
order creation), so a rate maps to exactly one Service — correlate a rate to a
service by matching $rate['service_id'] to $service['id'].
Orders
Build an order with the fluent builders (or pass a raw array matching the API body). The corridor and delivery type come from the Service; you do not send a rate — Starmile resolves the billing rate automatically.
Creating an order is idempotent on your order_id. Re-sending one you already
used (a retry after a timeout, a double submit) creates nothing: the original order
is replayed — HTTP 200 instead of 201, duplicate is true, and order_id,
region_status and items[] are exactly what you got the first time. Use it to
recover our ids when you are unsure a create landed; to change an accepted order,
use updateParcel() instead — the rest of the body is ignored on a replay.
For a Home Delivery service the destination region is resolved from your own
(parent_region, region) reference, map-only per partner. If it is not mapped yet
the order is still accepted and comes back with region_status
pending_mapping — an operator maps your region in Starmile and the waiting order
resolves automatically, so you do not resend it. mapped means the region was
resolved; not_applicable means the service has no home region (PUDO / locker /
clearance). Compare against Starmile\PartnerSdk\Enum\RegionStatus.
Orders and parcels are addressed by your own references afterwards — the
order_id you sent, and a parcel's item_id (which the parcel carries back as
partner_tracking in responses):
Parcel labels (PDF)
Download a SINGLE parcel's label as a PDF, rendered from your organization's default
parcel label template. Address the parcel by its merchant_tracking (sticker code) or
its parcel_id (our parcel id, returned as items[].parcel_id on create). The method returns the raw PDF bytes. Scope:
labels:read.
Status pool (replaces webhooks)
Poll the pool with the cursor you last processed; you receive every change after it, in order. Persist the returned cursor to resume next time.
An order's feed opens with the status it was created in (typically
waiting_for_arrival), carrying previous_status: null — so the first row you
read for an order is always the point we took it on, not whichever hub event came
first. previous_status is non-null on every row after that.
external_parent_id is your own reference for the order. external_id is your
reference for a single parcel (the item_id you sent on create) and is present
only on parcel-scoped changes — e.g. a parcel received at the hub — so you can act
on that exact parcel; order-level changes leave external_id null. country is the
ISO-2 country the change occurred in (the hub's country), so you can tell an origin
event (e.g. CN) apart from a destination one (e.g. AZ). occurred_at is a plain
Y-m-d H:i:s timestamp (e.g. 2026-06-20 09:14:00); timezone gives the IANA zone
it is expressed in (e.g. UTC) — parse occurred_at in that zone.
Why a change happened
A change that has a reason carries reason — a stable code from Enum\Reason —
and sometimes reason_detail, free text a person wrote. Branch on reason; show
reason_detail to a human.
Codes cover customs holds (missing_declaration, inaccurate_information,
prohibited_content_restricted_item,
commercial_quantity_personal_allowance_exceeded), failed deliveries
(customer_absent, address_not_found, customer_refused,
could_not_reach_customer, wrong_or_incomplete_address) and cancellations
(cancelled_by_partner, cancelled_by_customer, cancelled_by_operator). When
you report an event whose data carries a reason, sending one of these codes
publishes it on the merchant's feed as a code they can act on automatically; any
other wording is passed through untouched as free text.
Inbound events
Report a lifecycle event for a package you handle as a carrier (shipment.*),
PUDO point (parcel.*), customs broker (customs.*), or foreign organization on
the next leg (leg.*). The EventBuilder validates the type and its data fields
locally — an unknown type or field throws before a request is made.
Each accepted event advances the package. An event that is not a legal next step
for the package's current status is rejected with a 422 carrying an error and a
hint (see error handling below).
The recognised event types, their scope, and their data fields are exposed on
the EventType enum:
Error handling
Non-2xx responses raise a typed exception; all extend StarmileException.
| Exception | HTTP | Meaning |
|---|---|---|
AuthenticationException |
401 | Missing/expired/invalid token or bad credentials. |
AuthorizationException |
403 | Credential lacks the scope, is revoked, or API off. |
NotFoundException |
404 | Unknown resource (or another partner's). |
ConflictException |
409 | No longer changeable (shipment received / in custody). |
ValidationException |
422 | Validation failed; field errors + event hint. |
RateLimitException |
429 | Too many requests; carries getRetryAfter(). |
ApiException |
other | Any other non-2xx; base for the above. |
TransportException |
— | Network failure (no response). |
Retries & resilience
By default the SDK retries safe (GET) requests on transient failures —
network errors, 429, and 5xx — with exponential backoff + jitter, honoring a
Retry-After header. Non-idempotent writes (POST /orders, POST /partner/events)
are never retried automatically, so a flaky response can't create a duplicate
order. Tune or disable this with max_attempts (see options above). Creating an
order is safe to retry either way — it is idempotent on your order_id.
When you do want a write retried, opt in per call with retry() — mirroring
Laravel's HTTP client. It returns a one-off client; the original is unchanged:
When a failure can't be decoded as JSON (e.g. a gateway's HTML 502), the raw
body is preserved on the exception via getRawBody():
Constants
The SDK ships the server vocabularies verbatim so you compare against names, not magic strings:
Starmile\PartnerSdk\Enum\Scope— every OAuth2 scope.Starmile\PartnerSdk\Enum\EventType— every inbound event type (+ scope/field maps).Starmile\PartnerSdk\Enum\OrderStatus— every status seen in the pool.Starmile\PartnerSdk\Enum\PackageType—fragile/breakable/liquid.Starmile\PartnerSdk\Enum\DeliveryMethod—home/pudo/locker.
Custom HTTP transport
Replace the default cURL transport with anything implementing
HttpClientInterface (e.g. to reuse a configured Guzzle client, add retries, or
mock in tests):
Token sharing across processes
By default the token lives in process memory. Implement TokenStorageInterface
(backed by APCu, Redis, a PSR-16 cache, or a file) and pass it as token_storage
to reuse one token across requests/workers and avoid re-hitting /oauth/token.
Testing
The suite drives the client through a scripted in-memory transport — no network.
Versioning & support
Semantic Versioning. New Partner API capabilities are added here in lockstep with the server; see the CHANGELOG.
License
MIT — see LICENSE.
All versions of partner-sdk with dependencies
ext-curl Version *
ext-json Version *