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

Salesteer Laravel Laravel Package

devua/salesteer-laravel

Laravel integration for SaleSteer that adds a service provider, publishable configuration, and a ready-to-use API client for connecting your application to the SaleSteer platform. Simplifies authentication, requests, and data sync between Laravel apps and SaleSteer.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check first. This is a zero-star, zero-dependent package with a single maintainer. Treat it as a thin convenience wrapper over the Salesteer HTTP API, not as infrastructure you can lean on blindly. Before writing feature code, spend 15 minutes reading src/ and the service provider — that tells you more than any README will.

Minimal steps:

composer require devua/salesteer-laravel
php artisan vendor:publish --provider="Devua\Salesteer\SalesteerServiceProvider"

Then set credentials in .env (names will match the published config/salesteer.php — check it, don't guess):

SALESTEER_API_KEY=
SALESTEER_API_URL=https://api.salesteer.com

Verify the binding resolves before building anything on top of it:

// routes/web.php or tinker
dd(app(\Devua\Salesteer\SalesteerClient::class));

If the container can't resolve it, the provider isn't auto-discovered (check composer.json → extra.laravel.providers) or the config keys differ from your env names.

First use case: pick the lowest-risk read operation the package exposes — listing clients or fetching a single invoice — and wire it into a Tinker session or an artisan command. Do not start with write operations against a production Salesteer account. Get one successful authenticated GET back, then expand.

Where to look, in order:

  1. config/salesteer.php — the actual env keys and defaults.
  2. The service provider — what's bound, whether it's a singleton, whether a facade alias exists.
  3. src/ resource/endpoint classes — the real method signatures. The README on a 0-star package is frequently stale relative to the code.

Implementation Patterns

Wrap it, don't scatter it. The single highest-leverage move with a low-traffic package is to put your own thin layer between your app and the package. When (not if) you need to patch or fork it, you change one file.

namespace App\Services;

use Devua\Salesteer\SalesteerClient;

class Salesteer
{
    public function __construct(private SalesteerClient $client) {}

    public function createInvoice(array $payload): array
    {
        return retry(3, fn () => $this->client->invoices()->create($payload), 200);
    }
}

Queue every write. Network calls to a third-party CRM should never sit in a request cycle. Dispatch a job, let the user continue:

SyncLeadToSalesteer::dispatch($lead)->onQueue('integrations');

Give the job $tries = 5, a backoff() of [10, 60, 300, 900, 3600], and a failed() handler that flags the local record. This matters more than usual here because you have no vendor SLA to fall back on.

Mirror IDs, not full records. Store salesteer_id and a synced_at timestamp on your local models rather than duplicating the remote payload. Two-way sync is a trap; pick Salesteer as the source of truth for invoices and your app as the source of truth for whatever originates there.

// migration
$table->string('salesteer_id')->nullable()->index();
$table->timestamp('salesteer_synced_at')->nullable();

Hook it to model events for one-directional flows.

static::created(fn (Lead $lead) => SyncLeadToSalesteer::dispatch($lead));

Guard against loops: only dispatch when salesteer_id is null, and have the job set it on success.

Webhooks, if the package supports them. Check for a route/controller in src/. If one exists, verify the signature before trusting the payload, and make the handler idempotent — webhook providers retry aggressively. If the package has no webhook support, register your own route and skip the package for inbound traffic entirely.

Testing. Since there's no test suite you can rely on, fake at the HTTP layer:

Http::fake([
    'api.salesteer.com/*' => Http::response(['id' => 'inv_123'], 201),
]);

$this->post('/invoices', [...])->assertRedirect();
Http::assertSent(fn ($req) => str_contains($req->url(), 'invoices'));

This assumes the package uses Laravel's Http client. If it uses Guzzle directly, you'll need MockHandler on the client — verify by grepping for GuzzleHttp vs Illuminate\Support\Facades\Http in src/.

Gotchas and Tips

Config caching will bite you. If you add env keys that aren't read inside config/salesteer.php, they're null after php artisan config:cache. Always route credentials through the config file. This is the single most common "it works locally" failure in small Laravel packages.

Pin the version and commit the lockfile. With one maintainer and a 2026 release date, a ^1.0 constraint is a coin flip. Use an exact constraint ("devua/salesteer-laravel": "1.2.3") and review diffs before bumping. Budget for the possibility that you become the maintainer of your fork.

Audit before trusting. Specifically check: does it log the API key on error? Does it send credentials over plain HTTP if the base URL is misconfigured? Does it swallow exceptions and return null? Any of those three justifies forking immediately — it's a small package and the fix is usually a few lines.

Error handling is probably thin. Expect raw Guzzle exceptions or a generic package exception. Wrap calls yourself and translate to domain exceptions so your jobs fail with something meaningful:

try {
    $this->salesteer->createInvoice($payload);
} catch (\Throwable $e) {
    report($e); // surfaces the real HTTP body in your logs
    throw new InvoiceSyncFailed($payload, previous: $e);
}

Rate limits and idempotency. If Salesteer's API accepts an idempotency key header, use it — your retry logic will otherwise create duplicate invoices the first time a request times out after the server processed it. If there's no idempotency support, search by a local reference ID before retrying a create.

Extension points if the package is too thin. If the client doesn't expose the endpoint you need, you generally have two options: reach into the underlying HTTP client and call it directly, or register your own authenticated PendingRequest using the same credentials from config. The second is cleaner and doesn't depend on the package's internals:

Http::withToken(config('salesteer.api_key'))
    ->baseUrl(config('salesteer.api_url'))
    ->post('invoices', $payload);

Don't build your product on this abstraction. Expose your own interfaces (InvoiceGateway) and bind the package behind them. That way an abandoned upstream costs you a single adapter rewrite rather than a codebase-wide search-and-replace. With a package at this score and star count, treat that adapter boundary as non-negotiable, not a nice-to-have.

Document the version you audited in your team's internal notes — the commit SHA, the endpoints you actually use, and the date. When the package goes quiet, that note is your migration brief.

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