itmosoft / laravel-abandoned-carts
Abandoned cart capture, expiry detection, recovery links, and conversion tracking for Laravel applications.
Package info
github.com/ITMOSOFT/laravel-abandoned-carts
pkg:composer/itmosoft/laravel-abandoned-carts
Requires
- php: ^8.3
- illuminate/database: ^12.0 || ^13.0
- illuminate/support: ^12.0 || ^13.0
Requires (Dev)
- orchestra/testbench: ^10.0 || ^11.0
- phpunit/phpunit: ^11.0 || ^12.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Abandoned cart capture, inactivity detection, recovery tracking and conversion recording for Laravel 12 and 13.
Features
- Store cart snapshots using a stable host-application
cart_key. - Optional polymorphic customer association.
- Configurable inactivity threshold.
- Mark stale carts as abandoned.
- Unique recovery tokens and token lookup.
- Record recovery and order conversion.
- Query abandoned carts by date range.
- Configurable table prefix.
Requirements
- PHP 8.3+
- Laravel 12 or 13
Installation
composer require itmosoft/laravel-abandoned-carts php artisan vendor:publish --tag=itmosoft-abandoned-carts-config php artisan migrate
The migration creates itmosoft_abandoned_carts by default. Configure table_prefix before running migrations if you need a different table name.
Capture or refresh a cart
use ItmoSoft\AbandonedCarts\AbandonedCartManager; $manager = app(AbandonedCartManager::class); $record = $manager->capture( cartKey: (string) $cart->id, items: [ ['product_id' => 10, 'sku' => 'SKU-10', 'name' => 'Example', 'quantity' => 2, 'unit_price' => '25.00'], ], subtotal: '50.00', customer: auth()->user(), currency: 'USD', metadata: ['source' => 'web'], );
Pass a stable unique key for the cart from your application. The items argument should be a serializable snapshot. The package does not depend on or modify a particular cart package.
Detect abandoned carts
Schedule the manager method in your application's scheduler. For example, call it hourly:
use Illuminate\Support\Facades\Schedule; use ItmoSoft\AbandonedCarts\AbandonedCartManager; Schedule::call(fn () => app(AbandonedCartManager::class)->markAbandoned()) ->hourly();
The default threshold is 60 minutes since last_activity_at. Configure abandon_after_minutes in config/itmosoft-abandoned-carts.php.
Recovery and conversion
$record = $manager->findByRecoveryToken($token); if ($record) { $manager->markRecovered($record); } // After the order is successfully created: $manager->markConverted($record, (string) $order->getKey());
Use recovery_token to build a signed or otherwise protected recovery URL in your application. Do not expose cart snapshots or customer information solely because a token is present without applying your application's authorization and expiry rules.
Query abandoned carts
$abandoned = $manager->abandoned( now()->subDays(7), now(), )->latest('abandoned_at')->get();
Important scope and production notes
- This package stores snapshots; it does not integrate automatically with
itmosoft/laravel-cartor any other cart implementation. - The host application must call
capture()whenever cart contents or activity change, and must schedulemarkAbandoned(). - It does not send email/SMS/push reminders, generate signed URLs, restore cart contents, or create orders.
- The package does not implement campaign frequency caps, consent management, GDPR/data-retention workflows, multi-tenant scopes, or per-customer deduplication. Add these policies in the host application.
cart_keyis unique and must be stable. Use a suitable opaque identifier; do not use raw session secrets.- The subtotal is stored as a decimal string/decimal database value; the package does not calculate tax, shipping, discounts, or currency conversion.
- Recovery tokens are random, stored as plaintext and intended as opaque lookup tokens. Protect recovery endpoints with HTTPS, rate limiting, expiry/authorization checks and consider hashing tokens at rest for higher-security deployments.
- Concurrent capture calls for the same new
cart_keycan race at the unique constraint. Handle duplicate-key retries in the host app if concurrent writes are possible. markAbandoned()uses a bulk update; it does not dispatch per-record Eloquent model events.
Testing
composer install
composer validate --strict
composer test
License
MIT. See LICENSE.