false, 'error' => 'Head of household verification required.']); exit; } } /** * @param mixed $raw * @return array> */ function normalizePeopleList($raw): array { if (!is_array($raw) || !array_is_list($raw)) { return []; } $out = []; foreach ($raw as $row) { if (is_array($row) && !empty($row['id']) && is_string($row['id'])) { $out[] = $row; } } return $out; } /** * @param array $person * @return array */ function migrateLegacyPersonRow(array $person): array { $currencyBalance = is_numeric($person['currency_balance'] ?? null) ? (float) $person['currency_balance'] : 0.0; $checkingBalance = array_key_exists('checking_balance', $person) && is_numeric($person['checking_balance']) ? (float) $person['checking_balance'] : $currencyBalance; $person['checking_balance'] = round($checkingBalance, 2); if (!array_key_exists('currency_balance', $person) || !is_numeric($person['currency_balance'])) { $person['currency_balance'] = $person['checking_balance']; } else { $person['currency_balance'] = round((float) $person['currency_balance'], 2); } if (!array_key_exists('savings_balance', $person) || !is_numeric($person['savings_balance'])) { $person['savings_balance'] = 0.0; } else { $person['savings_balance'] = round((float) $person['savings_balance'], 2); } if (!array_key_exists('charity_pending_balance', $person) || !is_numeric($person['charity_pending_balance'])) { $person['charity_pending_balance'] = 0.0; } else { $person['charity_pending_balance'] = round((float) $person['charity_pending_balance'], 2); } if (!array_key_exists('charity_donated_total', $person) || !is_numeric($person['charity_donated_total'])) { $person['charity_donated_total'] = 0.0; } else { $person['charity_donated_total'] = round((float) $person['charity_donated_total'], 2); } if (!array_key_exists('donation_goal_monthly', $person) || !is_numeric($person['donation_goal_monthly'])) { $person['donation_goal_monthly'] = 0.0; } else { $person['donation_goal_monthly'] = max(0, round((float) $person['donation_goal_monthly'], 2)); } if (!array_key_exists('banking_interest_last_applied_at', $person) || !is_string($person['banking_interest_last_applied_at'])) { $person['banking_interest_last_applied_at'] = ''; } if (!array_key_exists('nfc_submit_token_hash', $person) || !is_string($person['nfc_submit_token_hash'])) { $person['nfc_submit_token_hash'] = ''; } if (!array_key_exists('nfc_submit_token_updated_at', $person) || !is_string($person['nfc_submit_token_updated_at'])) { $person['nfc_submit_token_updated_at'] = ''; } return $person; } /** * @param array> $people * @return array> */ function migrateAllPeople(array $people): array { $out = []; foreach ($people as $person) { if (!is_array($person)) { continue; } $out[] = migrateLegacyPersonRow($person); } return $out; } /** * @param array> $people * @return array{index:int, person:array}|null */ function findPersonBySubmitToken(array $people, string $token): ?array { $token = trim($token); if ($token === '') { return null; } $candidateHash = hash('sha256', $token); foreach ($people as $index => $person) { $storedHash = trim((string) ($person['nfc_submit_token_hash'] ?? '')); if ($storedHash === '') { continue; } if (hash_equals($storedHash, $candidateHash)) { return ['index' => (int) $index, 'person' => $person]; } } return null; }