padosoft/laravel-affiliate-network
A Laravel package for building affiliate and referral programs out of the box. Track referrals, attribute conversions, calculate commissions, and manage payouts with Eloquent models and configurable, multi-tier-ready workflows. Integrates cleanly into existing apps.
This package wraps several affiliate networks (Commission Junction, Zanox, Effiliation, Publicideas) behind a common NetworkManager so you can pull product feeds and build affiliate links without writing a separate client per network. Version 2.0.1 is a maintenance release — no installation changes — so treat the setup below as the baseline.
Minimal steps:
composer require <vendor>/<package>
php artisan vendor:publish --tag=<package>-config
config/<package>.php (or .env if the package maps them). Each network needs its own keys — Commission Junction uses a publisher PID/website ID, Zanox uses a connect ID plus secret key, Effiliation uses an API key.use <Vendor>\<Package>\NetworkManager;
$manager = app(NetworkManager::class);
$zanox = $manager->driver('zanox');
$products = $manager->driver('zanox')->getProducts(['q' => 'laptop']);
Note getProducts() on Zanox is new in 2.0.1 — if you're coming from 2.0.0 the method simply doesn't exist, so any code path relying on it must be gated behind a version constraint of ^2.0.1.Where to look first: the NetworkManager class (driver resolution, shared config, default network), then the individual adapter you actually need. Each adapter exposes a different surface — do not assume parity across networks. The Publicideas and EffiliationEx adapters are the thinnest and are the most likely to lag behind their real APIs.
Manager-as-facade, adapter-per-network. The constructor change in 2.0.1 (Commission Junction now accepts the arguments NetworkManager passes) confirms the intended pattern: always go through the manager, never new CommissionJunction(...) in your own code. Direct instantiation is what broke in the previous version and it will break again.
Wrap network calls in your own repository/service. Because each network returns differently shaped payloads, normalise early:
class ProductFeed
{
public function __construct(private NetworkManager $manager) {}
public function forNetwork(string $network, array $filters = []): Collection
{
return Cache::remember(
"feed:{$network}:" . md5(serialize($filters)),
now()->addHours(6),
fn () => collect($this->manager->driver($network)->getProducts($filters))
->map(fn ($p) => ProductData::fromNetwork($network, $p))
);
}
}
This isolates your app from the fact that getProducts() exists on Zanox but may be named differently elsewhere.
Queue the pulls. Feeds are slow and networks throttle. Push each network into its own job so one failing network doesn't sink the whole refresh:
foreach (config('affiliate.networks') as $network) {
RefreshNetworkFeed::dispatch($network);
}
Multi-network fallback for links. If you generate outbound links, keep the network name on the record and resolve the driver at render time. This lets you move a product from Zanox to CJ without a data migration.
Testing. Bind a fake into the container instead of hitting live APIs — $this->mock(NetworkManager::class) and have driver() return a stub. Given the constructor signature changed in this release, fakes are also your regression net for future adapter swaps.
Integration tip: if you expose affiliate data via an API, cache aggressively at the boundary. The package adds no caching layer of its own.
CommissionJunction directly. If you have new CommissionJunction($pid, $websiteId) scattered in your codebase, it now expects the NetworkManager-compatible signature. Grep for direct instantiation and migrate to $manager->driver('commissionjunction').var_dump() was removed from the Publicideas adapter. If you were relying on that debug output — deliberately or not — you'll lose it. Add your own logging around the adapter call if you need visibility into its responses.json_encode paths assumed a different encoding, re-verify product names and descriptions after upgrading. Existing rows pulled before the upgrade may still hold mis-encoded data; re-run the feed import rather than assuming the fix is retroactive.getProducts() on Zanox is additive, not universal. It's tempting to write $manager->driver($n)->getProducts() across all networks. Check each adapter first — calling a missing method is a fatal error, not a graceful null."^2.0.1" is safer than "^2.0", and definitely avoid "*".config/, run php artisan config:clear in local dev. Most "invalid credentials" reports on packages like this are stale config caches.NetworkManager constructor injection introduced in 2.0.1 is the hook that makes third-party adapters viable — accept the manager in your constructor and you'll stay compatible.How can I help you explore Laravel packages today?