Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Speedy Laravel Package

gdinko/speedy

Laravel package for integrating the Speedy courier API. It simplifies shipping rate calculations, shipment creation, label generation, parcel tracking, and courier office or pickup management, helping PHP applications connect to Speedy services with less boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

Start with the README — for a 21-star wrapper, example snippets are usually the only authoritative docs. Then trace the service provider and config file to understand what's actually bound in the container.

Install:

composer require gdinko/speedy
php artisan vendor:publish --provider="Gdinko\Speedy\SpeedyServiceProvider"

Populate credentials in .env (username/password or API key — check config/speedy.php for exact keys). Speedy typically issues credentials for a test/demo environment, so point the config at that base URL first.

Your first use case should be a rate quote — it's read-only, exercises auth + HTTP client + response mapping without creating real courier orders. Something like:

$quote = Speedy::calculate([
    'recipient' => ['city' => 'Sofia', 'postcode' => '1000'],
    'weight' => 1.5,
    'service' => 'PICKUP_TO_DOOR',
]);

If that returns data, your credentials and HTTP wiring are correct. Only then move on to creating shipments. Look first at:

  • config/speedy.php — env vars, endpoints, defaults
  • The main service class / facade — the public method surface is your real API
  • Tests, if any — they encode expected request/response shapes better than prose

Implementation Patterns

Wrap it behind your own service. Don't scatter Speedy:: calls through controllers. Create App\Services\ShippingService that depends on the package class (or facade), and inject it. This isolates you from the wrapper if you ever swap to a direct API client or a competitor (Econt is the usual alternative in Bulgaria). Laravel's container makes this painless:

class ShippingService
{
    public function __construct(private \Gdinko\Speedy\Speedy $speedy) {}

    public function quoteForOrder(Order $order): array
    {
        return $this->speedy->calculate([
            'weight' => $order->total_weight_kg,
            'recipient' => $order->shipping_address->toSpeedyPayload(),
        ]);
    }
}

Treat creation and tracking as separate workflows. Shipment creation returns an identifier/waybill that you persist on your order model. Tracking is a periodic poll — a scheduled job that loops over orders with a non-terminal status, calls tracking, and updates state. Don't try to inline tracking into a request/response cycle; courier APIs are slow and flaky.

Normalize addresses once. Speedy APIs are picky about city IDs vs. city names, postcode formats, and street/site codes. Add a mapper that converts your internal address model into the shape Speedy expects, and cache the city/site lookups (they change rarely and are usually a separate API call).

Use jobs for anything creating a shipment. Queue it with retries and a unique constraint — a partial failure can leave you uncertain whether the shipment exists. Store an idempotency key (order ID + attempt) if the API supports one; otherwise query by reference before retrying to avoid duplicates.

Log request/response pairs for anything that mutates. Courier errors are often vague; the raw payload is what their support team will ask for.

Gotchas and Tips

  • Facade vs. injected class. Most small wrappers register a singleton plus a facade. A singleton with mutable state can bite you in long-running workers (Octane, queues) — prefer resolving a fresh instance or ensure the wrapper is stateless. If unsure, app()->make() per use.
  • Config cache. After editing config/speedy.php or .env, run php artisan config:clear. Credential changes silently ignored after caching is the #1 "it stopped working" cause.
  • Test environment drift. Sandbox payloads often validate differently from production (weight limits, allowed services, address fields). Do one real shipment manually before trusting the sandbox.
  • Error handling is thin. Small wrappers typically throw a generic exception or return a raw array on failure. Wrap calls in try/catch, inspect the response body, and translate courier error codes into your domain errors rather than leaking them to your UI.
  • Timeouts. The default Guzzle timeout may be too short or nonexistent depending on config. Set explicit connect/read timeouts and retry only on connection errors, never on 4xx.
  • Extension point: if the wrapper doesn't expose an endpoint you need, look for a way to inject a configured Illuminate\Http\Client\Factory or access the underlying client. That's usually the cleanest escape hatch short of forking.
  • Small-package risk. 21 stars and a single maintainer means you may hit unpatched edge cases. Pin the version in composer.json, and plan for the possibility of forking — keeping the wrapper behind your ShippingService is what makes that cheap.
  • Currency and locale. Speedy is Bulgarian-market; prices, weight units, and date formats may differ from your app's defaults. Assert units explicitly at the boundary rather than assuming.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
muka/shape-reader
tdt/json
refinery29/php-cs-fixer-config
hotmeteor/eco-env
mvaliolahi/hydrate
wearede/tbcpay-php
parsnick/steak
thibaud-dauce/mattermost-php
multilinguals/apollo-client
antaresbr/collection
kr0lik/phpstan-rules
loco/loco
starfederation/datastar-php
yupmin/modern-phystrix
stephenjude/extended-artisan-commands
lumphp/laravel-macroable
lumphp/laravel-collections
lumphp/laravel-contracts
sermepa/sermepa
arkecosystem/fortify