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

Laravel Uk Kyb Edge Laravel Package

jamesforsyth/laravel-uk-kyb-edge

A Laravel package for UK KYB (Know Your Business) checks, offering a clean API for company verification and edge-case handling. Configure services, run checks, and integrate compliance workflows quickly.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and configure

composer require <vendor>/uk-business
php artisan vendor:publish --tag=uk-business-config

Set the credentials in .env (names follow the published config — check config/uk-business.php after publishing):

UK_BUSINESS_API_KEY=
UK_BUSINESS_BASE_URI=

Where to look first

  1. config/uk-business.php — base URI, key, timeout, retry settings.
  2. The UKBusiness facade accessor and UKBusinessClient constructor — the client is a thin Guzzle wrapper, so every method maps to one endpoint.
  3. The two new methods in v1.1.0: financials() and document().

First use case — pull a structured balance sheet for a filed document:

use UKBusiness;

$financials = UKBusiness::financials($documentId);

// ['balance_sheet' => [...], 'period_end' => '...', ...] depending on the schema

Then stream the source PDF to confirm the numbers came from the right filing:

return UKBusiness::document($documentId)
    ->streamDownload("{$documentId}.pdf");

Start with financials() in a tinker session or an artisan command before wiring it into HTTP — see the timeout notes below.

Implementation Patterns

Facade for one-offs, injected client for services. The facade resolves the singleton client; in longer-lived classes inject UKBusinessClient so you can swap it in tests without touching the container.

Treat financials() as a slow, billable job. Vision extraction is not a lookup — it parses a PDF. Push it to a queue with a generous timeout and an explicit, persisted result:

final class ExtractFinancials implements ShouldQueue
{
    public int $timeout = 180;
    public int $tries = 3;
    public array $backoff = [30, 120];

    public function __construct(public string $documentId) {}

    public function handle(UKBusinessClient $client): void
    {
        $data = $client->financials($this->documentId);

        CompanyDocument::where('document_id', $this->documentId)
            ->update([
                'financials' => $data,
                'extracted_at' => now(),
            ]);
    }

    public function retryUntil(): DateTimeInterface
    {
        return now()->addHours(2);
    }
}

Cast the column to array/json and version the payload — extraction output can change shape between releases, so store the package version alongside it:

'financials' => $data,
'financials_schema' => 'v1.1.0',

Proxy the PDF, don't hotlink it. document() is described as streaming with CORS headers enabled, which is convenient for a browser but tempting to abuse. In a Laravel app you generally want your own signed, authenticated route in front of it:

Route::get('/documents/{document}', function (CompanyDocument $document) {
    return UKBusiness::document($document->document_id)
        ->streamDownload("{$document->document_id}.pdf");
})->middleware(['auth', 'can:view,document']);

If you're genuinely exposing the upstream URL to a browser, understand that the CORS headers are applied by the registry, not by your app — your own config/cors.php governs any route you serve. Don't let the browser call the upstream directly with your API key.

Persist PDFs to disk, then re-run extraction from the stored copy. Download once, extract many. This makes re-extraction after a package upgrade cheap and keeps a local audit trail:

Storage::disk('documents')->writeStream(
    "{$documentId}.pdf",
    UKBusiness::document($documentId)->toPsrResponse()->getBody()
);

Cache extraction results on documentId. The same filing yields the same balance sheet; a cache-aside wrapper with a long TTL removes almost all repeat cost:

$financials = Cache::rememberForever(
    "ukb:financials:{$documentId}",
    fn () => UKBusiness::financials($documentId)
);

Fake in tests. Because the client is Guzzle-based, Http::fake() works if the package routes through Laravel's HTTP client; if not, bind a stub UKBusinessClient in the container and assert the job dispatched financials() once with the expected ID. Never hit the live registry in CI.

Chain the two methods for a "fetch and verify" flow: extract financials, store them, then queue a second job that downloads the PDF for archival. Keeping them separate means a PDF storage failure doesn't lose an expensive extraction.

Gotchas and Tips

Timeouts are the number-one failure mode. Guzzle's default timeout is far too short for vision extraction on a 60-page filing. Set an explicit per-call timeout (or a separate extraction_timeout in config) and make sure it's lower than your queue worker's --timeout, or the worker gets killed mid-request and you're billed twice.

Don't blindly retry extraction. A 500 or timeout may mean the extraction succeeded server-side but the response was lost. Key your job on document_id and make the write idempotent (upsert, not insert), otherwise retries produce duplicates.

Streaming is not the same as buffering. document() returns a streamed response; calling ->body() or ->json() on it loads the whole PDF into memory. Use streamDownload(), ->toPsrResponse()->getBody() into writeStream, or Storage::put with a stream. Large filings will blow past memory_limit otherwise.

CORS headers on the upstream response do not make your proxy CORS-correct. If your own route is consumed cross-origin, add it to your paths in config/cors.php. Also confirm whether the headers are on the success path only — error responses from a proxy frequently lose them and show up as opaque CORS failures in the browser console.

Validate the document ID before spending money. Registry IDs are not the same as company numbers. Reject malformed input at the FormRequest layer; a bad ID may still return 200 with an empty or partial extraction.

Scanned or image-only PDFs can return partial structures. Vision extraction is probabilistic: fields may be missing rather than erroring. Validate the response shape before persisting and mark documents as needs_review when key sections are absent instead of failing the job.

Handle 429s with backoff, not immediate retry. Registry endpoints rate-limit per key. Laravel's $backoff on the job plus a shared rate limiter (RateLimiter::for('uk-business', ...)) is the simplest defence if you fan out extractions.

The v1.1.0 client changes are the upgrade risk. The release notes say the Guzzle methods were updated — that's the layer everything else sits on. Even though it's a minor version, re-run your suite and check the return types of any method you were calling directly on UKBusinessClient rather than through the facade. If the client previously returned a raw Response, confirm that's unchanged for existing methods.

Debug by logging the raw body first. When extraction returns something unexpected, dump the unparsed response before any transformation — most "wrong numbers" reports are actually a mismatched document ID or a partially-scanned PDF, not a parsing bug.

Extension point: financials() returns a plain array, so wrap it in your own DTO/action class (FetchCompanyFinancials) rather than spreading the facade call across controllers. That's the seam where you'll add caching, versioning, and schema validation later.

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.
labyrinthe/connect-php
encore2/laravel-admin2
cboxdk/laravel-console-kit
shapecode/doctrine-dbal-datetimeutc
gilads-otiannoh254/inertia-protocol
dakiquang/laravel-oauth-refresh-lock
woda/laravel-worktrees
dalefranks/ramasite
faghy/laracashbook
roundly-consulting/google-places-for-laravel
roundly-consulting/kubernetes-api-for-laravel
roundly-consulting/plausible-for-laravel
gvp/gvp-laravel-template
metasyncsite/laravel-metasync-client
roundly-consulting/onboarding-for-laravel
adachsoft/cli-process-tester
adachsoft/changelog-linter
adachsoft/collection
adachsoft/php-code-style
adachsoft/api-version-bundle