![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/soundstudiopro.com/private_html/ |
<?php
/**
* Automated Missing Purchase Fixer
*
* This script automatically detects and fixes missing track purchases.
* It should be run via cron every 5-15 minutes.
*
* Usage:
* php auto_fix_missing_purchases.php [--dry-run] [--verify-stripe]
*
* Options:
* --dry-run : Show what would be fixed without actually fixing
* --verify-stripe : Verify payment status with Stripe before fixing
*
* Created: December 2025
*/
require_once __DIR__ . '/config/database.php';
// Configuration
$STRIPE_SECRET_KEY = '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';
$LOG_FILE = __DIR__ . '/logs/auto_fix_purchases.log';
$MINUTES_TO_WAIT = 5; // Wait 5 minutes after payment intent creation before flagging as missing
// Parse command line arguments
$dry_run = in_array('--dry-run', $argv);
$verify_stripe = in_array('--verify-stripe', $argv);
$pdo = getDBConnection();
// Log function
function logMessage($message, $data = []) {
global $LOG_FILE;
$entry = [
'timestamp' => date('Y-m-d H:i:s'),
'message' => $message,
'data' => $data
];
file_put_contents($LOG_FILE, json_encode($entry) . "\n", FILE_APPEND | LOCK_EX);
echo "[" . date('Y-m-d H:i:s') . "] $message\n";
}
logMessage("Auto-fix script started", ['dry_run' => $dry_run, 'verify_stripe' => $verify_stripe]);
// Parse the cart payment logs
$log_file = __DIR__ . '/logs/cart_payment_detailed.log';
$missing_purchases = [];
if (!file_exists($log_file)) {
logMessage("Log file not found", ['file' => $log_file]);
exit(1);
}
$lines = file($log_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$lines = array_reverse($lines); // Most recent first
$seen_intents = [];
$cutoff_time = time() - ($MINUTES_TO_WAIT * 60); // Only check payment intents older than 5 minutes
foreach ($lines as $line) {
$entry = json_decode($line, true);
if (!$entry) continue;
// Only look at "after_create_payment_intent" entries
if (($entry['action'] ?? '') !== 'after_create_payment_intent') continue;
$payment_intent_id = $entry['payment_intent_id'] ?? null;
if (!$payment_intent_id || isset($seen_intents[$payment_intent_id])) continue;
// Check if payment intent is old enough (wait for webhook to process)
$entry_timestamp = strtotime($entry['timestamp'] ?? 'now');
if ($entry_timestamp > $cutoff_time) {
continue; // Too recent, skip
}
$seen_intents[$payment_intent_id] = true;
$user_id = $entry['user_id'] ?? null;
$cart_summary = $entry['cart_summary'] ?? [];
$timestamp = $entry['timestamp'] ?? 'Unknown';
// Extract track items
foreach ($cart_summary as $item) {
if (($item['type'] ?? '') === 'track') {
$track_id = $item['track_id'] ?? null;
if ($track_id) {
// Check if purchased
$stmt = $pdo->prepare("SELECT id FROM track_purchases WHERE user_id = ? AND track_id = ?");
$stmt->execute([$user_id, $track_id]);
$is_purchased = (bool)$stmt->fetch();
if (!$is_purchased) {
$missing_purchases[] = [
'payment_intent_id' => $payment_intent_id,
'user_id' => $user_id,
'track_id' => $track_id,
'title' => $item['title'] ?? 'Unknown',
'artist' => $item['artist'] ?? 'Unknown',
'timestamp' => $timestamp,
'amount' => ($item['amount'] ?? 0) / 100
];
}
}
}
}
}
logMessage("Missing purchases detected", ['count' => count($missing_purchases)]);
if (empty($missing_purchases)) {
logMessage("No missing purchases found. Exiting.");
exit(0);
}
// Process each missing purchase
$fixed_count = 0;
$skipped_count = 0;
$error_count = 0;
foreach ($missing_purchases as $mp) {
logMessage("Processing missing purchase", [
'payment_intent_id' => $mp['payment_intent_id'],
'user_id' => $mp['user_id'],
'track_id' => $mp['track_id']
]);
// Verify with Stripe if requested
if ($verify_stripe) {
$payment_status = verifyPaymentWithStripe($mp['payment_intent_id'], $STRIPE_SECRET_KEY);
if ($payment_status['status'] !== 'succeeded') {
logMessage("Payment not succeeded in Stripe, skipping", [
'payment_intent_id' => $mp['payment_intent_id'],
'status' => $payment_status['status']
]);
$skipped_count++;
continue;
}
}
// Fix the purchase
if ($dry_run) {
logMessage("DRY RUN: Would fix purchase", [
'payment_intent_id' => $mp['payment_intent_id'],
'user_id' => $mp['user_id'],
'track_id' => $mp['track_id']
]);
$fixed_count++;
} else {
$result = fixMissingPurchase($pdo, $mp['user_id'], $mp['track_id'], $mp['payment_intent_id']);
if ($result['success']) {
logMessage("Purchase fixed successfully", [
'payment_intent_id' => $mp['payment_intent_id'],
'user_id' => $mp['user_id'],
'track_id' => $mp['track_id'],
'purchase_id' => $result['purchase_id'] ?? null
]);
$fixed_count++;
} else {
logMessage("Failed to fix purchase", [
'payment_intent_id' => $mp['payment_intent_id'],
'user_id' => $mp['user_id'],
'track_id' => $mp['track_id'],
'error' => $result['message']
]);
$error_count++;
}
}
}
// Summary
logMessage("Auto-fix completed", [
'total_detected' => count($missing_purchases),
'fixed' => $fixed_count,
'skipped' => $skipped_count,
'errors' => $error_count,
'dry_run' => $dry_run
]);
echo "\n=== Summary ===\n";
echo "Total missing purchases detected: " . count($missing_purchases) . "\n";
echo "Fixed: $fixed_count\n";
echo "Skipped: $skipped_count\n";
echo "Errors: $error_count\n";
exit(0);
/**
* Verify payment status with Stripe
*/
function verifyPaymentWithStripe($payment_intent_id, $stripe_secret) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.stripe.com/v1/payment_intents/' . urlencode($payment_intent_id));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer ' . $stripe_secret]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http_code !== 200) {
return ['status' => 'error', 'message' => 'Failed to fetch payment intent'];
}
$data = json_decode($response, true);
return [
'status' => $data['status'] ?? 'unknown',
'amount' => $data['amount'] ?? 0,
'currency' => $data['currency'] ?? 'usd'
];
}
/**
* Fix a missing purchase (same logic as admin_purchase_tracker.php)
*/
function fixMissingPurchase($pdo, $user_id, $track_id, $payment_intent_id) {
try {
// Check if already purchased
$stmt = $pdo->prepare("SELECT id FROM track_purchases WHERE user_id = ? AND track_id = ?");
$stmt->execute([$user_id, $track_id]);
if ($stmt->fetch()) {
return ['success' => false, 'message' => 'This track is already purchased by this user.'];
}
// Get track info
$stmt = $pdo->prepare("SELECT id, title, price, user_id as artist_id FROM music_tracks WHERE id = ?");
$stmt->execute([$track_id]);
$track = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$track) {
return ['success' => false, 'message' => 'Track not found in database.'];
}
$pdo->beginTransaction();
// Determine revenue recipient
$artist_stmt = $pdo->prepare("SELECT plan FROM users WHERE id = ?");
$artist_stmt->execute([$track['artist_id']]);
$artist = $artist_stmt->fetch(PDO::FETCH_ASSOC);
$is_free_user_track = ($artist && strtolower($artist['plan']) === 'free');
$revenue_recipient = $is_free_user_track ? 'platform' : 'artist';
$recipient_id = $is_free_user_track ? 1 : $track['artist_id'];
// Record sale
$stmt = $pdo->prepare("
INSERT INTO sales (track_id, buyer_id, artist_id, amount, quantity, revenue_recipient, recipient_id, is_free_user_track, created_at)
VALUES (?, ?, ?, ?, 1, ?, ?, ?, NOW())
");
$stmt->execute([
$track_id,
$user_id,
$track['artist_id'],
$track['price'],
$revenue_recipient,
$recipient_id,
$is_free_user_track ? 1 : 0
]);
// Record purchase
$stmt = $pdo->prepare("
INSERT INTO track_purchases (user_id, track_id, price_paid, credits_used, payment_method, stripe_payment_intent_id, purchase_date)
VALUES (?, ?, ?, 0, 'stripe', ?, NOW())
");
$stmt->execute([$user_id, $track_id, $track['price'], $payment_intent_id]);
$purchase_id = $pdo->lastInsertId();
// Add to library
$stmt = $pdo->prepare("INSERT IGNORE INTO user_library (user_id, track_id, purchase_date) VALUES (?, ?, NOW())");
$stmt->execute([$user_id, $track_id]);
$pdo->commit();
// Log the fix
$log_entry = [
'timestamp' => date('Y-m-d H:i:s'),
'action' => 'auto_fix_missing_purchase',
'user_id' => $user_id,
'track_id' => $track_id,
'track_title' => $track['title'],
'payment_intent_id' => $payment_intent_id,
'purchase_id' => $purchase_id
];
$log_file = __DIR__ . '/logs/manual_purchase_fixes.log';
file_put_contents($log_file, json_encode($log_entry) . "\n", FILE_APPEND | LOCK_EX);
return [
'success' => true,
'message' => "Purchase #{$purchase_id} created for \"{$track['title']}\"",
'purchase_id' => $purchase_id
];
} catch (Exception $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
return ['success' => false, 'message' => 'Error: ' . $e->getMessage()];
}
}