<?php
|
namespace JVBase\managers;
|
|
use WP_User;
|
use WP_Error;
|
|
if (!defined('ABSPATH')) {
|
exit;
|
}
|
|
/**
|
* Referral Tracking Manager
|
*
|
* Handles user referral codes, tracking, rewards, and reporting.
|
* Keeps referrals separate from invitations system.
|
*/
|
class ReferralManager
|
{
|
protected $wpdb;
|
protected CacheManager $cache;
|
protected string $referrals_table;
|
protected string $rewards_table;
|
|
// Default reward settings
|
protected array $default_settings = [
|
'referrer_reward_applies_to' => 'per_user', // 'per_user' or 'flat_total'
|
'referrer_reward_amount' => 25.00,
|
'referrer_reward_type' => 'fixed',
|
'referee_reward_type' => 'percentage', // 'percentage' or 'fixed'
|
'referee_reward_amount' => 20, // 20% or $20
|
'referee_reward_applies_to' => 'first_order' // 'first_order' or 'all_orders'
|
];
|
|
public function __construct()
|
{
|
global $wpdb;
|
$this->wpdb = $wpdb;
|
$this->cache = new CacheManager('referrals');
|
$this->referrals_table = BASE . 'referrals';
|
$this->rewards_table = BASE . 'referral_rewards';
|
|
// Hook into user registration to track referrals
|
add_action('user_register', [$this, 'processReferral'], 10, 1);
|
|
// Add meta boxes for admin to manage referrals
|
add_action('show_user_profile', [$this, 'displayUserReferralInfo']);
|
add_action('edit_user_profile', [$this, 'displayUserReferralInfo']);
|
|
// Save referral code changes
|
add_action('personal_options_update', [$this, 'saveUserReferralCode']);
|
add_action('edit_user_profile_update', [$this, 'saveUserReferralCode']);
|
|
add_filter(BASE.'new_user_email_content', [$this, 'addReferralToWelcomeEmail'], 99, 2);
|
|
if (is_user_logged_in()) {
|
add_action('wp_footer', [$this, 'outputShareWidget']);
|
}
|
|
add_action('template_redirect', [$this, 'trackReferralCode']);;
|
// Schedule cron jobs for reports
|
$this->registerCronJobs();
|
}
|
|
/**
|
* Register cron jobs for automated reporting
|
*/
|
protected function registerCronJobs(): void
|
{
|
add_action(BASE . 'referral_daily_report', [$this, 'sendDailyReport']);
|
add_action(BASE . 'referral_weekly_report', [$this, 'sendWeeklyReport']);
|
|
// Schedule if not already scheduled
|
if (!wp_next_scheduled(BASE . 'referral_daily_report')) {
|
wp_schedule_event(strtotime('tomorrow 9:00'), 'daily', BASE . 'referral_daily_report');
|
}
|
|
if (!wp_next_scheduled(BASE . 'referral_weekly_report')) {
|
wp_schedule_event(strtotime('next Monday 9:00'), 'weekly', BASE . 'referral_weekly_report');
|
}
|
}
|
|
/**
|
* Generate or get existing referral code for a user
|
*
|
* @param int $user_id
|
* @param string|null $custom_code Optional custom code
|
* @return string|WP_Error
|
*/
|
public function getUserReferralCode(int $user_id, ?string $custom_code = null)
|
{
|
$user = get_user_by('ID', $user_id);
|
if (!$user) {
|
return new WP_Error('invalid_user', 'User not found');
|
}
|
|
// Check if user already has a code
|
$existing_code = get_user_meta($user_id, BASE . 'referral_code', true);
|
|
if ($existing_code && !$custom_code) {
|
return $existing_code;
|
}
|
|
// Generate new code if custom provided or none exists
|
$code = $custom_code ?: $this->generateReferralCode($user);
|
|
// Validate uniqueness
|
if ($this->isCodeTaken($code, $user_id)) {
|
return new WP_Error('code_taken', 'This referral code is already in use');
|
}
|
|
// Save the code
|
update_user_meta($user_id, BASE . 'referral_code', $code);
|
|
return $code;
|
}
|
|
/**
|
* Generate a referral code based on user info
|
*
|
* @param WP_User $user
|
* @return string
|
*/
|
protected function generateReferralCode(WP_User $user): string
|
{
|
// Create code from first and last name
|
$first = sanitize_title($user->first_name ?: 'user');
|
$last = sanitize_title($user->last_name ?: wp_generate_password(4, false));
|
|
$base_code = strtoupper(substr($first, 0, 4) . substr($last, 0, 4));
|
|
// Ensure uniqueness
|
$code = $base_code;
|
$suffix = 1;
|
|
while ($this->isCodeTaken($code)) {
|
$code = $base_code . $suffix;
|
$suffix++;
|
}
|
|
return $code;
|
}
|
|
/**
|
* Check if a referral code is already taken
|
*
|
* @param string $code
|
* @param int|null $exclude_user_id
|
* @return bool
|
*/
|
protected function isCodeTaken(string $code, ?int $exclude_user_id = null): bool
|
{
|
$args = [
|
'meta_key' => BASE . 'referral_code',
|
'meta_value' => $code,
|
'fields' => 'ID',
|
'number' => 1
|
];
|
|
if ($exclude_user_id) {
|
$args['exclude'] = [$exclude_user_id];
|
}
|
|
$users = get_users($args);
|
return !empty($users);
|
}
|
|
/**
|
* Track a new referral when user registers
|
*
|
* @param int $user_id
|
*/
|
public function processReferral(int $user_id): void
|
{
|
// Check if there's a referral code in the session/cookie
|
$referral_code = $this->getReferralCodeFromSession();
|
|
if (!$referral_code) {
|
return;
|
}
|
|
// Find the referrer
|
$referrer = $this->getUserByReferralCode($referral_code);
|
|
if (!$referrer) {
|
return;
|
}
|
|
// Check if this user was already referred (prevent duplicates)
|
$existing = $this->getReferralByReferee($user_id);
|
if ($existing) {
|
return;
|
}
|
|
// Create referral record
|
$this->createReferral($referrer->ID, $user_id, $referral_code);
|
|
// Clear the session
|
$this->clearReferralSession();
|
}
|
|
/**
|
* Create a referral record in the database
|
*
|
* @param int $referrer_id
|
* @param int $referee_id
|
* @param string $code
|
* @return int|false
|
*/
|
protected function createReferral(int $referrer_id, int $referee_id, string $code)
|
{
|
$user = get_user_by('ID', $referee_id);
|
|
return $this->wpdb->insert(
|
$this->referrals_table,
|
[
|
'referrer_id' => $referrer_id,
|
'referee_id' => $referee_id,
|
'referee_name' => $user->display_name,
|
'referee_email' => $user->user_email,
|
'referee_phone' => get_user_meta($referee_id, BASE . 'phone', true) ?: '',
|
'referral_code' => $code,
|
'status' => 'pending',
|
'referred_at' => current_time('mysql')
|
],
|
['%d', '%d', '%s', '%s', '%s', '%s', '%s', '%s']
|
);
|
}
|
|
/**
|
* Get user by referral code
|
*
|
* @param string $code
|
* @return WP_User|null
|
*/
|
protected function getUserByReferralCode(string $code): ?WP_User
|
{
|
$users = get_users([
|
'meta_key' => BASE . 'referral_code',
|
'meta_value' => $code,
|
'number' => 1
|
]);
|
|
return $users[0] ?? null;
|
}
|
|
/**
|
* Get referral record by referee ID
|
*
|
* @param int $referee_id
|
* @return object|null
|
*/
|
public function getReferralByReferee(int $referee_id): ?object
|
{
|
$result = $this->wpdb->get_row($this->wpdb->prepare(
|
"SELECT * FROM {$this->referrals_table} WHERE referee_id = %d",
|
$referee_id
|
));
|
|
return $result ?: null;
|
}
|
|
/**
|
* Mark a referral as treated/rewarded
|
*
|
* @param int $referral_id
|
* @param bool $treated
|
* @return bool
|
*/
|
public function markAsTreated(int $referral_id, bool $treated = true): bool
|
{
|
$status = $treated ? 'treated' : 'pending';
|
|
$result = $this->wpdb->update(
|
$this->referrals_table,
|
[
|
'status' => $status,
|
'treated_at' => $treated ? current_time('mysql') : null
|
],
|
['id' => $referral_id],
|
['%s', '%s'],
|
['%d']
|
);
|
|
if ($result && $treated) {
|
// Create reward records when marking as treated
|
$this->createRewardRecords($referral_id);
|
}
|
|
return $result !== false;
|
}
|
|
/**
|
* Create reward records for both referrer and referee
|
*
|
* @param int $referral_id
|
*/
|
protected function createRewardRecords(int $referral_id): void
|
{
|
$referral = $this->wpdb->get_row($this->wpdb->prepare(
|
"SELECT * FROM {$this->referrals_table} WHERE id = %d",
|
$referral_id
|
));
|
|
if (!$referral) {
|
return;
|
}
|
|
$settings = $this->getRewardSettings();
|
|
// Create referrer reward
|
$this->wpdb->insert(
|
$this->rewards_table,
|
[
|
'referral_id' => $referral_id,
|
'user_id' => $referral->referrer_id,
|
'reward_type' => 'referrer',
|
'amount' => $settings['referrer_reward_amount'],
|
'status' => 'available',
|
'created_at' => current_time('mysql')
|
],
|
['%d', '%d', '%s', '%f', '%s', '%s']
|
);
|
|
// Create referee reward
|
$referee_amount = $settings['referee_reward_type'] === 'percentage'
|
? $settings['referee_reward_amount'] // Store as percentage
|
: $settings['referee_reward_amount']; // Store as fixed amount
|
|
$this->wpdb->insert(
|
$this->rewards_table,
|
[
|
'referral_id' => $referral_id,
|
'user_id' => $referral->referee_id,
|
'reward_type' => 'referee',
|
'amount' => $referee_amount,
|
'reward_calculation' => $settings['referee_reward_type'],
|
'status' => 'available',
|
'created_at' => current_time('mysql')
|
],
|
['%d', '%d', '%s', '%f', '%s', '%s', '%s']
|
);
|
}
|
|
/**
|
* Get referrals for a user
|
*
|
* @param int $user_id
|
* @param array $args
|
* @return array
|
*/
|
public function getUserReferrals(int $user_id, array $args = []): array
|
{
|
$defaults = [
|
'status' => 'all',
|
'limit' => 100,
|
'offset' => 0,
|
'orderby' => 'referred_at',
|
'order' => 'DESC'
|
];
|
|
$args = wp_parse_args($args, $defaults);
|
|
$where = $this->wpdb->prepare("WHERE referrer_id = %d", $user_id);
|
|
if ($args['status'] !== 'all') {
|
$where .= $this->wpdb->prepare(" AND status = %s", $args['status']);
|
}
|
|
$query = "SELECT * FROM {$this->referrals_table}
|
{$where}
|
ORDER BY {$args['orderby']} {$args['order']}
|
LIMIT {$args['limit']} OFFSET {$args['offset']}";
|
|
return $this->wpdb->get_results($query);
|
}
|
|
/**
|
* Get referral statistics for a user
|
*
|
* @param int $user_id
|
* @return array
|
*/
|
public function getUserStats(int $user_id): array
|
{
|
$cache_key = 'stats_' . $user_id;
|
$cached = $this->cache->get($cache_key);
|
|
if ($cached !== false) {
|
return $cached;
|
}
|
|
$stats = $this->wpdb->get_row($this->wpdb->prepare(
|
"SELECT
|
COUNT(*) as total_referrals,
|
SUM(CASE WHEN status = 'treated' THEN 1 ELSE 0 END) as treated_count,
|
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_count
|
FROM {$this->referrals_table}
|
WHERE referrer_id = %d",
|
$user_id
|
), ARRAY_A);
|
|
// Get total rewards
|
$rewards = $this->wpdb->get_row($this->wpdb->prepare(
|
"SELECT
|
SUM(CASE WHEN status = 'available' THEN amount ELSE 0 END) as available_rewards,
|
SUM(CASE WHEN status = 'redeemed' THEN amount ELSE 0 END) as redeemed_rewards
|
FROM {$this->rewards_table}
|
WHERE user_id = %d AND reward_type = 'referrer'",
|
$user_id
|
), ARRAY_A);
|
|
$stats = array_merge($stats, $rewards);
|
|
$this->cache->set($cache_key, $stats, HOUR_IN_SECONDS);
|
|
return $stats;
|
}
|
|
/**
|
* Get top referrers for a time period
|
*
|
* @param int $limit
|
* @param string $period 'day'|'week'|'month'|'all'
|
* @return array
|
*/
|
public function getTopReferrers(int $limit = 10, string $period = 'all'): array
|
{
|
$where = '';
|
|
if ($period !== 'all') {
|
$date_where = match($period) {
|
'day' => "referred_at >= DATE_SUB(NOW(), INTERVAL 1 DAY)",
|
'week' => "referred_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)",
|
'month' => "referred_at >= DATE_SUB(NOW(), INTERVAL 1 MONTH)",
|
default => "1=1"
|
};
|
|
$where = "WHERE {$date_where}";
|
}
|
|
$query = "SELECT
|
referrer_id,
|
COUNT(*) as referral_count,
|
SUM(CASE WHEN status = 'treated' THEN 1 ELSE 0 END) as treated_count
|
FROM {$this->referrals_table}
|
{$where}
|
GROUP BY referrer_id
|
ORDER BY referral_count DESC
|
LIMIT {$limit}";
|
|
$results = $this->wpdb->get_results($query);
|
|
// Enrich with user data
|
foreach ($results as &$result) {
|
$user = get_user_by('ID', $result->referrer_id);
|
$result->user_name = $user ? $user->display_name : 'Unknown';
|
$result->user_email = $user ? $user->user_email : '';
|
}
|
|
return $results;
|
}
|
|
/**
|
* Send daily report if there are new referrals
|
*/
|
public function sendDailyReport(): void
|
{
|
// Get referrals from the last 24 hours
|
$referrals = $this->wpdb->get_results(
|
"SELECT r.*, u.display_name as referrer_name, u.user_email as referrer_email
|
FROM {$this->referrals_table} r
|
LEFT JOIN {$this->wpdb->users} u ON r.referrer_id = u.ID
|
WHERE r.referred_at >= DATE_SUB(NOW(), INTERVAL 1 DAY)
|
ORDER BY r.referred_at DESC"
|
);
|
|
if (empty($referrals)) {
|
return; // No referrals, no email
|
}
|
|
// Generate CSV
|
$csv_content = $this->generateCSV($referrals);
|
$csv_filename = 'referrals-' . date('Y-m-d') . '.csv';
|
|
// Save CSV temporarily
|
$upload_dir = wp_upload_dir();
|
$csv_path = $upload_dir['basedir'] . '/' . $csv_filename;
|
file_put_contents($csv_path, $csv_content);
|
|
// Send email with attachment
|
$to = get_option('admin_email');
|
$subject = '[' . get_bloginfo('name') . '] Daily Referral Report - ' . date('F j, Y');
|
|
$message = $this->generateReportEmail($referrals, 'daily');
|
|
$attachments = [$csv_path];
|
|
wp_mail($to, $subject, $message, ['Content-Type: text/html; charset=UTF-8'], $attachments);
|
|
// Clean up temporary file
|
unlink($csv_path);
|
}
|
|
/**
|
* Send weekly report with top referrers
|
*/
|
public function sendWeeklyReport(): void
|
{
|
$top_referrers = $this->getTopReferrers(10, 'week');
|
$total_referrals = $this->wpdb->get_var(
|
"SELECT COUNT(*) FROM {$this->referrals_table}
|
WHERE referred_at >= DATE_SUB(NOW(), INTERVAL 1 WEEK)"
|
);
|
|
if ($total_referrals == 0) {
|
return;
|
}
|
|
$to = get_option('admin_email');
|
$subject = '[' . get_bloginfo('name') . '] Weekly Referral Summary - ' . date('F j, Y');
|
|
$message = $this->generateWeeklyReportEmail($top_referrers, $total_referrals);
|
|
wp_mail($to, $subject, $message, ['Content-Type: text/html; charset=UTF-8']);
|
}
|
|
/**
|
* Generate CSV content from referrals
|
*
|
* @param array $referrals
|
* @return string
|
*/
|
protected function generateCSV(array $referrals): string
|
{
|
$csv = "Referred By,Referee Name,Referee Email,Referee Phone,Referral Code,Status,Referred At,Treated At\n";
|
|
foreach ($referrals as $referral) {
|
$csv .= sprintf(
|
'"%s","%s","%s","%s","%s","%s","%s","%s"' . "\n",
|
$referral->referrer_name ?? 'Unknown',
|
$referral->referee_name,
|
$referral->referee_email,
|
$referral->referee_phone,
|
$referral->referral_code,
|
$referral->status,
|
$referral->referred_at,
|
$referral->treated_at ?? 'Not yet'
|
);
|
}
|
|
return $csv;
|
}
|
|
/**
|
* Generate HTML email for daily report
|
*
|
* @param array $referrals
|
* @param string $period
|
* @return string
|
*/
|
protected function generateReportEmail(array $referrals, string $period): string
|
{
|
$count = count($referrals);
|
|
$content = sprintf('<p>You have <strong>%d new referral%s</strong> today.</p>',
|
$count,
|
$count !== 1 ? 's' : ''
|
);
|
|
$content .= '<table style="width:100%; border-collapse: collapse; margin: 20px 0;">';
|
$content .= '<thead><tr style="background: #f5f5f5; text-align: left;">';
|
$content .= '<th style="padding: 10px; border: 1px solid #ddd;">Referred By</th>';
|
$content .= '<th style="padding: 10px; border: 1px solid #ddd;">New User</th>';
|
$content .= '<th style="padding: 10px; border: 1px solid #ddd;">Email</th>';
|
$content .= '<th style="padding: 10px; border: 1px solid #ddd;">Status</th>';
|
$content .= '<th style="padding: 10px; border: 1px solid #ddd;">Time</th>';
|
$content .= '</tr></thead><tbody>';
|
|
foreach ($referrals as $referral) {
|
$content .= '<tr>';
|
$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%s</td>',
|
esc_html($referral->referrer_name ?? 'Unknown'));
|
$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%s</td>',
|
esc_html($referral->referee_name));
|
$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%s</td>',
|
esc_html($referral->referee_email));
|
$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%s</td>',
|
esc_html(ucfirst($referral->status)));
|
$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%s</td>',
|
esc_html(date('g:i A', strtotime($referral->referred_at))));
|
$content .= '</tr>';
|
}
|
|
$content .= '</tbody></table>';
|
$content .= '<p><small>See attached CSV for full details.</small></p>';
|
|
return jvbGetEmailTemplate($content, 'Daily Referral Report');
|
}
|
|
/**
|
* Generate HTML email for weekly report
|
*
|
* @param array $top_referrers
|
* @param int $total_referrals
|
* @return string
|
*/
|
protected function generateWeeklyReportEmail(array $top_referrers, int $total_referrals): string
|
{
|
$content = sprintf(
|
'<p>This week you had <strong>%d total referral%s</strong>.</p>',
|
$total_referrals,
|
$total_referrals !== 1 ? 's' : ''
|
);
|
|
$content .= '<h3>Top 10 Referrers This Week</h3>';
|
$content .= '<table style="width:100%; border-collapse: collapse; margin: 20px 0;">';
|
$content .= '<thead><tr style="background: #f5f5f5; text-align: left;">';
|
$content .= '<th style="padding: 10px; border: 1px solid #ddd;">Rank</th>';
|
$content .= '<th style="padding: 10px; border: 1px solid #ddd;">User</th>';
|
$content .= '<th style="padding: 10px; border: 1px solid #ddd;">Total Referrals</th>';
|
$content .= '<th style="padding: 10px; border: 1px solid #ddd;">Treated</th>';
|
$content .= '</tr></thead><tbody>';
|
|
$rank = 1;
|
foreach ($top_referrers as $referrer) {
|
$content .= '<tr>';
|
$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%d</td>', $rank++);
|
$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%s</td>',
|
esc_html($referrer->user_name));
|
$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%d</td>',
|
$referrer->referral_count);
|
$content .= sprintf('<td style="padding: 10px; border: 1px solid #ddd;">%d</td>',
|
$referrer->treated_count);
|
$content .= '</tr>';
|
}
|
|
$content .= '</tbody></table>';
|
|
return jvbGetEmailTemplate($content, 'Weekly Referral Summary');
|
}
|
|
/**
|
* Get reward settings
|
*
|
* @return array
|
*/
|
protected function getRewardSettings(): array
|
{
|
$saved = get_option(BASE . 'referral_settings', []);
|
return wp_parse_args($saved, $this->default_settings);
|
}
|
|
/**
|
* Session/Cookie handling for referral codes
|
*/
|
protected function getReferralCodeFromSession(): ?string
|
{
|
if (session_status() === PHP_SESSION_NONE) {
|
session_start();
|
}
|
|
return $_SESSION[BASE . 'referral_code'] ?? $_COOKIE[BASE . 'referral_code'] ?? null;
|
}
|
|
protected function clearReferralSession(): void
|
{
|
if (session_status() === PHP_SESSION_NONE) {
|
session_start();
|
}
|
|
unset($_SESSION[BASE . 'referral_code']);
|
setcookie(BASE . 'referral_code', '', time() - 3600, '/');
|
}
|
|
/**
|
* Display referral info in user profile
|
*
|
* @param WP_User $user
|
*/
|
public function displayUserReferralInfo(WP_User $user): void
|
{
|
if (!current_user_can('edit_user', $user->ID)) {
|
return;
|
}
|
|
$referral_code = get_user_meta($user->ID, BASE . 'referral_code', true);
|
$stats = $this->getUserStats($user->ID);
|
$referrals = $this->getUserReferrals($user->ID, ['limit' => 10]);
|
|
?>
|
<h2>Referral Information</h2>
|
<table class="form-table">
|
<tr>
|
<th><label for="referral_code">Referral Code</label></th>
|
<td>
|
<input type="text"
|
name="referral_code"
|
id="referral_code"
|
value="<?php echo esc_attr($referral_code); ?>"
|
class="regular-text" />
|
<p class="description">
|
Users can sign up with this code.
|
<?php if ($referral_code): ?>
|
Share link: <?php echo home_url('/?ref=' . $referral_code); ?>
|
<?php endif; ?>
|
</p>
|
</td>
|
</tr>
|
</table>
|
|
<h3>Referral Statistics</h3>
|
<table class="form-table">
|
<tr>
|
<th>Total Referrals:</th>
|
<td><?php echo $stats['total_referrals'] ?? 0; ?></td>
|
</tr>
|
<tr>
|
<th>Treated:</th>
|
<td><?php echo $stats['treated_count'] ?? 0; ?></td>
|
</tr>
|
<tr>
|
<th>Pending:</th>
|
<td><?php echo $stats['pending_count'] ?? 0; ?></td>
|
</tr>
|
<tr>
|
<th>Available Rewards:</th>
|
<td>$<?php echo number_format($stats['available_rewards'] ?? 0, 2); ?></td>
|
</tr>
|
<tr>
|
<th>Redeemed Rewards:</th>
|
<td>$<?php echo number_format($stats['redeemed_rewards'] ?? 0, 2); ?></td>
|
</tr>
|
</table>
|
|
<?php if (!empty($referrals)): ?>
|
<h3>Recent Referrals</h3>
|
<table class="widefat">
|
<thead>
|
<tr>
|
<th>Name</th>
|
<th>Email</th>
|
<th>Status</th>
|
<th>Referred At</th>
|
<th>Actions</th>
|
</tr>
|
</thead>
|
<tbody>
|
<?php foreach ($referrals as $referral): ?>
|
<tr>
|
<td><?php echo esc_html($referral->referee_name); ?></td>
|
<td><?php echo esc_html($referral->referee_email); ?></td>
|
<td><?php echo esc_html(ucfirst($referral->status)); ?></td>
|
<td><?php echo esc_html($referral->referred_at); ?></td>
|
<td>
|
<?php if ($referral->status === 'pending'): ?>
|
<button type="button"
|
onclick="markReferralTreated(<?php echo $referral->id; ?>)">
|
Mark as Treated
|
</button>
|
<?php endif; ?>
|
</td>
|
</tr>
|
<?php endforeach; ?>
|
</tbody>
|
</table>
|
<?php endif; ?>
|
|
<script>
|
function markReferralTreated(referralId) {
|
if (!confirm('Mark this referral as treated? This will create reward records.')) {
|
return;
|
}
|
|
fetch('<?php echo rest_url(BASE . '/v1/referrals/' ); ?>' + referralId + '/treat', {
|
method: 'POST',
|
headers: {
|
'X-WP-Nonce': '<?php echo wp_create_nonce('wp_rest'); ?>'
|
}
|
})
|
.then(response => response.json())
|
.then(data => {
|
if (data.success) {
|
alert('Referral marked as treated!');
|
location.reload();
|
} else {
|
alert('Error: ' + (data.message || 'Unknown error'));
|
}
|
});
|
}
|
</script>
|
<?php
|
}
|
|
/**
|
* Save custom referral code
|
*
|
* @param int $user_id
|
*/
|
public function saveUserReferralCode(int $user_id): void
|
{
|
if (!current_user_can('edit_user', $user_id)) {
|
return;
|
}
|
|
if (!isset($_POST['referral_code'])) {
|
return;
|
}
|
|
$code = sanitize_text_field($_POST['referral_code']);
|
|
if (empty($code)) {
|
delete_user_meta($user_id, BASE . 'referral_code');
|
return;
|
}
|
|
// Validate code format (alphanumeric only)
|
if (!preg_match('/^[A-Z0-9]+$/i', $code)) {
|
add_action('user_profile_update_errors', function($errors) {
|
$errors->add('invalid_referral_code',
|
'Referral code can only contain letters and numbers.');
|
});
|
return;
|
}
|
|
// Check if code is unique
|
if ($this->isCodeTaken($code, $user_id)) {
|
add_action('user_profile_update_errors', function($errors) {
|
$errors->add('referral_code_taken',
|
'This referral code is already in use.');
|
});
|
return;
|
}
|
|
update_user_meta($user_id, BASE . 'referral_code', strtoupper($code));
|
}
|
|
public function trackReferralCode(): void
|
{
|
if (!isset($_GET['ref'])) {
|
return;
|
}
|
|
$referral_code = strtoupper(sanitize_text_field($_GET['ref']));
|
|
// Start session if not already started
|
if (session_status() === PHP_SESSION_NONE) {
|
session_start();
|
}
|
|
// Store in both session and cookie (30 day expiry)
|
$_SESSION[BASE . 'referral_code'] = $referral_code;
|
setcookie(BASE . 'referral_code', $referral_code, time() + (30 * DAY_IN_SECONDS), '/');
|
|
// Optional: Redirect to clean URL (removes ?ref= from address bar)
|
$clean_url = remove_query_arg('ref');
|
wp_safe_redirect($clean_url);
|
exit;
|
}
|
|
/**
|
* Display user's referral code and share options
|
* Use in templates or dashboard with: echo jvbReferralShareWidget();
|
*
|
* @return string HTML output
|
*/
|
public function outputShareWidget(): string
|
{
|
$user_id = get_current_user_id();
|
|
if (!$user_id) {
|
return '';
|
}
|
|
$referral_code = get_user_meta($user_id, BASE . 'referral_code', true);
|
|
// Generate code if user doesn't have one
|
if (empty($referral_code)) {
|
$manager = new \JVBase\managers\ReferralManager();
|
$referral_code = $manager->getUserReferralCode($user_id);
|
}
|
|
$share_url = home_url('/?ref=' . $referral_code);
|
$encoded_url = urlencode($share_url);
|
$site_name = get_bloginfo('name');
|
|
ob_start();
|
?>
|
<div class="jvb-referral-widget" style="background: #f9f9f9; padding: 20px; border-radius: 8px; margin: 20px 0;">
|
<h3 style="margin-top: 0;">Share & Earn Rewards</h3>
|
<p>Share your unique referral code with friends and earn rewards when they book!</p>
|
|
<div class="referral-code-display" style="background: white; padding: 15px; border-radius: 4px; margin: 15px 0; text-align: center;">
|
<label style="display: block; font-size: 12px; color: #666; margin-bottom: 5px;">Your Referral Code</label>
|
<div style="font-size: 24px; font-weight: bold; letter-spacing: 2px; color: #2271b1;">
|
<?php echo esc_html($referral_code); ?>
|
</div>
|
</div>
|
|
<div class="referral-url" style="margin: 15px 0;">
|
<label style="display: block; font-size: 12px; color: #666; margin-bottom: 5px;">Share Link</label>
|
<div style="display: flex; gap: 10px;">
|
<input type="text"
|
readonly
|
value="<?php echo esc_url($share_url); ?>"
|
id="referral-url-<?php echo $user_id; ?>"
|
style="flex: 1; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-family: monospace; font-size: 14px;">
|
<button type="button"
|
onclick="jvbCopyReferralUrl('referral-url-<?php echo $user_id; ?>')"
|
style="padding: 8px 16px; background: #2271b1; color: white; border: none; border-radius: 4px; cursor: pointer;">
|
Copy
|
</button>
|
</div>
|
</div>
|
|
<div class="referral-share-buttons" style="display: flex; gap: 10px; margin-top: 15px; flex-wrap: wrap;">
|
<a href="mailto:?subject=Check out <?php echo esc_attr($site_name); ?>&body=I thought you might like <?php echo esc_url($share_url); ?>"
|
class="share-button"
|
style="padding: 10px 20px; background: #666; color: white; text-decoration: none; border-radius: 4px; display: inline-flex; align-items: center; gap: 8px;">
|
📧 Email
|
</a>
|
<a href="sms:?&body=Check out <?php echo esc_attr($site_name); ?>: <?php echo esc_url($share_url); ?>"
|
class="share-button"
|
style="padding: 10px 20px; background: #25D366; color: white; text-decoration: none; border-radius: 4px; display: inline-flex; align-items: center; gap: 8px;">
|
💬 Text
|
</a>
|
<a href="https://www.facebook.com/sharer/sharer.php?u=<?php echo $encoded_url; ?>"
|
target="_blank"
|
class="share-button"
|
style="padding: 10px 20px; background: #1877f2; color: white; text-decoration: none; border-radius: 4px; display: inline-flex; align-items: center; gap: 8px;">
|
f Facebook
|
</a>
|
<a href="https://twitter.com/intent/tweet?url=<?php echo $encoded_url; ?>&text=Check out <?php echo esc_attr($site_name); ?>"
|
target="_blank"
|
class="share-button"
|
style="padding: 10px 20px; background: #1da1f2; color: white; text-decoration: none; border-radius: 4px; display: inline-flex; align-items: center; gap: 8px;">
|
𝕏 Twitter
|
</a>
|
</div>
|
|
<div id="referral-stats-<?php echo $user_id; ?>" class="referral-stats" style="margin-top: 20px; padding-top: 20px; border-top: 1px solid #ddd;">
|
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 15px; text-align: center;">
|
<div>
|
<div style="font-size: 24px; font-weight: bold; color: #2271b1;" data-stat="total">-</div>
|
<div style="font-size: 12px; color: #666;">Total Referrals</div>
|
</div>
|
<div>
|
<div style="font-size: 24px; font-weight: bold; color: #00a32a;" data-stat="treated">-</div>
|
<div style="font-size: 12px; color: #666;">Completed</div>
|
</div>
|
<div>
|
<div style="font-size: 24px; font-weight: bold; color: #dba617;" data-stat="pending">-</div>
|
<div style="font-size: 12px; color: #666;">Pending</div>
|
</div>
|
<div>
|
<div style="font-size: 24px; font-weight: bold; color: #2271b1;" data-stat="rewards">$0</div>
|
<div style="font-size: 12px; color: #666;">Earned</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
|
<script>
|
function jvbCopyReferralUrl(elementId) {
|
const input = document.getElementById(elementId);
|
input.select();
|
document.execCommand('copy');
|
|
// Visual feedback
|
const button = input.nextElementSibling;
|
const originalText = button.textContent;
|
button.textContent = 'Copied!';
|
button.style.background = '#00a32a';
|
|
setTimeout(() => {
|
button.textContent = originalText;
|
button.style.background = '#2271b1';
|
}, 2000);
|
}
|
|
// Load stats via AJAX
|
(function() {
|
fetch('<?php echo rest_url(BASE . '/v1/referrals/stats'); ?>', {
|
headers: {
|
'X-WP-Nonce': '<?php echo wp_create_nonce('wp_rest'); ?>'
|
}
|
})
|
.then(response => response.json())
|
.then(data => {
|
if (data.success && data.stats) {
|
const container = document.getElementById('referral-stats-<?php echo $user_id; ?>');
|
container.querySelector('[data-stat="total"]').textContent = data.stats.total_referrals || 0;
|
container.querySelector('[data-stat="treated"]').textContent = data.stats.treated_count || 0;
|
container.querySelector('[data-stat="pending"]').textContent = data.stats.pending_count || 0;
|
container.querySelector('[data-stat="rewards"]').textContent =
|
'$' + parseFloat(data.stats.available_rewards || 0).toFixed(2);
|
}
|
})
|
.catch(error => console.error('Error loading referral stats:', error));
|
})();
|
</script>
|
<?php
|
return ob_get_clean();
|
}
|
|
function addReferralToWelcomeEmail(string $content, WP_User $user): string
|
{
|
$referral = $this->getReferralByReferee($user->ID);
|
|
if (!$referral) {
|
return $content;
|
}
|
|
$settings = get_option(BASE . 'referral_settings', []);
|
$reward_amount = $settings['referee_reward_amount'] ?? 20;
|
$reward_type = $settings['referee_reward_type'] ?? 'percentage';
|
|
$reward_text = $reward_type === 'percentage'
|
? $reward_amount . '% off'
|
: '$' . number_format($reward_amount, 2) . ' off';
|
|
$bonus_content = '<div style="background: #e7f5ff; padding: 20px; border-radius: 8px; margin: 20px 0;">';
|
$bonus_content .= '<h3 style="margin-top: 0; color: #2271b1;">🎉 Welcome Bonus!</h3>';
|
$bonus_content .= '<p>Since you were referred by a friend, you\'ve earned <strong>' . $reward_text . '</strong> your first booking!</p>';
|
$bonus_content .= '<p>Your reward will be automatically applied when you book.</p>';
|
$bonus_content .= '</div>';
|
|
// Insert bonus content after the first paragraph
|
$parts = explode('</p>', $content, 2);
|
if (count($parts) === 2) {
|
return $parts[0] . '</p>' . $bonus_content . $parts[1];
|
}
|
|
return $content . $bonus_content;
|
}
|
}
|