Jake Vanderwerf
2025-11-10 e9967fa22781d922ba4eb8fb44fe72d200ac4b14
inc/managers/ReferralManager.php
@@ -24,6 +24,7 @@
   protected MagicLinkManager $magic_link;
   protected CacheManager $cache;
   protected string $referrals_table;
   protected ?int $referralPage = null;
   protected string $rewards_table;
   // Default reward settings
@@ -36,6 +37,8 @@
      'referee_reward_applies_to' => 'first_order'  // 'first_order' or 'all_orders'
   ];
   protected array $settings;
   public function __construct()
   {
      global $wpdb;
@@ -45,8 +48,13 @@
      $this->rewards_table = BASE . 'referral_rewards';
      $this->magic_link = new MagicLinkManager();
      // Hook into user registration to track referrals
      add_action('user_register', [$this, 'processReferral'], 10, 1);
      $this->referralPage = $this->getReferralPageId();
      $this->settings = $this->getRewardSettings();
      add_action('jvbUserRegistered', [$this, 'processRegistrationToken'], 10, 3);
      add_action('jvb_add_token_inputs', [$this, 'addLoginInputs'], 10, 1);
      add_action('jvbUserRegistered', [$this, 'processReferral'], 10, 1);
      // Add meta boxes for admin to manage referrals
      add_action('show_user_profile', [$this, 'displayUserReferralInfo']);
@@ -56,7 +64,7 @@
      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);
      add_filter('jvbNewUserEmail', [$this, 'addReferralToWelcomeEmail'], 99, 2);
      add_filter('jvbAdditionalActions', [$this, 'outputShareWidget']);
@@ -66,7 +74,7 @@
      $this->registerCronJobs();
      // Register admin subpage
      add_filter('jvbAdminSubpages', [$this, 'addSubpage'], 10, 1);
//    add_filter('jvbAdminSubpages', [$this, 'addSubpage'], 10, 1);
      // Add admin bar label for referral page
      add_action('admin_bar_menu', [$this, 'addReferralPageLabel'], 999);
