run:R W Run
7.55 KB
2026-04-08 19:24:59
R W Run
1.56 KB
2026-04-08 19:24:57
R W Run
1.34 KB
2026-04-08 19:24:56
R W Run
787 By
2026-04-08 19:24:58
R W Run
29.66 KB
2026-04-08 19:24:57
R W Run
6.35 KB
2026-04-08 19:24:57
R W Run
851 By
2026-04-08 19:24:58
R W Run
4.56 KB
2026-04-08 19:24:57
R W Run
11.08 KB
2026-04-08 19:24:56
R W Run
2.83 KB
2026-04-08 19:24:59
R W Run
40 By
2026-04-08 19:24:56
R W Run
39.18 KB
2026-04-08 19:24:57
R W Run
69.96 KB
2026-04-08 19:24:57
R W Run
310 By
2026-04-08 19:24:57
R W Run
12.03 KB
2026-04-08 19:24:57
R W Run
error_log
📄trial_expiry_cron.php
1<?php
2/**
3 * Trial Expiry Reminder — Standalone Cron
4 * The Service 4 U
5 *
6 * Place in: /home/theservice4u/public_html/clients/crons/
7 * Crontab: 0 * * * * php /home/theservice4u/public_html/clients/crons/trial_expiry_cron.php
8 */
9
10define('ROOTDIR', '/home/theservice4u/public_html/clients');
11define('ADMINDIR', 'admin');
12
13chdir(ROOTDIR);
14
15require_once ROOTDIR . '/init.php';
16require_once ROOTDIR . '/includes/functions.php';
17
18use WHMCS\Database\Capsule;
19
20logActivity('Trial Reminder — cron started.');
21
22// ── CONFIG ───────────────────────────────────────────────────
23$trialPid = 11;
24$trialHours = 48;
25$windowMinHours = 5;
26$windowMaxHours = 6;
27// ────────────────────────────────────────────────────────────
28
29// Ensure tracking table exists
30try {
31 if (!Capsule::schema()->hasTable('mod_trial_reminder_sent')) {
32 Capsule::schema()->create('mod_trial_reminder_sent', function($table) {
33 $table->increments('id');
34 $table->integer('service_id')->unsigned()->unique();
35 $table->integer('client_id')->unsigned();
36 $table->timestamp('sent_at');
37 });
38 logActivity('Trial Reminder — created tracking table.');
39 }
40} catch (\Exception $e) {
41 logActivity('Trial Reminder — could not create tracking table: ' . $e->getMessage());
42 exit(1);
43}
44
45// Calculate window
46$now = new \DateTime('now', new \DateTimeZone('UTC'));
47$windowMin = clone $now; $windowMin->modify('+' . $windowMinHours . ' hours');
48$windowMax = clone $now; $windowMax->modify('+' . $windowMaxHours . ' hours');
49
50// Find eligible trials
51try {
52 $trials = Capsule::table('tblhosting as h')
53 ->join('tblorders as o', 'o.id', '=', 'h.orderid')
54 ->select('h.id as service_id', 'h.userid as client_id', 'o.date as order_date')
55 ->where('h.packageid', $trialPid)
56 ->whereIn('h.domainstatus', ['Active', 'active'])
57 ->whereNotExists(function($query) {
58 $query->select(Capsule::raw(1))
59 ->from('mod_trial_reminder_sent')
60 ->whereRaw('mod_trial_reminder_sent.service_id = h.id');
61 })
62 ->whereBetween(
63 Capsule::raw('DATE_ADD(o.date, INTERVAL ' . $trialHours . ' HOUR)'),
64 [
65 $windowMin->format('Y-m-d H:i:s'),
66 $windowMax->format('Y-m-d H:i:s'),
67 ]
68 )
69 ->get();
70} catch (\Exception $e) {
71 logActivity('Trial Reminder — query failed: ' . $e->getMessage());
72 exit(1);
73}
74
75if (empty($trials) || count($trials) === 0) {
76 logActivity('Trial Reminder — no eligible trials found. Window: ' . $windowMin->format('Y-m-d H:i:s') . ' to ' . $windowMax->format('Y-m-d H:i:s'));
77 exit(0);
78}
79
80foreach ($trials as $trial) {
81
82 // Get client details
83 $client = Capsule::table('tblclients')->where('id', $trial->client_id)->first();
84 if (!$client || empty($client->email)) continue;
85
86 // Calculate time remaining
87 $orderDate = new \DateTime($trial->order_date, new \DateTimeZone('UTC'));
88 $expiresAt = clone $orderDate;
89 $expiresAt->modify('+' . $trialHours . ' hours');
90 $diff = $now->diff($expiresAt);
91 $hoursLeft = max(1, ($diff->h + ($diff->days * 24)));
92
93 if ($hoursLeft >= 2) {
94 $timeLeft = "less than ' . $hoursLeft . ' hours";
95 } elseif ($hoursLeft === 1) {
96 $timeLeft = "less than 1 hour";
97 } else {
98 $timeLeft = "less than {$diff->i} minutes";
99 }
100
101 $firstName = htmlspecialchars($client->firstname);
102
103 // Get WHMCS mail settings
104 $fromName = Capsule::table('tblconfiguration')->where('setting', 'SystemEmailsFromName')->value('value');
105 $fromEmail = Capsule::table('tblconfiguration')->where('setting', 'SystemEmailsFromEmail')->value('value');
106
107 // Build email HTML inline — no WHMCS template size limit
108 $subject = "⏳ Your free trial expires in {$timeLeft} — don't lose your access";
109 $body = '<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/></head> <body style="margin:0;padding:0;background:#f0f2f5;font-family:Arial,sans-serif;"> <table width="100%" cellpadding="0" cellspacing="0" border="0" style="background:#f0f2f5;padding:32px 0;"> <tr><td align="center"> <table width="600" cellpadding="0" cellspacing="0" border="0" style="max-width:600px;width:100%;background:#fff;border-radius:12px;overflow:hidden;"> <tr><td height="5" style="background:#2b44f5;font-size:0;">&nbsp;</td></tr> <tr><td align="center" style="background:#0d0d14;padding:24px 40px;"> <img src="https://theservice4u.com/The-Service-4-U_transparent.webp" alt="The Service 4 U" width="48" height="48" style="display:block;margin:0 auto 10px;"/> <div style="font-size:12px;font-weight:700;color:#818cf8;letter-spacing:2px;text-transform:uppercase;">THE SERVICE 4 U</div> </td></tr> <tr><td align="center" style="background:#fef3c7;padding:12px 40px;border-bottom:1px solid #fcd34d;"> <span style="font-size:15px;font-weight:700;color:#92400e;">⏳ &nbsp;Your free trial expires in <strong>' . $timeLeft . '</strong></span> </td></tr> <tr><td style="padding:32px 40px 24px;color:#4b5563;font-size:15px;line-height:1.7;"> <p style="margin:0 0 16px;font-size:17px;font-weight:700;color:#0d0d14;">Hey ' . $firstName . ',</p> <p style="margin:0 0 16px;">Your 48-hour free trial expires in <strong>' . $timeLeft . '</strong>. Once it\'s gone your access stops — but switching to a paid plan takes less than two minutes.</p> <p style="margin:0 0 16px;">Your username and password stay exactly the same. Just pick a plan, complete checkout, and refresh your app. That\'s it.</p> <p style="margin:0 0 24px;background:#fff8e1;border-left:4px solid #f59e0b;padding:12px 16px;border-radius:4px;font-size:14px;"><strong>🎬 Heads up:</strong> The free trial doesn\'t include our full VOD library or premium sports — those unlock the moment you upgrade. 30,000+ movies, series, and live sports are waiting.</p> <p style="margin:0 0 20px;background:#f0f7ff;border:1px solid #c7d2fe;border-radius:8px;padding:14px 18px;font-size:14px;text-align:center;">💰 <strong>Save $5 on your first plan</strong> — use code <strong style="color:#2b44f5;">TRIAL5</strong> at checkout</p> <p style="margin:0 0 16px;font-size:11px;font-weight:700;color:#6b7280;text-transform:uppercase;letter-spacing:1.5px;border-bottom:2px solid #e5e7eb;padding-bottom:10px;">Choose Your Plan — All Include 12,000+ Channels, 4K &amp; 4 Streams</p> <table width="100%" cellpadding="0" cellspacing="0" border="0" style="margin:10px 0;border:1px solid #e5e7eb;border-radius:8px;"><tr><td style="padding:14px 18px;"><div style="font-weight:700;color:#0d0d14;font-size:14px;margin-bottom:4px;">1 Month &mdash; $19.99</div><div style="font-size:12px;color:#6b7280;margin-bottom:10px;">Flexible, no commitment</div><a href="https://theservice4u.com/clients/cart.php?a=add&pid=1" style="display:inline-block;background:#2b44f5;color:#fff;font-size:12px;font-weight:700;padding:8px 16px;border-radius:50px;text-decoration:none;margin-right:8px;">Adult</a><a href="https://theservice4u.com/clients/cart.php?a=add&pid=6" style="display:inline-block;background:#f4f5ff;color:#2b44f5;border:1px solid #c7d2fe;font-size:12px;font-weight:700;padding:8px 16px;border-radius:50px;text-decoration:none;">Family Friendly</a></td></tr></table> <table width="100%" cellpadding="0" cellspacing="0" border="0" style="margin:10px 0;border:1px solid #e5e7eb;border-radius:8px;"><tr><td style="padding:14px 18px;"><div style="font-weight:700;color:#0d0d14;font-size:14px;margin-bottom:4px;">3 Months &mdash; $49.99</div><div style="font-size:12px;color:#6b7280;margin-bottom:10px;">~$16.66 / month</div><a href="https://theservice4u.com/clients/cart.php?a=add&pid=2" style="display:inline-block;background:#2b44f5;color:#fff;font-size:12px;font-weight:700;padding:8px 16px;border-radius:50px;text-decoration:none;margin-right:8px;">Adult</a><a href="https://theservice4u.com/clients/cart.php?a=add&pid=7" style="display:inline-block;background:#f4f5ff;color:#2b44f5;border:1px solid #c7d2fe;font-size:12px;font-weight:700;padding:8px 16px;border-radius:50px;text-decoration:none;">Family Friendly</a></td></tr></table> <table width="100%" cellpadding="0" cellspacing="0" border="0" style="margin:10px 0;border:1px solid #e5e7eb;border-radius:8px;"><tr><td style="padding:14px 18px;"><div style="font-weight:700;color:#0d0d14;font-size:14px;margin-bottom:4px;">6 Months &mdash; $79.99</div><div style="font-size:12px;color:#6b7280;margin-bottom:10px;">~$13.33 / month</div><a href="https://theservice4u.com/clients/cart.php?a=add&pid=4" style="display:inline-block;background:#2b44f5;color:#fff;font-size:12px;font-weight:700;padding:8px 16px;border-radius:50px;text-decoration:none;margin-right:8px;">Adult</a><a href="https://theservice4u.com/clients/cart.php?a=add&pid=8" style="display:inline-block;background:#f4f5ff;color:#2b44f5;border:1px solid #c7d2fe;font-size:12px;font-weight:700;padding:8px 16px;border-radius:50px;text-decoration:none;">Family Friendly</a></td></tr></table> <table width="100%" cellpadding="0" cellspacing="0" border="0" style="margin:10px 0;border:2px solid #2b44f5;border-radius:8px;"><tr><td style="background:#2b44f5;padding:6px 18px;"><span style="font-size:11px;font-weight:800;color:#fff;text-transform:uppercase;letter-spacing:1px;">Best Value &mdash; Save 42%</span></td></tr><tr><td style="padding:14px 18px;"><div style="font-weight:700;color:#0d0d14;font-size:14px;margin-bottom:4px;">12 Months &mdash; $139.99</div><div style="font-size:12px;color:#2b44f5;font-weight:600;margin-bottom:10px;">Just $11.67 / month</div><a href="https://theservice4u.com/clients/cart.php?a=add&pid=5" style="display:inline-block;background:#2b44f5;color:#fff;font-size:12px;font-weight:700;padding:8px 16px;border-radius:50px;text-decoration:none;margin-right:8px;">Adult</a><a href="https://theservice4u.com/clients/cart.php?a=add&pid=10" style="display:inline-block;background:#f4f5ff;color:#2b44f5;border:1px solid #c7d2fe;font-size:12px;font-weight:700;padding:8px 16px;border-radius:50px;text-decoration:none;">Family Friendly</a></td></tr></table> <p style="margin:20px 0 0;font-size:12pxlans include 12,000+ channels, HD &amp; 4K, 4 simultaneous connections, 24/7 support. No contracts.</p> </td></tr> <tr><td style="background:#f9fafb;border-top:1px solid #f0f0f0;padding:18px 40px;text-align:center;"> <p style="margin:0 0 4px;font-size:12px;color:#9ca3af;">&copy; 2026 The Service 4 U &nbsp;&middot;&nbsp; <a href="https://theservice4u.com" style="color:#9ca3af;text-decoration:none;">theservice4u.com</a></p> <p style="margin:0;font-size:11px;color:#d1d5db;">You received this because you signed up for a free trial. This is the only reminder we\'ll send.</p> </td></tr> <tr><td height="5" style="background:#2b44f5;font-size:0;">&nbsp;</td></tr> </table> </td></tr> </table> </body> </html>';
110 // Send via WHMCS localAPI
111 $results = localAPI('SendEmail', [
112 'customtype' => 'general',
113 'customsubject' => $subject,
114 'custommessage' => $body,
115 'id' => $trial->client_id,
116 ]);
117
118 // Fallback log if failed
119 if ($results['result'] !== 'success') {
120 logActivity("Trial Reminder — SendEmail failed for client #{$trial->client_id}: " . $results['message']);
121 }
122
123 if ($results['result'] === 'success') {
124 try {
125 Capsule::table('mod_trial_reminder_sent')->insert([
126 'service_id' => $trial->service_id,
127 'client_id' => $trial->client_id,
128 'sent_at' => $now->format('Y-m-d H:i:s'),
129 ]);
130 logActivity("Trial Reminder — sent to client #{$trial->client_id}, service #{$trial->service_id}, {$timeLeft} remaining.");
131 } catch (\Exception $e) {
132 logActivity('Trial Reminder — sent OK but tracking failed: ' . $e->getMessage());
133 }
134 } else {
135 logActivity("Trial Reminder — FAILED for client #{$trial->client_id}: " . $results['message']);
136 }
137}
138
139exit(0);
140