beziworld / laravel-furgonetka
Furgonetka.pl REST API client for Laravel: OAuth2 with rotating refresh tokens, price calculation, packages, order commands, labels, pickups, parcel points and tracking.
Requires
- php: ^8.3
- ext-json: *
- guzzlehttp/guzzle: ^7.8
- illuminate/contracts: ^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-04 16:36:29 UTC
README
Furgonetka.pl REST API client for Laravel. It ships parcels with InPost, DPD, DHL, UPS, GLS, FedEx, Poczta Polska, Orlen Paczka and more, on Furgonetka's prices or your own carrier contracts:
- OAuth2 (password, client credentials or authorization code) with cached, rotating refresh tokens
- price calculation, package validation and creation
- asynchronous order, pickup and cancellation commands, with polling helpers
- labels (PDF A4/A6, ZPL, EPL), tracking and parcel points search
Installation
composer require beziworld/laravel-furgonetka
php artisan vendor:publish --tag=furgonetka-config # optional
FURGONETKA_CLIENT_ID=... # OAuth2 application registered in Furgonetka.pl FURGONETKA_CLIENT_SECRET=... FURGONETKA_SANDBOX=false # true = api.sandbox.furgonetka.pl FURGONETKA_GRANT=password # password | client_credentials | authorization_code FURGONETKA_USERNAME=shop@example.com FURGONETKA_PASSWORD=... FURGONETKA_CACHE_STORE=redis # shared store: refresh tokens are single-use
Refresh tokens rotate on every use, so every process must read them from the same cache store (redis, database, file on a single server). Refreshing runs under a cache lock to prevent two workers from using the same refresh token.
Authorization code grant
Accounts with two-factor authentication, or applications acting for other users, use the authorization code grant:
// FURGONETKA_GRANT=authorization_code, FURGONETKA_REDIRECT_URI=https://shop.test/furgonetka/callback Route::get('/furgonetka/connect', fn () => redirect(Furgonetka::authorizationUrl(state: csrf_token()))); Route::get('/furgonetka/callback', function (Request $request) { abort_unless(hash_equals(csrf_token(), (string) $request->query('state')), 403); Furgonetka::exchangeCode($request->query('code')); // the code is valid for 30 seconds return redirect('/admin/shipping'); });
The authorization lasts up to three months and is extended by every refresh.
Create and order a package
use Beziworld\Furgonetka\Data\Address; use Beziworld\Furgonetka\Data\PackageRequest; use Beziworld\Furgonetka\Data\Parcel; use Beziworld\Furgonetka\Facades\Furgonetka; $services = Furgonetka::services(); // [['id' => 11, 'service' => 'inpost', 'name' => ..., 'owner' => 'furgonetka'], ...] $request = new PackageRequest( serviceId: 11, pickup: new Address('ul. Prosta 1', '00-001', 'Warszawa', name: 'Jan Nadawca', company: 'ACME', email: 'shop@acme.pl', phone: '500600700'), receiver: new Address('ul. Długa 5', '30-001', 'Kraków', name: 'Anna Odbiorca', email: 'anna@example.com', phone: '600700800', point: 'KRA01M'), parcels: [new Parcel(width: 20, depth: 30, height: 10, weight: 1.5, value: 120.00)], codAmount: 99.90, codIban: 'PL61 1090 1014 0000 0712 1981 2874', reference: 'ORDER-1', additionalServices: ['sms_predelivery_information' => true], ); Furgonetka::validatePackage($request); // ApiException with errors() when invalid $prices = Furgonetka::calculatePrice($request, ['inpost', 'dpd']); $package = Furgonetka::createPackage($request); // state "waiting" $status = Furgonetka::orderAndWait([$package->id]); // runs the order command and polls it $pdf = Furgonetka::label($package->id); // A6 PDF; 'a4' for A4
Ordering, pickups and cancellations are asynchronous commands identified by a UUID the client generates. Use
order(), schedulePickup() and cancel() to start a command and commandStatus() / waitForCommand() to read it,
for example from a queued job. Pass your own UUID to make a retried job reuse the same command.
Pickups, tracking and points
$proposals = Furgonetka::pickupDateProposals([$package->id]); $slot = $proposals[0]['proposals'][0]; Furgonetka::schedulePickup([$package->id], $slot['date'], $slot['min_time'], $slot['max_time'], $slot['hash']); foreach (Furgonetka::tracking($package->id) as $event) { $event->state; // waiting, collected, transit, delivery, delivered, delivery-problem, returned, ... $event->status; // carrier's description $event->occurredAt; } $lockers = Furgonetka::pointsNear(52.2297, 21.0122, ['inpost', 'orlen'], type: 'parcel_machine'); $points = Furgonetka::pointsByPhrase('Kraków Długa', ['dpd', 'dhl']); Furgonetka::cancel([$package->id], 'other', 'Order cancelled by the customer'); Furgonetka::request('GET', '/account/balance'); // any other endpoint
Errors
| Exception | When |
|---|---|
ApiException |
HTTP errors: httpStatus(), field errors() ({path, message, code}), isRateLimited(), retryAfter() |
AuthenticationException |
invalid credentials, revoked authorization, account with two-factor authentication |
InvalidConfiguration |
missing credentials or redirect URI |
Furgonetka allows 5,000 requests per hour per application. After HTTP 401 the client refreshes the token and repeats the request once.
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 a shipping integration for your shop? Get in touch through beziworld.eu.
Other packages by the author
laravel-apaczka · laravel-inpost · laravel-dpd · laravel-dhl-parcel · laravel-orlen-paczka
License
MIT. See LICENSE.md. This package is not affiliated with Furgonetka.pl.