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

Sdk Laravel Package

myparcelnl/sdk

Official MyParcel NL PHP SDK for creating shipments, managing labels, tracking, and handling returns via the MyParcel API. Offers a clean, object-oriented interface for integrating Dutch parcel carriers into Laravel or plain PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and pin. composer require myparcelnl/sdk — there is no Laravel service provider, no config file, and no facade shipped with the package, so treat it as a plain library you wire into the container yourself. Pin an exact minor version in composer.json; the public namespace layout has changed across majors (older releases live under MyParcelNL\Sdk\src\..., newer ones drop the src segment), so an unpinned ^ constraint on a major boundary will silently break every use statement in your app.

Where to look first, in this order:

  1. README.md / examples — the only narrative documentation that exists; it covers the happy path for a single consignment.
  2. src/Model/Consignment/ — the consignment classes are the real API surface. Read AbstractConsignment (or the interface it implements) end-to-end; every setter you'll call daily is declared there.
  3. src/Helper/MyParcelCollection.php — the batch object that actually talks to the API (setApiKey, createConcepts, getLabels, setLatestData).
  4. The carrier-specific consignment subclasses (PostNL, DHL, DPD, …) for which options are supported per carrier.

The minimal flow is always the same four beats: build a consignment → set the API key on the collection → create the concept → fetch the label. There is no single createShipment() call; shipment creation and label retrieval are two separate round-trips, and the label call is where the PDF comes back.

First use case: a Dutch domestic PostNL parcel.

  • Construct a PostNLConsignment, set recipient name, street + number, postcode (1234AB, no space), city, country, and the sender block.
  • Set setReferenceIdentifier() to your order ID — this is the only thing that makes reconciliation possible later, because you can't search MyParcel by your own identifier otherwise.
  • Push it into a MyParcelCollection, call setApiKey($key), then createConcepts(). The collection now holds MyParcel's shipment IDs; call getLabels() and write the returned bytes to storage/app/labels/{order}.pdf.
  • Immediately after createConcepts(), persist the shipment ID and barcode (track_trace) on your own order row. If the process dies between concept creation and label download, that row is how you recover.

Implementation Patterns

Put an adapter between your app and the SDK. Because there's no framework glue, the natural move is a MyParcelService bound in a service provider, taking an API key from config('services.myparcel.key'). Every controller, job, and listener talks to your service; only the service imports MyParcelNL\Sdk\*. This costs one class and buys you the ability to mock the boundary and to survive the next namespace reshuffle.

Model the shipment lifecycle as a state machine on your own table. The SDK has nouns (consignment, collection) and no persistence. Give orders columns like myparcel_shipment_id, myparcel_barcode, label_path, and a status enum (pending → concept → labelled → delivered). Every SDK call is then a guarded transition rather than a fire-and-forget call, which is what prevents double-labelling.

Create concepts in a queued job, not in the request. Label generation is the slowest part of the flow and the most likely to time out. A CreateMyParcelShipment job with $tries = 3 and a ShouldBeUnique lock on the order ID is the pattern that works. Keep the label PDF in object storage and store the path — never re-download a label on page render.

Batch through the collection, not a loop. If you're shipping ten orders, add ten consignments to one MyParcelCollection and call createConcepts() once. The collection issues a single multi-shipment request, which is meaningfully faster and gets you consistent behaviour when one row is invalid (the collection reports which consignments failed rather than throwing on the first).

Treat delivery options as a cache-first, checkout-only problem. The pickup-location and delivery-option lookups are a separate API surface from shipment creation, keyed by postcode + house number + carrier. Don't call them on every page load. Resolve them when the user has a complete address, cache by carrier:postcode:number for a day or so, and re-validate the chosen option right before createConcepts() — availability drifts and a stale pickup point will be rejected server-side.

Store the barcode, build the track-and-trace URL yourself. getTrackTraceUrl()-style helpers exist, but the durable artefact is the barcode. Render the consumer-facing link from your own route so you can change wording, add branding, or fall back to a generic carrier URL if MyParcel is down.

Test your adapter, not the SDK. The SDK constructs its own HTTP client internally, so there's no injection point for Http::fake(). The practical approach is a contract test against your MyParcelService interface plus a small set of recorded real responses (one successful concept, one validation rejection, one label PDF) as fixtures. Do one manual smoke run against the sandbox before each release.

Gotchas and Tips

  • createConcepts() is not idempotent. There is no client-supplied dedupe key on the SDK side unless you set a reference identifier and check for existing shipments first. A retried job without a ShouldBeUnique lock will happily create a second real shipment and bill you for a second label. Lock first, then call.
  • getLabels() returns binary/base64 PDF data. It is not JSON, not a URL, and not safe to return response()->json(). Write it straight to disk. The single most common first-day bug is a mojibake response in a controller.
  • Postcode and house number formatting is validated loosely client-side and strictly server-side. Normalise to 1234AB (uppercase, no space) and split the number off the street before setting the address. If you pass 1234 AB or Dam 1-2, the SDK will accept it and MyParcel will reject the concept with a terse message.
  • API key vs. account ID are different things and the required one varies by endpoint (delivery options historically needed the account ID). Read the error body, not the exception message — exceptions often carry only a generic string while the useful diagnostics sit in the request/response payload you didn't log.
  • Carrier-specific setters throw or silently ignore. AbstractConsignment declares a broad set of options but each carrier supports a subset. Setting a Saturday-delivery flag on a carrier that doesn't offer it may not error until the API call. Validate against the carrier subclass's capability list before you offer the option in your UI.
  • Sandbox/base-URL selection is a setter, not a config file, and it's easy to leave a production key pointed at the sandbox or vice versa. Put the environment flag in config/services.php, assert it in your adapter's constructor, and log which environment each shipment was created against.
  • Retry only the read paths. setLatestData(), track-and-trace, and label re-download are safe to retry with backoff. Concept creation and label generation are side-effecting; retry those only behind your own idempotency guard.
  • Maintenance signal is weak (one maintainer, low star count, NOASSERTION licence — check the actual licence text before shipping commercially). The adapter boundary described above isn't ceremony; it's your exit ramp to raw HTTP calls against the documented MyParcel API if the SDK stalls.
  • Extension points are thin. The realistic customisation hooks are subclassing a consignment to add your own defaults (sender block, package type, signature requirement) and decorating your service around the collection. Don't bother trying to swap out the internal HTTP layer — it isn't designed for it.
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.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment