Abandoned Cart Detector
Finds carts inactive beyond a threshold and classifies recovery eligibility.
Function signature
ogDetectAbandonedCarts(carts = array(), options = array())
Categories
- Ecommerce Workflows
Parameters
cartsCart rows reviewed for abandoned-cart recovery eligibility.optionsOptional documented policy controls for this helper.Return value
Public-safe status string returned by the function for explicit controller branching or logging.
- success
- message
- data
Compatibility
Existing function name, slug, path, and call order preserved; advertised metadata corrected to the actual source behavior.
Minimum PHP version: 7.4
Security notes
Use caller-owned allowlists and context-specific escaping; validate admin actions, export fields, privacy plans, cache keys, templates, settings, routes, and ecommerce policies before production use.
Code
<?php
/*
* Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
* Free for personal and internal use. Paid project use requires visible credit
* to Jeffery L. Paris. Corporate use requires a paid license fee unless a
* separate written license states otherwise.
*/
/**
* Finds carts inactive beyond a threshold and classifies recovery eligibility.
*
* The function respects consent and cooldown metadata supplied by the caller.
* It does not send recovery emails by itself.
*
* @param array $carts Cart records with last_activity_at, customer_email, and consent fields.
* @param array $options Threshold and cooldown settings.
* @return array Abandoned cart candidates and skipped carts.
*/
function ogDetectAbandonedCarts($carts = array(), $options = array()) {
$result = array(
'success' => false,
'message' => '',
'data' => array()
);
if (!is_array($carts)) {
$result['message'] = 'Carts must be an array.';
return $result;
}
if (!is_array($options)) {
$options = array();
}
$inactive_seconds = 3600;
if (!empty($options['inactive_seconds'])) {
$inactive_seconds = (int)$options['inactive_seconds'];
}
if ($inactive_seconds < 900) {
$inactive_seconds = 900;
}
$cooldown_seconds = 86400;
if (!empty($options['cooldown_seconds'])) {
$cooldown_seconds = (int)$options['cooldown_seconds'];
}
if ($cooldown_seconds < 3600) {
$cooldown_seconds = 3600;
}
$now = time();
$abandoned = array();
$skipped = array();
foreach ($carts as $cart) {
if (!is_array($cart)) {
continue;
}
$cart_id = '';
if (!empty($cart['cart_id'])) {
$cart_id = (string)$cart['cart_id'];
}
$last_activity_at = 0;
if (!empty($cart['last_activity_at'])) {
if (is_numeric($cart['last_activity_at'])) {
$last_activity_at = (int)$cart['last_activity_at'];
} else {
$last_activity_at = strtotime((string)$cart['last_activity_at']);
}
}
$email = '';
if (!empty($cart['customer_email'])) {
$email = trim((string)$cart['customer_email']);
}
$has_consent = false;
if (!empty($cart['marketing_consent'])) {
$has_consent = true;
}
$last_recovery_sent_at = 0;
if (!empty($cart['last_recovery_sent_at'])) {
if (is_numeric($cart['last_recovery_sent_at'])) {
$last_recovery_sent_at = (int)$cart['last_recovery_sent_at'];
} else {
$last_recovery_sent_at = strtotime((string)$cart['last_recovery_sent_at']);
}
}
$reason = '';
if ($last_activity_at <= 0) {
$reason = 'Missing last activity time.';
} elseif (($now - $last_activity_at) < $inactive_seconds) {
$reason = 'Cart is not old enough.';
} elseif (empty($email) || filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
$reason = 'Missing valid customer email.';
} elseif (!$has_consent) {
$reason = 'Customer has not consented to recovery email.';
} elseif ($last_recovery_sent_at > 0 && ($now - $last_recovery_sent_at) < $cooldown_seconds) {
$reason = 'Recovery cooldown is still active.';
}
if (empty($reason)) {
$abandoned[] = array(
'cart_id' => $cart_id,
'customer_email' => $email,
'last_activity_at' => $last_activity_at,
'inactive_seconds' => $now - $last_activity_at
);
} else {
$skipped[] = array(
'cart_id' => $cart_id,
'reason' => $reason
);
}
}
$result['success'] = true;
$result['message'] = 'Abandoned cart scan completed.';
$result['data'] = array(
'abandoned_carts' => $abandoned,
'skipped_carts' => $skipped,
'inactive_seconds' => $inactive_seconds,
'cooldown_seconds' => $cooldown_seconds
);
return $result;
}