route = 'referrals'; parent::__construct(); } public function registerRoutes(): void { // Get user's referrals register_rest_route($this->namespace, "/{$this->route}", [ 'methods' => 'GET', 'callback' => [$this, 'getUserReferrals'], 'permission_callback' => [$this, 'checkPermission'] ]); register_rest_route($this->namespace, '/referrals/register', [ 'methods' => 'POST', 'callback' => [$this, 'registerWithReferral'], 'permission_callback' => '__return_true', 'args' => [ 'name' => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field' ], 'email' => [ 'required' => true, 'type' => 'string', 'format' => 'email', 'validate_callback' => function($param) { return is_email($param); } ], 'code' => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => function($code) { return strtoupper(sanitize_text_field($code)); } ] ] ]); register_rest_route($this->namespace, '/referrals/check-code', [ 'methods' => 'POST', 'callback' => [$this, 'checkReferralCode'], 'permission_callback' => '__return_true', 'args' => [ 'code' => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => function($code) { return strtoupper(sanitize_text_field($code)); } ] ] ]); // Get or create referral code register_rest_route($this->namespace, "/{$this->route}/code", [ 'methods' => 'GET', 'callback' => [$this, 'getReferralCode'], 'permission_callback' => [$this, 'checkPermission'] ]); // Track referral click (public endpoint) register_rest_route($this->namespace, "/{$this->route}/track", [ 'methods' => 'POST', 'callback' => [$this, 'trackReferralClick'], 'permission_callback' => '__return_true', 'args' => [ 'code' => [ 'required' => true, 'type' => 'string' ] ] ]); // Mark referral as treated register_rest_route($this->namespace, "/{$this->route}/(?P\d+)/treat", [ 'methods' => 'POST', 'callback' => [$this, 'markAsTreated'], 'permission_callback' => function() { return current_user_can('manage_options'); }, 'args' => [ 'id' => [ 'required' => true, 'validate_callback' => function($param) { return is_numeric($param); } ] ] ]); // Send referral invitation register_rest_route($this->namespace, '/'.$this->route.'/invite', [ 'methods' => 'POST', 'callback' => [$this, 'sendInvitation'], 'permission_callback' => [$this, 'checkPermission'], 'args' => [ 'email' => [ 'required' => true, 'type' => 'string', 'format' => 'email', 'validate_callback' => function($param) { return is_email($param); } ], 'name' => [ 'required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field' ] ] ]); // Send batch invitations register_rest_route($this->namespace, '/'.$this->route.'/invite/batch', [ 'methods' => 'POST', 'callback' => [$this, 'sendBatchInvitations'], 'permission_callback' => [$this, 'checkPermission'], 'args' => [ 'invitations' => [ 'required' => true, 'type' => 'array', 'validate_callback' => function($param) { return is_array($param) && !empty($param); } ] ] ]); // Get invitation stats for current user register_rest_route($this->namespace, '/'.$this->route.'/invite/stats', [ 'methods' => 'GET', 'callback' => [$this, 'getInvitationStats'], 'permission_callback' => [$this, 'checkPermission'] ]); // Export referrals for Jane App register_rest_route($this->namespace, '/'.$this->route.'/export', [ 'methods' => 'POST', 'callback' => [$this, 'exportReferrals'], 'permission_callback' => function() { return current_user_can('manage_options'); }, 'args' => [ 'start_date' => [ 'required' => true, 'type' => 'string', 'validate_callback' => function($param) { return (bool) strtotime($param); } ], 'end_date' => [ 'required' => true, 'type' => 'string', 'validate_callback' => function($param) { return (bool) strtotime($param); } ] ] ]); // Get top referrers (admin only) register_rest_route($this->namespace, "/{$this->route}/leaderboard", [ 'methods' => 'GET', 'callback' => [$this, 'getTopReferrers'], 'permission_callback' => function() { return current_user_can('manage_options'); }, 'args' => [ 'period' => [ 'default' => 'week', 'enum' => ['day', 'week', 'month', 'all'] ], 'limit' => [ 'default' => 10, 'type' => 'integer' ] ] ]); // Get/Update referral settings (admin only) register_rest_route($this->namespace, "/{$this->route}/settings", [ [ 'methods' => 'GET', 'callback' => [$this, 'getSettings'], 'permission_callback' => function() { return current_user_can('manage_options'); } ], [ 'methods' => 'POST', 'callback' => [$this, 'updateSettings'], 'permission_callback' => function() { return current_user_can('manage_options'); } ] ]); } public function checkPermission(WP_REST_Request $request): bool { return is_user_logged_in(); } /** * Get user's referrals */ public function getUserReferrals(WP_REST_Request $request): WP_REST_Response { $user_id = get_current_user_id(); $args = [ 'status' => $request->get_param('status') ?? 'all', 'limit' => $request->get_param('limit') ?? 50, 'offset' => $request->get_param('offset') ?? 0 ]; $referrals = JVB()->referrals()->getUserReferrals($user_id, $args); return new WP_REST_Response([ 'success' => true, 'referrals' => $referrals ]); } /** * Get user's referral code */ public function getReferralCode(WP_REST_Request $request): WP_REST_Response { $user_id = get_current_user_id(); $code = JVB()->referrals()->getUserReferralCode($user_id); if (is_wp_error($code)) { return new WP_REST_Response([ 'success' => false, 'message' => $code->get_error_message() ], 400); } return new WP_REST_Response([ 'success' => true, 'code' => $code, 'share_url' => home_url('/?ref=' . $code) ]); } /** * Update user's referral code */ public function updateReferralCode(WP_REST_Request $request): WP_REST_Response { $user_id = get_current_user_id(); $new_code = strtoupper(sanitize_text_field($request->get_param('code'))); $result = JVB()->referrals()->getUserReferralCode($user_id, $new_code); if (is_wp_error($result)) { return new WP_REST_Response([ 'success' => false, 'message' => $result->get_error_message() ], 400); } return new WP_REST_Response([ 'success' => true, 'code' => $result, 'message' => 'Referral code updated successfully' ]); } /** * Track referral click and store in session */ public function trackReferralClick(WP_REST_Request $request): WP_REST_Response { $code = strtoupper(sanitize_text_field($request->get_param('code'))); // Start session if not already started if (session_status() === PHP_SESSION_NONE) { session_start(); } // Store referral code in both session and cookie (30 day expiry) $_SESSION[BASE . 'referral_code'] = $code; setcookie(BASE . 'referral_code', $code, time() + (30 * DAY_IN_SECONDS), '/'); return new WP_REST_Response([ 'success' => true, 'message' => 'Referral tracked' ]); } /** * Mark referral as treated */ public function markAsTreated(WP_REST_Request $request): WP_REST_Response { $referral_id = intval($request->get_param('id')); $result = JVB()->referrals()->markAsTreated($referral_id, true); if (!$result) { return new WP_REST_Response([ 'success' => false, 'message' => 'Failed to update referral' ], 400); } return new WP_REST_Response([ 'success' => true, 'message' => 'Referral marked as treated and rewards created' ]); } /** * Get user stats */ public function getUserStats(WP_REST_Request $request): WP_REST_Response { $user_id = get_current_user_id(); $stats = JVB()->referrals()->getUserStats($user_id); return new WP_REST_Response([ 'success' => true, 'stats' => $stats ]); } /** * Get top referrers */ public function getTopReferrers(WP_REST_Request $request): WP_REST_Response { $period = $request->get_param('period') ?? 'week'; $limit = $request->get_param('limit') ?? 10; $top_referrers = JVB()->referrals()->getTopReferrers($limit, $period); return new WP_REST_Response([ 'success' => true, 'period' => $period, 'referrers' => $top_referrers ]); } /** * Get referral settings */ public function getSettings(WP_REST_Request $request): WP_REST_Response { $settings = get_option(BASE . 'referral_settings', []); return new WP_REST_Response([ 'success' => true, 'settings' => $settings ]); } /** * Update referral settings */ public function updateSettings(WP_REST_Request $request): WP_REST_Response { $settings = [ 'referrer_reward_type' => $request->get_param('referrer_reward_type') ?? 'per_user', 'referrer_reward_amount' => floatval($request->get_param('referrer_reward_amount') ?? 25), 'referee_reward_type' => $request->get_param('referee_reward_type') ?? 'percentage', 'referee_reward_amount' => floatval($request->get_param('referee_reward_amount') ?? 20), 'referee_reward_applies_to' => $request->get_param('referee_reward_applies_to') ?? 'first_order' ]; update_option(BASE . 'referral_settings', $settings); return new WP_REST_Response([ 'success' => true, 'message' => 'Settings updated successfully', 'settings' => $settings ]); } /** * Send a single referral invitation * * @param WP_REST_Request $request * @return WP_REST_Response */ public function sendInvitation(WP_REST_Request $request): WP_REST_Response { $user_id = get_current_user_id(); $email = sanitize_email($request->get_param('email')); $name = sanitize_text_field($request->get_param('name')); // Send invitation via ReferralManager $referral_manager = JVB()->referrals(); $result = $referral_manager->sendReferralInvitation($user_id, $email, $name); if (is_wp_error($result)) { return new WP_REST_Response([ 'success' => false, 'message' => $result->get_error_message(), 'code' => $result->get_error_code() ], 400); } return new WP_REST_Response($result, 200); } /** * Send batch referral invitations * * @param WP_REST_Request $request * @return WP_REST_Response */ public function sendBatchInvitations(WP_REST_Request $request): WP_REST_Response { $user_id = get_current_user_id(); $invitations = $request->get_param('invitations'); // Validate invitation format foreach ($invitations as $invite) { if (empty($invite['email']) || empty($invite['name'])) { return new WP_REST_Response([ 'success' => false, 'message' => 'Each invitation must have email and name' ], 400); } } // Send batch via ReferralManager $referral_manager = JVB()->referrals(); $result = $referral_manager->sendBatchReferralInvitations($user_id, $invitations); return new WP_REST_Response($result, 200); } /** * Get invitation stats for current user * * @param WP_REST_Request $request * @return WP_REST_Response */ public function getInvitationStats(WP_REST_Request $request): WP_REST_Response { $user_id = get_current_user_id(); $referral_manager = JVB()->referrals(); $stats = $referral_manager->getUserInvitationStats($user_id); return new WP_REST_Response([ 'success' => true, 'stats' => $stats ], 200); } /** * Export referrals for Jane App cross-reference * Admin only * * @param WP_REST_Request $request * @return WP_REST_Response */ public function exportReferrals(WP_REST_Request $request): WP_REST_Response { $start_date = sanitize_text_field($request->get_param('start_date')); $end_date = sanitize_text_field($request->get_param('end_date')); $referral_manager = JVB()->referrals(); $csv_content = $referral_manager->exportReferrals($start_date, $end_date); // Return CSV for download return new WP_REST_Response([ 'success' => true, 'csv' => $csv_content, 'filename' => sprintf('referrals_%s_to_%s.csv', $start_date, $end_date) ], 200); } public function registerWithReferral(WP_REST_Request $request): WP_REST_Response { $name = sanitize_text_field($request->get_param('name')); $email = sanitize_email($request->get_param('email')); $code = strtoupper(sanitize_text_field($request->get_param('code'))); // Validate email if (!is_email($email)) { return new WP_REST_Response([ 'success' => false, 'message' => 'Invalid email address' ], 400); } // Check if user exists if (email_exists($email)) { return new WP_REST_Response([ 'success' => false, 'message' => 'An account with this email already exists' ], 400); } // Validate referral code $referral_manager = JVB()->referrals(); $referrer = $referral_manager->getUserByReferralCode($code); if (!$referrer) { return new WP_REST_Response([ 'success' => false, 'message' => 'Invalid referral code' ], 404); } // Get reward text $settings = $referral_manager->getRewardSettings(); $reward_amount = $settings['referee_reward_amount'] ?? 20; $reward_type = $settings['referee_reward_type'] ?? 'percentage'; $reward_text = $reward_type === 'percentage' ? "{$reward_amount}% off your first treatment!" : "\${$reward_amount} off your first treatment!"; // Send magic link with referral context via MagicLinkManager $magic_link_manager = new \JVBase\managers\MagicLinkManager(); $result = $magic_link_manager->sendMagicLink( $email, \JVBase\managers\MagicLinkManager::TYPE_REFERRAL, [ 'name' => $name, 'referral_code' => $code, 'referrer_id' => $referrer->ID, 'referrer_name' => $referrer->display_name, 'reward_text' => $reward_text ] ); if (is_wp_error($result)) { return new WP_REST_Response([ 'success' => false, 'message' => 'Failed to send registration link. Please try again.' ], 500); } return new WP_REST_Response([ 'success' => true, 'message' => 'Check your email! We sent you a link to complete your registration.', 'email' => $email ], 200); } public function checkReferralCode(WP_REST_Request $request): WP_REST_Response { $code = strtoupper(sanitize_text_field($request->get_param('code'))); if (empty($code)) { return new WP_REST_Response([ 'success' => false, 'message' => 'Code is required' ], 400); } $referral_manager = JVB()->referrals(); $referrer = $referral_manager->getUserByReferralCode($code); if (!$referrer) { return new WP_REST_Response([ 'success' => false, 'message' => 'Invalid referral code' ], 404); } // Return basic referrer info (no sensitive data) return new WP_REST_Response([ 'success' => true, 'code' => $code, 'referrer_name' => $referrer->display_name, ], 200); } }