@@ -81,6 +89,37 @@
      add_action('admin_init', [$this, 'registerSettings']);
   }
   public function addLoginInputs(string $action):void
   {
      if (array_key_exists('ref', $_GET)) {
         echo '<input type="hidden" name="referral_code" value="'.$_GET['ref'].'">';
      }
   }
   public function modifyLoginText(array $defaults):array
   {
      if (!array_key_exists('ref', $_GET)){
         return $defaults;
      }
      $code = $_GET['ref'];
      $user = $this->getUserByReferralCode($code);
      $desc = ($user) ? strtok($user->display_name, ' ') . ' invited you ' : 'You\'ve been invited ';
      $defaults['title'] = 'Register your Account. Get Your Reward.';
      $defaults['description'] = [
         $desc.' to see the difference with us.',
         'Oh, and you\'ll get 20% off your first treatment!',
         'Finish this account creation (you\'ll still need to make an account on Jane App to book), or let us know when you come in for your appointment.'
      ];
      $defaults['extra'] = [
         'Once you\'re in, you\'ll get your own code you can share.',
         'Get a $25 credit for <b>each</b> person you send our way who comes in for their first treatment!'
      ];
      return $defaults;
   }
   public function enqueueScripts():void
   {
      $requirements = [
@@ -205,17 +244,47 @@
      return !empty($users);
   }
   public function processRegistrationToken(int $user_id, string $email, array $data): void
   {
      // Check for referral code in data
      $code = $data['referral_code'] ?? '';
      if (empty($code)) {
         return;
      }
      // Store in session/cookie for processReferral to pick up
      if (session_status() === PHP_SESSION_NONE) {
         session_start();
      }
      $_SESSION[BASE . 'referral_code'] = sanitize_text_field($code);
      setcookie(
         BASE . 'referral_code',
         sanitize_text_field($code),
         time() + (86400 * 30),
         '/'
      );
   }
   /**
    * Track a new referral when user registers
    *
    * @param int $user_id
    */
   public function processReferral(int $user_id): void
   public function processReferral(int $user_id, string $email, array $data): void
   {
      // Check if user was created via referral magic link
      $referral_code = get_user_meta($user_id, BASE . 'pending_referral_code', true);
      // Try to get code from multiple sources
      $referral_code = $data['referral_code'] ??
      get_user_meta($user_id, BASE . 'pending_referral_code', true);
      if (!$referral_code) {
      // Check session/cookie if not in data
      if (empty($referral_code)) {
         if (session_status() === PHP_SESSION_NONE) {
            session_start();
         }
         $referral_code = $_SESSION[BASE . 'referral_code'] ?? $_COOKIE[BASE . 'referral_code'] ?? '';
      }
      if (empty($referral_code)) {
         return;
      }
@@ -254,7 +323,7 @@
    * @param string $code
    * @return int|false
    */
   protected function createReferral(int $referrer_id, int $referee_id, string $code)
   public function createReferral(int $referrer_id, int $referee_id, string $code)
   {
      $user = get_user_by('ID', $referee_id);
@@ -282,13 +351,17 @@
    */
   public function getUserByReferralCode(string $code): ?WP_User
   {
      $users = get_users([
         'meta_key' => BASE . 'referral_code',
         'meta_value' => $code,
         'number' => 1
      ]);
      return $users[0] ?? null;
      return $this->cache->remember(
         $code,
         function () use ($code) {
            $users = get_users([
               'meta_key' => BASE . 'referral_code',
               'meta_value' => $code,
               'number' => 1
            ]);
            return $users[0] ?? null;
         }
      );
   }
   /**
@@ -353,8 +426,6 @@
         return;
      }
      $settings = $this->getRewardSettings();
      // Create referrer reward
      $this->wpdb->insert(
         $this->rewards_table,
@@ -362,7 +433,7 @@
            'referral_id' => $referral_id,
            'user_id' => $referral->referrer_id,
            'reward_type' => 'referrer',
            'amount' => $settings['referrer_reward_amount'],
            'amount' => $this->settings['referrer_reward_amount'],
            'status' => 'available',
            'created_at' => current_time('mysql')
         ],
@@ -370,9 +441,9 @@
      );
      // 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
      $referee_amount = $this->settings['referee_reward_type'] === 'percentage'
         ? $this->settings['referee_reward_amount']  // Store as percentage
         : $this->settings['referee_reward_amount'];  // Store as fixed amount
      $this->wpdb->insert(
         $this->rewards_table,
@@ -381,7 +452,7 @@
            'user_id' => $referral->referee_id,
            'reward_type' => 'referee',
            'amount' => $referee_amount,
            'reward_calculation' => $settings['referee_reward_type'],
            'reward_calculation' => $this->settings['referee_reward_type'],
            'status' => 'available',
            'created_at' => current_time('mysql')
         ],
@@ -998,9 +1069,8 @@
         return '';
      }
      $settings = $this->getRewardSettings();
      $reward_amount = $settings['referee_reward_amount'] ?? 20;
      $reward_type = $settings['referee_reward_type'] ?? 'percentage';
      $reward_amount = $this->settings['referee_reward_amount'] ?? 20;
      $reward_type = $this->settings['referee_reward_type'] ?? 'percentage';
      $reward_text = $reward_type === 'percentage'
         ? $reward_amount . '% off'
@@ -1036,7 +1106,7 @@
      return ob_get_clean();
   }
   function getLoggedInReferral(int $user_id):string
   public function getLoggedInReferral(int $user_id):string
   {
      // Logged-in user widget
      $referral_code = get_user_meta($user_id, BASE . 'referral_code', true);
@@ -1049,7 +1119,7 @@
         }
      }
      $share_url = home_url('/?ref=' . $referral_code);
      $share_url = $this->getShareURL($referral_code);
      ob_start();
      ?>
@@ -1069,14 +1139,14 @@
            target="_blank"
            rel="noopener noreferrer"
            class="share-btn facebook-share">
            <?php echo jvbIcon('facebook', ['size' => 20]); ?>
            <?php echo jvbIcon('facebook-logo', ['size' => 20]); ?>
            Facebook
         </a>
         <a href="https://twitter.com/intent/tweet?url=<?php echo urlencode($share_url); ?>&text=<?php echo urlencode('Check this out!'); ?>"
            target="_blank"
            rel="noopener noreferrer"
            class="share-btn twitter-share">
            <?php echo jvbIcon('twitter', ['size' => 20]); ?>
            <?php echo jvbIcon('twitter-logo', ['size' => 20]); ?>
            Twitter
         </a>
      </div>
@@ -1129,27 +1199,52 @@
         return $content;
      }
      $settings = get_option(BASE . 'referral_settings', []);
      $reward_amount = $settings['referee_reward_amount'] ?? 20;
      $reward_type = $settings['referee_reward_type'] ?? 'percentage';
      $reward_amount = $this->settings['referee_reward_amount'] ?? 20;
      $reward_type = $this->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>';
      $bonus_content = sprintf(
         '
         <h3>Thanks for the â™¡</h3>
         <p>Since you were referred by a friend, you\'ve earned <strong>%s</strong> your first booking!</p>
         <p>Your reward will be automatically applied when you book.</p>',
         $reward_text,
      );
      // Insert bonus content after the first paragraph
      $parts = explode('</p>', $content, 2);
      if (count($parts) === 2) {
         return $parts[0] . '</p>' . $bonus_content . $parts[1];
      $code = $this->getUserReferralCode($user->ID);
      $yourCode = '';
      if (!is_wp_error($code)) {
         $share_url = $this->getShareURL($code);
         $yourCode = sprintf(
            '<div class="callout">
         <h3>Share the â™¡ with Friends</h3>
         <p>If you find you love what we can do for you, you can share your own code!</p>
         <p>Your Referral Code: <strong>%s</strong></p>
         <p>Or click the button below:</p>
         %s
         </div>',
            jvbEmailLink($code),
            jvbMailButton($share_url, 'Share Your Code')
         );
      }
      return $content . $bonus_content;
      return $content . $bonus_content . $yourCode;
   }
   public function getShareURL(string $code):string
   {
      return add_query_arg(
         [
            'ref' => $code,
            'action' => 'register'
         ],
         wp_login_url()
      );
   }
   /**
@@ -1197,10 +1292,9 @@
      }
      // Get reward text for email
      $settings = $this->getRewardSettings();
      $reward_text = $settings['referee_reward_type'] === 'percentage'
         ? "Get {$settings['referee_reward_amount']}% off your first treatment!"
         : "Get \${$settings['referee_reward_amount']} off your first treatment!";
      $reward_text = $this->settings['referee_reward_type'] === 'percentage'
         ? "Get {$this->settings['referee_reward_amount']}% off your first treatment!"
         : "Get \${$this->settings['referee_reward_amount']} off your first treatment!";
      // Record the invitation attempt (for tracking)
      $this->recordInvitationAttempt($user_id, $invitee_email, $invitee_name);
@@ -1555,9 +1649,6 @@
         echo '<div class="notice notice-success is-dismissible"><p>Settings saved successfully.</p></div>';
      }
      $referral_page_id = $this->getReferralPageId();
      $settings = $this->getRewardSettings();
      echo $this->renderAdminHTML();
   }
@@ -1585,7 +1676,7 @@
                        wp_dropdown_pages([
                           'name' => BASE . 'referral_page_id',
                           'id' => BASE . 'referral_page_id',
                           'selected' => $referral_page_id,
                           'selected' => $this->referralPage,
                           'show_option_none' => __('— Select â€”', 'jvbase'),
                           'option_none_value' => '0'
                        ]);
@@ -1611,8 +1702,8 @@
                     </th>
                     <td>
                        <select name="referrer_reward_type" id="referrer_reward_type">
                           <option value="fixed" <?php selected($settings['referrer_reward_type'], 'fixed'); ?>>Fixed Amount</option>
                           <option value="percentage" <?php selected($settings['referrer_reward_type'], 'percentage'); ?>>Percentage</option>
                           <option value="fixed" <?php selected($this->settings['referrer_reward_type'], 'fixed'); ?>>Fixed Amount</option>
                           <option value="percentage" <?php selected($this->settings['referrer_reward_type'], 'percentage'); ?>>Percentage</option>
                        </select>
                     </td>
                  </tr>
@@ -1624,7 +1715,7 @@
                        <input type="number"
                              name="referrer_reward_amount"
                              id="referrer_reward_amount"
                              value="<?= esc_attr($settings['referrer_reward_amount']) ?>"
                              value="<?= esc_attr($this->settings['referrer_reward_amount']) ?>"
                              step="0.01"
                              min="0">
                        <p class="description">Amount in dollars or percentage</p>
@@ -1636,8 +1727,8 @@
                     </th>
                     <td>
                        <select name="referrer_reward_applies_to" id="referrer_reward_applies_to">
                           <option value="per_user" <?php selected($settings['referrer_reward_applies_to'], 'per_user'); ?>>Per User Referred</option>
                           <option value="flat_total" <?php selected($settings['referrer_reward_applies_to'], 'flat_total'); ?>>Flat Total</option>
                           <option value="per_user" <?php selected($this->settings['referrer_reward_applies_to'], 'per_user'); ?>>Per User Referred</option>
                           <option value="flat_total" <?php selected($this->settings['referrer_reward_applies_to'], 'flat_total'); ?>>Flat Total</option>
                        </select>
                     </td>
                  </tr>
@@ -1651,8 +1742,8 @@
                     </th>
                     <td>
                        <select name="referee_reward_type" id="referee_reward_type">
                           <option value="percentage" <?php selected($settings['referee_reward_type'], 'percentage'); ?>>Percentage</option>
                           <option value="fixed" <?php selected($settings['referee_reward_type'], 'fixed'); ?>>Fixed Amount</option>
                           <option value="percentage" <?php selected($this->settings['referee_reward_type'], 'percentage'); ?>>Percentage</option>
                           <option value="fixed" <?php selected($this->settings['referee_reward_type'], 'fixed'); ?>>Fixed Amount</option>
                        </select>
                     </td>
                  </tr>
@@ -1664,7 +1755,7 @@
                        <input type="number"
                              name="referee_reward_amount"
                              id="referee_reward_amount"
                              value="<?= esc_attr($settings['referee_reward_amount']) ?>"
                              value="<?= esc_attr($this->settings['referee_reward_amount']) ?>"
                              step="0.01"
                              min="0">
                        <p class="description">Amount in dollars or percentage</p>
@@ -1676,8 +1767,8 @@
                     </th>
                     <td>
                        <select name="referee_reward_applies_to" id="referee_reward_applies_to">
                           <option value="first_order" <?php selected($settings['referee_reward_applies_to'], 'first_order'); ?>>First Order Only</option>
                           <option value="all_orders" <?php selected($settings['referee_reward_applies_to'], 'all_orders'); ?>>All Orders</option>
                           <option value="first_order" <?php selected($this->settings['referee_reward_applies_to'], 'first_order'); ?>>First Order Only</option>
                           <option value="all_orders" <?php selected($this->settings['referee_reward_applies_to'], 'all_orders'); ?>>All Orders</option>
                        </select>
                     </td>
                  </tr>
@@ -1744,16 +1835,14 @@
         return;
      }
      $referral_page_id = $this->getReferralPageId();
      if (!$referral_page_id) {
      if (!$this->referralPage) {
         return;
      }
      global $pagenow, $post;
      // Check if we're editing the referral page
      if ('post.php' === $pagenow && $post && $post->ID === $referral_page_id) {
      if ('post.php' === $pagenow && $post && $post->ID === $this->referralPage) {
         $wp_admin_bar->add_node([
            'id' => 'referral-page',
            'parent' => 'top-secondary',
@@ -1787,9 +1876,7 @@
         return;
      }
      $referral_page_id = $this->getReferralPageId();
      if ($post->ID === $referral_page_id) {
      if ($post->ID === $this->referralPage) {
         echo '<div class="notice notice-info">';
         echo '<p>' . __('This page is designated as the <strong>Referral Page</strong>.', 'jvbase') . '</p>';
         echo '</div>';