Jake Vanderwerf
4 days ago 747d741293e064a979d7bf6c143ef969ea6d7629
inc/rest/routes/ReferralRoutes.php
@@ -20,20 +20,12 @@
 */
class ReferralRoutes extends Rest
{
   protected CustomTable $referrals;
   protected CustomTable $rewards;
   protected CustomTable $treatments;
   public function __construct()
   {
      $this->cacheName = 'referrals';
      $this->cacheTtl = (int)HOUR_IN_SECONDS;
      parent::__construct();
      $this->referrals = CustomTable::for('referrals');
      $this->rewards = CustomTable::for('referral_rewards');
      $this->treatments = CustomTable::for('referral_treatments');
      add_filter(BASE.'handle_bulk_operation', [$this, 'processOperation'], 10, 3);
   }
@@ -235,33 +227,17 @@
         return $this->error('referral_id required', 'missing_id', 400);
      }
      // Get referral using CustomTable
      $referral = $this->referrals->get(['id' => $referral_id]);
      if (!$referral) {
         return $this->notFound('Referral not found');
      $result = JVB()->referrals()->updateStatus($referral_id, $status);
      if (is_wp_error($result)) {
         $code = $result->get_error_code();
         return $code === 'not_found'
            ? $this->notFound($result->get_error_message())
            : $this->error($result->get_error_message(), $code, 500);
      }
      // Update status
      $update_data = ['status' => $status];
      $update_data["{$status}_at"] = current_time('mysql');
      if ($status === 'treated') {
         $update_data['treatment_count'] = ($referral->treatment_count ?? 0) + 1;
      }
      $updated = $this->referrals->update($update_data, ['id' => $referral_id]);
      if ($updated !== false) {
         // Create rewards if treated
         if ($status === 'treated') {
            $this->createRewards($referral);
         }
         $this->cache->flush();
         return $this->success(['message' => "Referral marked as {$status}"]);
      }
      return $this->error('Failed to update referral', 'update_failed', 500);
      $this->cache->flush();
      return $this->success(['message' => "Referral marked as {$status}"]);
   }
   /**
@@ -274,26 +250,20 @@
         return $this->error('referral_id required', 'missing_id', 400);
      }
      // Get referral
      $referral = $this->referrals->get(['id' => $referral_id]);
      if (!$referral) {
         return $this->notFound('Referral not found');
      $result = JVB()->referrals()->removeReferral($referral_id, get_current_user_id());
      if (is_wp_error($result)) {
         $code = $result->get_error_code();
         $status = match($code) {
            'not_found'      => 404,
            'unauthorized'   => 403,
            'invalid_status' => 400,
            default          => 500
         };
         return $this->error($result->get_error_message(), $code, $status);
      }
      // Check ownership
      $current_user_id = get_current_user_id();
      if ($referral->referrer_id != $current_user_id && !current_user_can('manage_options')) {
         return $this->forbidden('Unauthorized');
      }
      // Can only remove pending referrals
      if ($referral->status !== 'pending') {
         return $this->error('Can only remove pending referrals', 'invalid_status', 400);
      }
      $this->referrals->delete(['id' => $referral_id]);
      $this->cache->flush();
      return $this->success(['message' => 'Referral removed']);
   }
@@ -307,44 +277,18 @@
         return $this->error('referral_id required', 'missing_id', 400);
      }
      $current_user_id = get_current_user_id();
      // Get referral with ownership check
      $referral = $this->referrals->where([
         'id' => $referral_id,
         'referrer_id' => $current_user_id
      ])->first();
      if (!$referral) {
         return $this->notFound('Referral not found');
      }
      // Check rate limit (once per week)
      $transient_key = 'referral_last_invite_' . md5($referral->referee_email);
      $last_invite = get_transient($transient_key);
      if ($last_invite && (time() - $last_invite) < WEEK_IN_SECONDS) {
         return $this->error(
            'Can only resend once per week',
            'rate_limit',
            429
         );
      }
      // Resend via referral manager
      $result = JVB()->referrals()->sendReferralInvitation(
         $current_user_id,
         $referral->referee_email,
         $referral->referee_name,
         sprintf('Reminder: Join %s', get_bloginfo('name')),
         'Just a friendly reminder about my invitation!'
      );
      $result = JVB()->referrals()->resendInvitation($referral_id, get_current_user_id());
      if (is_wp_error($result)) {
         return $this->error($result->get_error_message(), 'send_failed', 500);
         $code = $result->get_error_code();
         $status = match($code) {
            'not_found'  => 404,
            'rate_limit' => 429,
            default      => 500
         };
         return $this->error($result->get_error_message(), $code, $status);
      }
      set_transient($transient_key, time(), WEEK_IN_SECONDS);
      return $this->success(['message' => 'Invitation resent']);
   }
@@ -461,90 +405,19 @@
    */
   protected function getAllReferrals(WP_REST_Request $request): WP_REST_Response
   {
      global $wpdb;
      $where = ['1=1'];
      $where_params = [];
      // Build WHERE conditions
      $status = $request->get_param('status');
      if ($status && $status !== 'all') {
         $where[] = 'status = %s';
         $where_params[] = $status;
      }
      if ($date_start = $request->get_param('date_start')) {
         $where[] = 'referred_at >= %s';
         $where_params[] = $date_start;
      }
      if ($date_end = $request->get_param('date_end')) {
         $where[] = 'referred_at <= %s';
         $where_params[] = $date_end;
      }
      $search = $request->get_param('search');
      if (!empty($search)) {
         $search_term = '%' . $wpdb->esc_like($search) . '%';
         $where[] = '(r.referee_name LIKE %s OR r.referee_email LIKE %s OR r.referral_code LIKE %s OR u.display_name LIKE %s)';
         $where_params[] = $search_term;
         $where_params[] = $search_term;
         $where_params[] = $search_term;
         $where_params[] = $search_term;
      }
      $limit = $request->get_param('limit') ?? 50;
      $offset = $request->get_param('offset') ?? 0;
      $where_params[] = $limit;
      $where_params[] = $offset;
      // Use CustomTable's query method
      $query = "SELECT r.*, u.display_name as referrer_name
         FROM {table} r
         LEFT JOIN {$wpdb->users} u ON r.referrer_id = u.ID
         WHERE " . implode(' AND ', $where) . "
         ORDER BY referred_at DESC
         LIMIT %d OFFSET %d";
      $items = $this->referrals->queryResults($query, $where_params);
      return $this->success([
         'items' => $items,
         'total' => count($items)
      ]);
   }
   /**
    * Helper: Create rewards for completed referral
    */
   protected function createRewards(object $referral): void
   {
      $settings = JVB()->referrals()->getRewardSettings();
      // Referrer reward
      $this->rewards->insert([
         'referral_id' => $referral->id,
         'user_id' => $referral->referrer_id,
         'reward_type' => 'referrer',
         'amount' => $settings['referrer_reward_amount'],
         'reward_calculation' => $settings['referrer_reward_type'],
         'status' => 'available'
      $items = JVB()->referrals()->getAllReferrals([
         'status'     => $request->get_param('status') ?? 'all',
         'search'     => $request->get_param('search'),
         'date_start' => $request->get_param('date_start'),
         'date_end'   => $request->get_param('date_end'),
         'limit'      => $request->get_param('limit') ?? 50,
         'offset'     => $request->get_param('offset') ?? 0,
      ]);
      // Referee reward
      if ($referral->referee_id) {
         $this->rewards->insert([
            'referral_id' => $referral->id,
            'user_id' => $referral->referee_id,
            'reward_type' => 'referee',
            'amount' => $settings['referee_reward_amount'],
            'reward_calculation' => $settings['referee_reward_type'],
            'status' => 'available'
         ]);
      }
      return $this->success(['items' => $items, 'total' => count($items)]);
   }
   /**
    * Process queued referral operations
    */