awd-studio/novaposhta
Laravel package for integrating Nova Poshta (Нова Пошта) shipping into your app, offering an API client for city and warehouse lookups, delivery cost calculation, and tracking data. A small, low-adoption package with minimal documentation.
This release (v2.0.0-alpha1) restructures the package around three cooperating pieces: a POST data entity, a GET query entity, and static factories that produce a preconfigured cURL request for each verb. The intent is that you stop hand-assembling curl_setopt arrays and instead build a typed payload object, hand it to a factory, and execute.
Minimal path:
composer require <vendor>/<package>:2.0.0-alpha1. Do not use a floating constraint; alpha APIs move.$response = PostRequestFactory::create($url)
->withData(PostData::make([
'name' => 'Ada',
'email' => '[email protected]',
]))
->execute();
$response = GetRequestFactory::create($url)
->withQuery(GetQuery::make(['page' => 2, 'filter' => 'active']))
->execute();
Your first genuine use case is almost always an outbound call to a third-party API inside a queued job or a service class — not inside a controller. Start there so retries and timeouts are visible early.
Wrap the factory in a dedicated client class. The static factories are convenient but should not be sprinkled through your application. Create one class per external service that owns URL construction, auth headers, and response handling:
final class BillingClient
{
public function __construct(private string $baseUrl, private string $token) {}
public function charge(array $payload): array
{
$request = PostRequestFactory::create("{$this->baseUrl}/charges")
->withHeaders(['Authorization' => "Bearer {$this->token}"]);
return $request->withData(PostData::make($payload))->execute()->json();
}
}
Bind this in a service provider so tests can swap it. This is also your escape hatch if the alpha API changes — one class to update, not forty call sites.
Build entities from validated input, not from $request->all(). The POST entity is the right place to enforce shape. Map from a FormRequest or a DTO:
$data = PostData::make($request->validated());
Keeping the entity constructed only from validated or explicitly-typed arrays prevents accidental leakage of CSRF tokens or internal fields into the outbound body.
Keep GET query construction separate from the URL string. The query entity exists so you can pass associative arrays (including nested filters) and let the package encode them. Compose it independently, then attach:
$query = GetQuery::make(['tags' => ['php', 'laravel'], 'since' => $date]);
This makes query objects testable in isolation — assert the produced string without any network call.
Normalise the response at the boundary. The factory returns a cURL-shaped result, not a Laravel Response. Convert immediately into an array, a DTO, or throw a domain exception on non-2xx. Every caller downstream should deal in your types, never in raw cURL output.
Use it from queued jobs, not request cycles. Outbound HTTP in a controller blocks the user. Put the client call behind a job with $tries and backoff(), and set the package's timeout to something shorter than the job timeout — cURL's default is unlimited, which is a silent hang waiting to happen.
This is an alpha, and it is a major version. v2.0.0 introduces entities and static factories, which strongly implies the v1 array-based API is gone or reshaped. Treat the entire public surface as unstable until 2.0.0 stable lands. Pin the exact version, and if you have v1 code in production, read the source diff before upgrading — the release notes describe additions, not removals, but a major bump rarely ships without breaking changes to the request construction path.
Static factories are hard to mock. Static methods resist dependency injection, so Http::fake() will not intercept these calls — this package drives cURL directly and bypasses Laravel's HTTP client entirely. That means you lose Http::fake(), Http::retry(), middleware, and recorded request assertions for free. Options: wrap in the client class described above and fake your class with a container binding; or point the base URL at a local stub server in tests. Decide this before you have fifty tests depending on live calls.
No Http::retry() equivalent. Retry, backoff, and circuit-breaking are your responsibility. Implement them in the wrapper, and only for idempotent verbs — do not retry POST charges.
Check handle lifecycle. cURL handles must be closed. Confirm whether execute() closes internally or whether the request object exposes a close()/__destruct. In long-running workers (Octane, queue daemons) a leaked handle per job is a slow memory climb that only shows up under load.
Nested query encoding is the classic failure point. Array encoding differs between http_build_query defaults and what many APIs expect (tags[]=php versus tags=php&tags=laravel). Verify the GET entity's output for arrays, booleans (true versus 1), and nulls before trusting it against a real endpoint.
Timeouts and TLS. Set connect and total timeouts explicitly on every request. Alpha packages frequently ship without defaults. Also verify certificate verification is on by default — a disabled CURLOPT_SSL_VERIFYPEER in a convenience wrapper is a security regression you inherit silently.
Error detection is manual. cURL returns false on transport failure and a body on HTTP 500 alike. Assert on both the transport result and the status code; do not assume a non-empty body means success.
Extension points to watch for. If the entities expose setters or accept partial arrays in their constructors, prefer immutability in your own code — clone rather than mutate a shared entity, especially if you cache a base entity per client and vary one field per call. If factories allow subclassing or the entities implement an interface, that interface is your best long-term insulation against alpha churn: depend on it, not on the concrete factory.
Reassess at 2.0.0 stable. The alpha-to-stable window is where naming and signatures settle. Note this assessment as provisional and re-review before standardising the pattern across more than one service.
How can I help you explore Laravel packages today?