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

Ecologi Laravel Package

owenvoke/ecologi

A PHP client for the Ecologi API that makes it easy to plant trees, fund carbon offset projects, and retrieve impact data. Integrate climate action into Laravel or standalone PHP applications with a simple, expressive interface.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and configure.

composer require owenvoke/ecologi

Laravel auto-discovery registers the service provider and facade. Publish the config and set the credential:

php artisan vendor:publish --provider="OwenVoke\Ecologi\EcologiServiceProvider"
ECOLOGI_API_KEY=your-bearer-token

For non-Laravel PHP, skip the facade and instantiate the underlying client directly with the key. There is nothing else to wire up — no migrations, no queue configuration, no events to register.

Where to look first. Read the package source before you write a line of application code. It is small (a few hundred lines: a connector, a handful of request classes, a response DTO or two). One hour of reading gives you a complete and accurate API surface, which is cheaper and more reliable than inferring it from a README that may lag the release you installed. Confirm: the exact method names, whether write methods return a hydrated response or a raw array, how errors surface (exception type vs. result object), and the PHP/Illuminate constraints in composer.json against your runtime.

Your first use case should be read-only. Authenticate against a live key and pull impact totals — account totals and community totals. This validates credentials, confirms the package runs on your stack, and spends nothing. Only after that works should you attempt a write.

use OwenVoke\Ecologi\Facades\Ecologi;

$impact = Ecologi::getImpact(); // confirm exact signature against the source

Then, and only then, wire a single tree purchase to a sandbox account and reconcile the response against the Ecologi dashboard. Ship a read path first: impact figures on a profile, an offset badge on a receipt, or a number in an internal admin screen. All three are free, reversible, and independently shippable.

Implementation Patterns

Wrap it behind your own interface immediately. Do not let call sites touch Ecologi:: directly. Define a ClimateImpactService contract and make this package its first adapter. This is the single highest-leverage decision in the whole integration: it caps your exposure to a single-maintainer dependency at one file, and it gives you an obvious seam to fake in tests.

interface ClimateImpactService
{
    public function totals(): ImpactTotals;          // read
    public function plantTrees(int $count, ?string $ref): TreePurchase;  // write
}

Separate reads from writes and ship them as different deliverables. Reads carry no financial risk; writes spend real money on every successful call. Treat the write path as payments-adjacent: it needs authorization, a ledger, spend caps, and reconciliation before it goes near production. Reads need none of that.

Attach writes to events, off the request path. The natural pattern is a listener on OrderPlaced that dispatches a queued PlantTreesForOrder job. Never call the vendor inline in a checkout controller — latency, outages, and rate limits all become checkout failures.

class PlantTreesForOrder implements ShouldQueue
{
    public $tries = 1; // see Gotchas: retries can double-spend

    public function handle(ClimateImpactService $climate): void
    {
        $climate->plantTrees(1, "order:{$this->order->id}");
    }
}

Keep an idempotency ledger in your own database. The package almost certainly does not provide idempotency keys, and the upstream API may not either. Create a table keyed on your domain reference (order_id, subscription_period) with columns for status, requested amount, vendor response, and a claimed_at timestamp. Claim the row before the call; write the result after. A unique constraint on the reference is what actually prevents double-spend — a queue retry or a redeploy mid-job will otherwise plant twice.

Persist the impact records you generate. Don't let your own reporting depend on the vendor's dashboard. Every successful purchase should leave a row you own: timestamp, quantity, unit, vendor reference, cost. When you need a quarterly ESG figure or a customer's lifetime tree count, read your table. This also gives you an exit path if you ever change climate-action vendors.

Run a reconciliation job. Nightly, compare your ledger totals against the vendor's account totals. A divergence means a write succeeded that you didn't record (or vice versa). This is your only reliable detector for the failure modes the ledger alone can't catch.

Fail soft, everywhere. Wrap every vendor call — reads and writes — so that an exception is logged and swallowed, never propagated into a user-facing flow. An Ecologi outage must never break checkout, a receipt, or a profile page.

try {
    return $this->climate->totals();
} catch (Throwable $e) {
    Log::warning('ecologi.unavailable', ['exception' => $e]);
    return ImpactTotals::empty();
}

Config for spend. Put a hard cap in config (services.ecologi.max_trees_per_day) and check it before dispatching any write. Set it to 0 in local and staging so a developer's .env copy can never spend real money. This is a two-line guard that prevents the most expensive class of accident here.

Test with the seam, not the vendor. Bind a fake ClimateImpactService in tests and assert on interactions. For adapter-level tests, mock the HTTP layer. Avoid hitting the live API from CI — it costs money and is non-deterministic.

Gotchas and Tips

Units are the classic 1000x bug. Trees and carbon offsets are different products with different quantities — Ecologi sells carbon offsets in kilograms or tonnes depending on the endpoint. Getting the multiplier wrong means you either massively over-spend or make a meaningless purchase. Name your variables with the unit baked in ($kilogramsCo2e, $treeCount) and assert on the range before calling.

Retries double-spend. This package is a thin HTTP wrapper; it has no notion of a financial transaction. If you retry a failed write, you may plant twice — a timeout on your end does not mean the vendor didn't process it. Set $tries = 1 on purchase jobs and rely on the ledger plus an explicit, human-reviewed reconciliation to handle failures. Never use ->retry() blindly on a write.

The API key is a bearer token that spends money. Store it in a secret manager, not a committed .env. Give each environment its own key with its own spend limit. Write a rotation runbook and name an owner — this is not the same category of secret as a read-only third-party token.

Single maintainer, zero dependents. Plan to own this code. Pin the exact version in composer.json, and consider forking it into your org with CI from day one. The version constraint in composer.json is unpinned and will eventually collide with a framework upgrade; discover that now, not mid-migration. Forking removes the constraint entirely.

Verify endpoint coverage before designing around it. A package this small tracks the common purchase and read paths. If your roadmap depends on carbon-footprint measurement, reporting exports, or webhooks, check the source against Ecologi's current API documentation before committing — and check that the API version the package targets is still current.

Read the response objects, don't assume. Fully typed responses are the point of the wrapper, but property names may not match the vendor's JSON exactly. Dump one real response early and code against it. This is a five-minute step that saves an afternoon of null debugging.

Don't publish vendor numbers casually. Anything that reaches a customer, a public page, or an investor deck carries disclosure risk. Vendor-reported tonnes CO₂e are not the same as audited figures. Keep the caveats attached to the numbers wherever they appear, and treat public publication as a separate decision from building the integration.

Keep the seam when the facade is convenient. The Laravel facade makes Ecologi::plantTrees() look harmless inside a controller. Inject ClimateImpactService instead, even when the facade would work — the facade is the thing that makes swapping vendors hard 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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource