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

Novaposhta Laravel Package

daaner/novaposhta

Laravel wrapper for the Nova Poshta API: look up cities and warehouses, calculate delivery costs, and manage shipments from your PHP app. A clean integration that saves you from raw HTTP calls when building checkout and shipping flows.

View on GitHub
Deep Wiki
Context7

Getting Started

Version 1.4 is a small, additive release for the daaner/NovaPoshta wrapper (Nova Poshta API 2.0). Nothing is removed or renamed, so you can upgrade with composer update daaner/novaposhta and keep your existing InternetDocument chains working.

Minimal setup:

composer require daaner/novaposhta
php artisan vendor:publish --tag=novaposhta   # config file
NOVAPOSHTA_API_KEY=your_key_here

The wrapper is a thin, fluent layer over the API: you pick an API "model" (InternetDocument, Address, ContactPerson, Common, …), set fields with set* methods, then call the API action (save, update, delete, get*).

Where to look first

  1. The README for the fluent examples — the method names mirror the official API 2.0 reference almost 1:1, so the Nova Poshta docs are your real spec.
  2. src/ — each API model lives in its own class; the set* methods are just field assignments, so if the API supports a field, a setter almost certainly exists (or is trivially added).
  3. For 1.4 specifically, look at InternetDocument for the new setAfterpaymentOnGoodsCost() alongside the other COD/settlement setters.

First use case — create a ТТН (waybill)

use Daaner\NovaPoshta\NovaPoshta;

$doc = (new NovaPoshta())->internetDocument()
    ->setPayerType('Sender')
    ->setPaymentMethod('Cash')
    ->setDateTime(date('d.m.Y'))
    ->setCargoType('Cargo')
    ->setWeight(2)
    ->setServiceType('WarehouseWarehouse')
    ->setSeatsAmount(1)
    ->setDescription('Order #1001')
    ->setCost(1500)
    // 1.4: cash on delivery for the goods value
    ->setAfterpaymentOnGoodsCost(1500)
    ->setCitySender($senderCityRef)
    ->setSender($senderRef)
    ->setSenderAddress($senderWarehouseRef)
    ->setContactSender($senderContactRef)
    ->setSendersPhone('380501112233')
    ->setCityRecipient($recipientCityRef)
    ->setRecipient($recipientRef)
    ->setRecipientAddress($recipientWarehouseRef)
    ->setContactRecipient($recipientContactRef)
    ->setRecipientsPhone('380671112233')
    ->save();

If that call returns a Ref and a IntDocNumber, your integration baseline is done — everything after that is reference-data management and status tracking.

Implementation Patterns

Resolve refs, never hardcode them. Cities, warehouses, and counterparties are UUIDs that differ between the sandbox and production, and change when NP reorganises its network. Build a small lookup service that caches them:

$cities = Cache::remember('np:cities', now()->addDay(), fn () =>
    (new NovaPoshta())->address()->getCities()
);

Match by name + area on write, store the Ref on your own model, and only re-resolve on an API errors response.

Wrap the fluent chain behind a service. The chain is expressive but noisy inside controllers. Build one WaybillService::create(Order $order): WaybillData that owns the field mapping (Order → set*) and returns a DTO with Ref / IntDocNumber. This is the single place you touch when NP adds required fields.

New in 1.4 — afterpayment (COD). setAfterpaymentOnGoodsCost() is a InternetDocument setter, so it belongs in the same pre-save() chain. Treat the amount as part of your order snapshot: recompute it server-side from the order total, never from request input, and re-send it via update() if the order total changes before dispatch.

Tracking statuses. 1.4 adds status support (tracking/state handling). API 2.0 has no webhooks for documents, so the reliable pattern is a scheduled poller:

// app/Console/Commands/PollNovaPoshta.php — every 15–30 min
foreach (Waybill::whereNotIn('status_code', [9, 10, 11])->cursor() as $waybill) {
    $status = (new NovaPoshta())->internetDocument()
        ->getStatusDocuments($waybill->ttn);

    $waybill->update([
        'status_code' => data_get($status, '0.StatusCode'),
        'status_raw'  => $status,   // keep the full payload
    ]);
}

Persist the raw payload and the numeric StatusCode. Map codes to your own enum/labels in application code rather than relying on the package's constants — NP adds and renames statuses faster than any wrapper releases.

Testing and CI. The wrapper speaks HTTP underneath, so Http::fake() / Guzzle mock handlers give you deterministic tests with canned success: true and success: false payloads. Point the config at the sandbox key in phpunit.xml and never let the production key leak into CI.

Bulk and queued work. Creating dozens of waybills in a request will hit timeouts and NP rate limits. Push each save() into a job, add ->onQueue('novaposhta') with a low concurrency (redis queue, 2–5 workers) so you don't spike the API, and use retryUntil + backoff() because transient NP errors are common.

Printing and lifecycle. Once you have the Ref, use the package's print/PDF methods to render the label and wire delete() to an order-cancellation listener — orphaned ТТН in NP's system are billable and annoying to clean up manually.

Gotchas and Tips

  • AfterpaymentOnGoodsCost has preconditions. NP only accepts afterpayment when the recipient is the payer of the delivery and the payment method is cash. If you set setPayerType('Sender') and afterpayment, expect a success: false response rather than an exception. Set payer/afterpayment together deliberately, and mirror the same rules in your own validation so users get a readable error.
  • Errors are data, not exceptions. The wrapper surfaces NP's { success: false, errors: [...] } envelope. Every call site needs a success check and a log of the raw response; otherwise failed ТТН silently become null refs in your DB. Wrap the package in one adapter that throws a domain exception on success: false.
  • Phone format is strict. 380XXXXXXXXX, 13 digits, no +, no spaces, no leading 0 short form. Validation failures here are the #1 cause of "recipient not found" errors.
  • Method/field casing is API-exact. InternetDocument, getStatusDocuments, AfterpaymentOnGoodsCost — casing matters and is inconsistent across the API (some PascalCase, some camelCase). Copy names from the official docs; don't guess from the setter name.
  • Production vs sandbox drift. Sandbox reference data, TTN numbering, and some document methods behave differently. Never hardcode a sandbox Ref into a seeder that also runs in production.
  • 1.4's StyleCI commit is formatting-only. If you forked or vendored the package, expect a large whitespace-only diff — do not treat it as behaviour change, but do rebase carefully to avoid conflict noise. If you consume via Composer, this is irrelevant.
  • Rate limits and API key scope. NP enforces per-key limits and some methods require an extended/paid key. Batch lookups, cache reference data aggressively (cities/warehouses: hours-to-days TTL; TTN statuses: minutes), and back off on errors instead of retrying in a tight loop.
  • Extension point. Before forking for a missing field, check whether the model class exposes a raw data/custom-call path — most missing set* methods are a one-line addition to the field array, and a small PR upstream beats maintaining a divergent copy.
  • Don't trust the fluent chain to be idempotent. save() creates; re-running it on a retry produces duplicate waybills. Guard with an idempotency check on your own Waybill record (Ref already stored → skip) before retrying a job.
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.
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
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle