beziworld / laravel-polkurier
Polkurier.pl WebService API client for Laravel: carriers, price valuation, orders, labels, handover protocols, courier pickups, tracking, COD details, parcel points and address book.
Requires
- php: ^8.3
- ext-json: *
- guzzlehttp/guzzle: ^7.8
- illuminate/cache: ^12.0|^13.0
- illuminate/console: ^12.0|^13.0
- illuminate/contracts: ^12.0|^13.0
- illuminate/events: ^12.0|^13.0
- illuminate/http: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.18
- orchestra/testbench: ^10.0|^11.0
- pestphp/pest: ^3.7|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
- phpstan/phpstan-strict-rules: ^2.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-10 18:14:39 UTC
README
Polkurier.pl WebService API client for Laravel. Polkurier ships parcels, envelopes and pallets with DPD, InPost, DHL, UPS, Raben and other carriers on Polkurier's prices:
- carriers with their services, shipment types, pickup rules and carrier specific fields
- price valuation of every carrier (order_valuation_v2) and a cheapest-offer helper
- orders with COD, insurance, additional services, cover address, points and external order ids
- labels and handover protocols (PDF), cancellation
- courier pickup hours, pickup days and multi-order pickups
- status, order details with waybills, payment state, itemised price and COD settlement history
- courier points and parcel lockers, countries, localities by postal code, points map token
- address book, COD bank accounts (with two-factor confirmation) and pack templates
- several accounts (named connections or runtime credentials) and a status poller with events
Why this package
The official polkurier/polkurier-sdk is a framework-agnostic PHP 5.6
SDK built on raw cURL with mutable setter objects. This package talks to the same API through the Laravel HTTP
client, so every call can be faked with Http::fake(), and adds immutable DTOs (amounts as decimal strings, dates in
Europe/Warsaw), typed exceptions with the API error codes, safe retries of read-only methods, configuration,
a facade, multiple accounts and a scheduler-friendly status poller. Request shapes follow the official PDF
documentation (version 1.14) and the SDK.
Installation
composer require beziworld/laravel-polkurier
php artisan vendor:publish --tag=polkurier-config # optional
POLKURIER_LOGIN=123456 # account id POLKURIER_TOKEN=... # Panel klienta > Ustawienia > Token API POLKURIER_SANDBOX=false # true = api-sandbox.polkurier.pl (access from api@polkurier.pl)
Prices and carriers
use Beziworld\Polkurier\Data\Address; use Beziworld\Polkurier\Data\OrderRequest; use Beziworld\Polkurier\Data\Pack; use Beziworld\Polkurier\Enums\Service; use Beziworld\Polkurier\Facades\Polkurier; $carriers = Polkurier::carriers(details: true); // list<Carrier> $dpd = Polkurier::carrier('DPD'); $dpd?->supportsCod(); $dpd?->supportsService('ROD'); $dpd?->additionalFields(); // carrier specific order fields $quote = new OrderRequest( sender: Address::postcode('63-400'), recipient: Address::postcode('60-001'), packs: [new Pack(length: 30, width: 20, height: 10, weight: 2.5)], services: [Service::SmsNotification], ); foreach (Polkurier::valuation($quote) as $price) { $price->carrier; // "DPD" $price->priceGross; // "14.76" $price->available; // false with $price->unavailableMessage when the carrier cannot take it } $best = Polkurier::cheapest($quote);
Orders, labels and protocols
use Beziworld\Polkurier\Data\Cod; use Beziworld\Polkurier\Data\Pickup; use Beziworld\Polkurier\Enums\CodReturnTime; $order = Polkurier::createOrder(new OrderRequest( sender: new Address('Jan Kowalski', 'Kurierska', '1', '63-400', 'Ostrów Wielkopolski', '500600700', 'shop@example.com', company: 'ACME'), recipient: new Address('Anna Nowak', 'Poznańska', '12', '60-001', 'Poznań', '600700800', 'anna@example.com', pointId: 'POZ01M'), packs: [new Pack(30, 20, 10, 2.5)], courier: 'INPOST_PACZKOMAT', description: 'Books', cod: new Cod('149.99', 'PL61109010140000071219812874', CodReturnTime::OneDay), insurance: 200, pickup: Pickup::dropOff(), // or Pickup::at('2026-11-12', '10:00', '14:00') or Pickup::multi() externalApplicationId: 'my-shop', externalOrderId: 'A-1001', )); $order->orderNumber; // "1234-10" $order->waybill(); // "13299300045383" $order->priceGross; // "12.30" $pdf = Polkurier::label($order->orderNumber); // or label(['1234-10', '1234-11']) merged into one PDF $protocol = Polkurier::protocol(['1234-10', '1234-11']); Polkurier::cancelOrder('1234-10'); // orders waiting for payment or confirmed
createOrder(), cancelOrder(), multiPickup() and the save and delete methods are never repeated automatically.
Read-only methods are repeated after connection errors and HTTP 5xx (POLKURIER_RETRIES).
Pickups
use Beziworld\Polkurier\Enums\ShipmentType; $day = Polkurier::pickupHours('DPD', '2026-11-12', '63-400'); // PickupDate: available, windows $days = Polkurier::pickupDates('DPD', ShipmentType::Box, '63-400'); // next five working days // Orders created with Pickup::multi() share one courier pickup: $waiting = Polkurier::orders(['status' => 'PZ']); $result = Polkurier::multiPickup(['1234-56', '1234-78'], '2026-11-12', '10:00', '15:00'); $result->failed; // ['1234-78' => 'error message']
Tracking, order details and COD
$status = Polkurier::status('1234-10'); $status->code; // OrderStatus::InTransit $status->trackingUrl; $details = Polkurier::order('1234-10'); $details->waybills; // default waybill first $details->isPaid; $details->items; // itemised price $details->cod?->status; // CodStatus::Paid $details->cod?->operations; // payouts, compensations and fees $details->cod?->paidOut(); // "70.00" $page = Polkurier::orders(['date_from' => now()->subWeek(), 'courier' => 'DPD'], page: 1, pageSize: 100);
Polkurier has no webhooks. Schedule the poller to receive OrderStatusChanged events:
// routes/console.php Schedule::command('polkurier:poll-statuses --pages=2')->everyFifteenMinutes(); // a listener Event::listen(function (\Beziworld\Polkurier\Events\OrderStatusChanged $event) { $event->order->number; $event->order->statusCode; // new status $event->previousStatus; // e.g. "OC" });
Points, countries and dictionaries
use Beziworld\Polkurier\Enums\PointFunction; $lockers = Polkurier::points('INPOST_PACZKOMAT', search: 'Poznań', functions: [PointFunction::Cod], limit: 50); $point = Polkurier::point('INPOST_PACZKOMAT', 'POZ01M'); Polkurier::countries('UPS_EX'); // ['DE' => 'Niemcy', ...] Polkurier::supportedCountries(); // list<Country> with provinces Polkurier::cities('63-400'); // ['Ostrów Wielkopolski', ...] Polkurier::mapToken(); // JWT for the Polkurier points map widget (valid 4 hours)
Account data
use Beziworld\Polkurier\Data\AddressBookEntry; use Beziworld\Polkurier\Data\BankAccount; use Beziworld\Polkurier\Enums\AddressType; Polkurier::addresses(AddressType::Sender); Polkurier::saveAddress(new AddressBookEntry(AddressType::Sender, $address, name: 'Warehouse', default: true)); $session = Polkurier::requestTwoFactorCode(); // SMS code to the account phone Polkurier::saveBankAccount(new BankAccount('PL61109010140000071219812874', 'Main'), $session->token, $codeFromUser); Polkurier::packTemplates();
Several accounts
// config/polkurier.php: 'connections' => ['default' => [...], 'second' => ['login' => ..., 'token' => ...]] Polkurier::connection('second')->createOrder($request); // credentials stored per tenant Polkurier::using(['login' => $tenant->polkurier_login, 'token' => $tenant->polkurier_token])->valuation($quote);
Any other method: Polkurier::call('get_couriers_messages', ['courier' => 'DPD']) returns the response part.
Errors
| Exception | When |
|---|---|
ApiException |
the envelope status is error: errorCode() (e.g. ERROR_AUTH_DATA), statusCode() (e.g. 7), validationErrors(), method(), isAuthenticationError(), isRateLimited() with retryAfter() (60 s), isMaintenance(), isNotFound(); also transport errors and HTTP errors without an envelope |
InvalidConfiguration |
missing login or token, unknown connection |
All exceptions extend PolkurierException. Business errors such as missing funds or a refusal by the carrier come
with the message only (errorCode() is null). The API accepts 500 requests per minute per IP address; after
TOO_MANY_REQUESTS pause every request for at least 60 seconds.
Development
composer install
composer ci # pint --test, phpstan (level 8), pest
Author
Created and maintained by Kamil Ćwiertnia, beziworld.eu.
- E-mail: biuro@beziworld.eu
- Issues: GitHub issues
Need an integration? Get in touch through beziworld.eu.
Other packages by the author
laravel-apaczka · laravel-furgonetka · laravel-globkurier · laravel-inpost · laravel-dpd
License
MIT. See LICENSE.md. This package is not affiliated with Polkurier.pl.