ahmokhan1 / meta-ads
Meta Conversions API and Marketing API insights integration for Laravel
Requires
- php: ^8.1
- ext-mbstring: *
- guzzlehttp/guzzle: ^7.2|^8.0
- illuminate/bus: ^10.0|^11.0|^12.0|^13.0
- illuminate/cache: ^10.0|^11.0|^12.0|^13.0
- illuminate/console: ^10.0|^11.0|^12.0|^13.0
- illuminate/database: ^10.0|^11.0|^12.0|^13.0
- illuminate/http: ^10.0|^11.0|^12.0|^13.0
- illuminate/log: ^10.0|^11.0|^12.0|^13.0
- illuminate/queue: ^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
Requires (Dev)
- ext-pdo_sqlite: *
- larastan/larastan: ^2.11|^3.10
- laravel/pint: ^1.13
- orchestra/testbench: ^8.0|^9.0|^10.0|^11.0
- phpunit/phpunit: ^10.5|^11.5|^12.0|^13.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Laravel package for Meta Conversions API uploads and campaign-level Marketing API insights.
Requirements
- PHP 8.1+
- PHP
mbstringextension - Laravel 10, 11, 12, or 13
- A cache driver that supports atomic locks when sync locking or unique conversion jobs are enabled
Laravel 12 or 13 is recommended for currently supported applications. Laravel 10 and 11 compatibility is retained for legacy applications, but does not extend those framework versions beyond Laravel's upstream support policy.
Installation
composer require ahmokhan1/meta-ads php artisan vendor:publish --tag=meta-ads-config php artisan migrate
The package automatically loads migrations for tables it owns:
meta_ads_accountsmeta_ads_campaignsmeta_ads_metrics_daily
You may publish those migrations when an application needs to customize them:
php artisan vendor:publish --tag=meta-ads-migrations
Application-owned lead table
The package never creates or automatically changes your application's leads table. If the configured lead table does not already contain the required attribution and delivery columns, publish the optional additive migration after that table exists:
php artisan vendor:publish --tag=meta-ads-lead-migration php artisan migrate
This optional migration has a deliberately non-destructive down() method because it cannot know which columns were already owned by the application.
Required conversion columns:
fbclid,fbc,fbpmeta_campaign_id,meta_ad_set_id,meta_ad_idmeta_conversion_sent_at,meta_conversion_error
For a new application only, a minimal model stub is available:
php artisan vendor:publish --tag=meta-ads-models
Do not publish that model over an existing application model.
Configuration
Set the API version explicitly. Meta Graph API versions expire, so the package does not silently select one for the application.
META_ADS_ACCESS_TOKEN= META_ADS_AD_ACCOUNT_ID=act_123456789 META_ADS_PIXEL_ID=123456789 META_ADS_API_VERSION=vXX.X META_ADS_CURRENCY_CODE=GBP META_ADS_EVENT_NAME=Purchase META_ADS_ACTION_SOURCE=website META_ADS_TEST_EVENT_CODE= META_ADS_ALLOW_LOCAL=false META_ADS_TIMEOUT=25 META_ADS_INSIGHTS_TIMEOUT=180 META_ADS_CONNECT_TIMEOUT=10 META_ADS_RETRIES=3 META_ADS_RETRY_SLEEP_MS=500 META_ADS_RANGE_CHUNK_DAYS=15 META_ADS_MAX_PAGINATION_PAGES=1000 META_ADS_SYNC_LOCK_SECONDS=900 META_ADS_CONVERSION_UNIQUE_FOR=3600
Model and table overrides:
META_ADS_LEAD_MODEL=App\Models\Lead META_ADS_LEADS_TABLE=leads
For accurate purchase value, currency, and time, map fields from the configured lead/order model:
META_ADS_CONVERSION_VALUE_FIELD=amount META_ADS_CONVERSION_CURRENCY_FIELD=currency META_ADS_CONVERSION_TIME_FIELD=converted_at META_ADS_ORDER_ID_FIELD=order_number META_ADS_DEFAULT_COUNTRY_CODE=44
Optional browser context fields improve event matching:
META_ADS_EVENT_SOURCE_URL_FIELD=source_url META_ADS_CLIENT_IP_FIELD=client_ip META_ADS_CLIENT_USER_AGENT_FIELD=client_user_agent
If no value field is configured, META_ADS_CONVERSION_VALUE is used as a fallback. Do not report a lead creation as Purchase; either dispatch only after a confirmed purchase or configure META_ADS_EVENT_NAME=Lead.
Conversions API
Dispatch after the configured conversion has genuinely occurred:
use Ahmokhan1\MetaAds\Jobs\SendMetaAdsConversionJob; SendMetaAdsConversionJob::dispatch($lead->id)->delay(now()->addSeconds(5));
The job:
- is unique per lead while queued or processing;
- uses the stable event ID
lead-{id}for Meta deduplication; - retries transient network, 408, 429, and 5xx failures; queue retries use increasing backoff;
- hashes normalized customer identifiers;
- sets
meta_conversion_sent_atonly after Meta confirmsevents_received >= 1.
For local Meta Test Events:
META_ADS_ALLOW_LOCAL=true META_ADS_TEST_EVENT_CODE=TEST123
Insights sync
php artisan meta-ads:sync --days=30 php artisan meta-ads:sync --from=2026-08-01 --to=2026-08-31
Scheduler example:
$schedule->command('meta-ads:sync --days=30') ->dailyAt('01:20') ->withoutOverlapping() ->onOneServer();
The service also acquires an account-specific atomic cache lock. Set META_ADS_SYNC_LOCK_SECONDS=0 only when the application provides equivalent locking.
When Meta returns overlapping action aliases such as purchase and omni_purchase, the client uses configured priority rather than summing aliases and inflating results. Publish the config to customize lead_action_types and purchase_action_types.
Testing
composer install
composer test
composer analyse
composer format:test
composer quality runs formatting, level-8 Larastan analysis, and all tests. CI verifies the minimum PHP platform and current patched releases across Laravel 10 through 13, audits dependencies, and never calls Meta from the test suite.
Package development and tests require the PDO SQLite extension.
See CONTRIBUTING.md for the development workflow and SECURITY.md for vulnerability reporting.
License
MIT