| | |
| | | use JVBase\base\Site; |
| | | use JVBase\importers\JaneAppClientImporter; |
| | | use JVBase\importers\JaneAppSalesImporter; |
| | | use JVBase\managers\CustomTable; |
| | | use JVBase\Instance; |
| | | use JVBase\managers\Cache; |
| | | use JVBase\managers\queue\Queue; |
| | | use JVBase\managers\Referrals; |
| | | use JVBase\rest\Rest; |
| | | use JVBase\rest\Route; |
| | | use WP_REST_Request; |
| | | use WP_REST_Response; |
| | | use WP_Error; |
| | | |
| | | if (!defined('ABSPATH')) { |
| | | exit; |
| | |
| | | */ |
| | | class ReferralRoutes extends Rest |
| | | { |
| | | use Instance; |
| | | public function __construct() |
| | | { |
| | | $this->cacheName = 'referrals'; |
| | | $this->cacheTtl = (int)HOUR_IN_SECONDS; |
| | | parent::__construct(); |
| | | |
| | | add_filter(BASE.'handle_bulk_operation', [$this, 'processOperation'], 10, 3); |
| | | $this->cache = Cache::for($this->cacheName, 'custom', $this->cacheTtl)->user(); |
| | | } |
| | | |
| | | public function registerRoutes(): void |
| | |
| | | Route::for('referrals') |
| | | ->get([$this, 'getReferrals']) |
| | | ->args([ |
| | | 'status' => 'string|enum:all,pending,consulted,treated,unused,registered,completed|default:all', |
| | | 'status' => 'string|enum:all,invited,pending,consulted,treated,cancelled|default:all', |
| | | 'date_start' => 'string', |
| | | 'date_end' => 'string', |
| | | 'limit' => 'integer|default:50', |
| | |
| | | return $this->getAllReferrals($request); |
| | | } |
| | | |
| | | // Build cache key |
| | | $args = [ |
| | | 'status' => $request->get_param('status') ?? 'all', |
| | | 'limit' => $request->get_param('limit') ?? 50, |
| | | 'offset' => $request->get_param('offset') ?? 0, |
| | | 'date_start' => $request->get_param('date_start'), |
| | | 'date_end' => $request->get_param('date_end'), |
| | | ]; |
| | | $cache_key = "user_{$user_id}_" . $this->cache->generateKey($args); |
| | | |
| | | // Check 304 Not Modified |
| | | $cache_check = $this->checkHeaders($request, $cache_key); |
| | | if ($cache_check instanceof WP_REST_Response) { |
| | | return $cache_check; |
| | | } |
| | | $args = $this->buildArgs($request); |
| | | |
| | | // Get referrals from manager |
| | | $referrals = JVB()->referrals()->getUserReferrals($user_id, $args); |
| | | $referrals = Referrals::getInstance()->getUserReferrals($user_id, $args); |
| | | |
| | | $data = [ |
| | | 'items' => $referrals, |
| | | 'total' => count($referrals) |
| | | ]; |
| | | |
| | | $response = $this->success($data); |
| | | $response = $this->success($referrals); |
| | | return $this->addCacheHeaders($response); |
| | | } |
| | | |
| | |
| | | return $this->unauthorized(); |
| | | } |
| | | |
| | | //Additional check to not send too many emails in an hour |
| | | $transient_key = "referral_invite_limit_{$user}"; |
| | | $recent_invites = get_transient($transient_key) ?: 0; |
| | | |
| | | if ($recent_invites >= 20) { // Max 5 batch invites per hour |
| | | return $this->error('Too many invitations sent. Please try again later.', 'rate_limit', 429); |
| | | } |
| | | set_transient($transient_key, $recent_invites + 1, HOUR_IN_SECONDS); |
| | | |
| | | $subject = sanitize_text_field($request->get_param('subject')); |
| | | $message = sanitize_textarea_field($request->get_param('message')); |
| | | $subject = sanitize_text_field($request->get_param('subject')??''); |
| | | $message = sanitize_textarea_field($request->get_param('message')??''); |
| | | $invitations = $request->get_param('invite'); |
| | | |
| | | // Validate and sanitize invitations |
| | |
| | | } |
| | | |
| | | $operationID = sanitize_text_field($request->get_param('id')); |
| | | JVB()->queue()->queueOperation( |
| | | Queue::getInstance()->queueOperation( |
| | | 'referral_invite', |
| | | $user, |
| | | [ |
| | |
| | | return $this->error('referral_id required', 'missing_id', 400); |
| | | } |
| | | |
| | | $result = JVB()->referrals()->updateStatus($referral_id, $status); |
| | | [$success, $message] = Referrals::getInstance()->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); |
| | | if (!$success) { |
| | | return $message === 'Referral not found' |
| | | ? $this->notFound($message) |
| | | : $this->error($message); |
| | | } |
| | | |
| | | $this->cache->flush(); |
| | |
| | | return $this->error('referral_id required', 'missing_id', 400); |
| | | } |
| | | |
| | | $result = JVB()->referrals()->removeReferral($referral_id, get_current_user_id()); |
| | | $result = Referrals::getInstance()->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); |
| | | if (!$result['success']) { |
| | | return $this->error($result['message']); |
| | | } |
| | | |
| | | $this->cache->flush(); |
| | | return $this->success(['message' => 'Referral removed']); |
| | | } |
| | | |
| | |
| | | return $this->error('referral_id required', 'missing_id', 400); |
| | | } |
| | | |
| | | $result = JVB()->referrals()->resendInvitation($referral_id, get_current_user_id()); |
| | | $result = Referrals::getInstance()->resendInvitation($referral_id, get_current_user_id()); |
| | | |
| | | if (is_wp_error($result)) { |
| | | $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); |
| | | if (!$result['success']) { |
| | | return $this->error($result['message']); |
| | | } |
| | | |
| | | return $this->success(['message' => 'Invitation resent']); |
| | |
| | | return $this->unauthorized(); |
| | | } |
| | | |
| | | $code = JVB()->referrals()->getUserReferralCode($user_id); |
| | | $code = Referrals::getInstance()->getUserReferralCode($user_id); |
| | | |
| | | if (is_wp_error($code)) { |
| | | return $this->error($code->get_error_message(), 'code_error', 400); |
| | |
| | | */ |
| | | public function validateCode(WP_REST_Request $request): WP_REST_Response |
| | | { |
| | | $code = strtoupper(sanitize_text_field($request->get_param('code'))); |
| | | $code = strtoupper(sanitize_text_field($request->get_param('code')??'')); |
| | | |
| | | if (empty($code)) { |
| | | return $this->error('Code required', 'missing_code', 400); |
| | | } |
| | | |
| | | $referrer = JVB()->referrals()->getUserByReferralCode($code); |
| | | $referrer = Referrals::getInstance()->getUserByReferralCode($code); |
| | | |
| | | if (!$referrer) { |
| | | return $this->error('Invalid referral code', 'invalid_code', 404); |
| | |
| | | |
| | | // Check self-referral |
| | | if (is_user_logged_in() && get_current_user_id() === $referrer->ID) { |
| | | return $this->error('Cannot use your own referral code', 'self_referral', 400); |
| | | return $this->error('Cannot use your own referral code', 'self_referral'); |
| | | } |
| | | |
| | | return $this->success([ |
| | |
| | | if (!$user_id) { |
| | | return $this->unauthorized(); |
| | | } |
| | | $cache_key = "stats_{$user_id}"; |
| | | |
| | | // Check 304 Not Modified |
| | | $cache_check = $this->checkHeaders($request, $cache_key); |
| | | if ($cache_check instanceof WP_REST_Response) { |
| | | return $cache_check; |
| | | } |
| | | $stats = Referrals::getInstance()->getUserStats($user_id); |
| | | |
| | | $stats = JVB()->referrals()->getUserStats($user_id); |
| | | |
| | | $response = $this->success(['items' => [$stats]]); |
| | | return $this->addCacheHeaders($response); |
| | | return $this->success(['items' => [$stats]]); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public function getSettings(WP_REST_Request $request): WP_REST_Response |
| | | { |
| | | $settings = JVB()->referrals()->getRewardSettings(); |
| | | $settings = Referrals::getInstance()->getRewardSettings(); |
| | | return $this->success(['settings' => $settings]); |
| | | } |
| | | |
| | |
| | | /** |
| | | * Helper: Get all referrals (admin only) |
| | | */ |
| | | protected function buildArgs(WP_REST_Request $request):array |
| | | { |
| | | $params = $request->get_params(); |
| | | $args = [ |
| | | 'limit' => absint($params['limit']??20), |
| | | 'offset' => absint($params['offset']??0) |
| | | ]; |
| | | if (array_key_exists('status', $params) && $params['status'] !== 'all') { |
| | | $args['status'] = sanitize_text_field($params['status']); |
| | | } |
| | | if (array_key_exists('search', $params)) { |
| | | $args['search'] = sanitize_text_field($params['search']); |
| | | } |
| | | if (array_key_exists('date_start', $params) && array_key_exists('date_end', $params)) { |
| | | $args['created_at'] = ['BETWEEN', [sanitize_text_field($params['date_start']), sanitize_text_field($params['date_end'])]]; |
| | | } |
| | | return $args; |
| | | } |
| | | protected function getAllReferrals(WP_REST_Request $request): WP_REST_Response |
| | | { |
| | | $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, |
| | | ]); |
| | | $args = $this->buildArgs($request); |
| | | $items = Referrals::getInstance()->getReferrals($args); |
| | | |
| | | return $this->success(['items' => $items, 'total' => count($items)]); |
| | | return $this->success($items); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Process queued referral operations |
| | | */ |
| | | public function processOperation(WP_Error|array $result, object $operation, array $data): array|WP_Error |
| | | { |
| | | if ($operation->type !== 'referral_invite') { |
| | | return $result; |
| | | } |
| | | |
| | | $result = JVB()->referrals()->sendBatchReferralInvitations( |
| | | $operation->user_id, |
| | | $data['invitations'], |
| | | $data['subject'], |
| | | $data['message'] |
| | | ); |
| | | |
| | | if ($result['success']) { |
| | | $this->cache->flush(); |
| | | } |
| | | |
| | | return [ |
| | | 'success' => true, |
| | | 'message' => sprintf( |
| | | 'Sent invitations. Success: %d. Failed: %d.', |
| | | count($result['result']['success']), |
| | | count($result['result']['failed']) |
| | | ), |
| | | 'details' => [ |
| | | 'successful' => $result['result']['success'], |
| | | 'failed' => $result['result']['failed'], |
| | | 'total' => count($data['invitations']) |
| | | ] |
| | | ]; |
| | | } |
| | | |
| | | /** |
| | | * Handle client CSV upload |
| | | */ |
| | | public function handleClientUpload(WP_REST_Request $request): WP_REST_Response |
| | |
| | | |
| | | // Import using JaneAppClientImporter |
| | | $importer = new JaneAppClientImporter(); |
| | | $default_role = get_option(BASE . 'referral_role', Site::getDefaultReferralRole()); |
| | | $default_role = get_option(BASE . 'referral_role', Site::getReferralRole()); |
| | | |
| | | $options = [ |
| | | 'update_existing' => true, |