flag('general/enabled', $storeId); } public function registrationPoints(?int $storeId = null): int { return (int)$this->earningRule('registration', $storeId)['points']; } public function firstAddressPoints(?int $storeId = null): int { return (int)$this->earningRule('first_shipping_address', $storeId)['points']; } public function orderRate(?int $storeId = null): float { return (float)$this->earningRule('order_earned', $storeId)['points']; } public function reviewTextPoints(?int $storeId = null): int { return (int)$this->earningRule('review_text', $storeId)['points']; } public function reviewMediaPoints(?int $storeId = null): int { return (int)$this->earningRule('review_media', $storeId)['points']; } public function socialPoints(?int $storeId = null): int { return (int)$this->earningRule('social_share', $storeId)['points']; } public function redemptionRate(?int $storeId = null): int { return max(1, $this->int('spending/points_per_dollar', $storeId)); } public function maxCheckoutPoints(?int $storeId = null): int { return $this->int('spending/max_checkout_points', $storeId); } public function reviewEligibility(?int $storeId = null): string { return (string)$this->value('review/eligibility', $storeId); } public function silverThreshold(?int $storeId = null): float { return $this->float('tiers/silver_threshold', $storeId); } public function silverMultiplier(?int $storeId = null): float { return $this->float('tiers/silver_multiplier', $storeId); } public function goldThreshold(?int $storeId = null): float { return $this->float('tiers/gold_threshold', $storeId); } public function goldMultiplier(?int $storeId = null): float { return $this->float('tiers/gold_multiplier', $storeId); } /** @return array{code: string, points: float, name: string, reason: string} */ public function earningRule(string $code, ?int $storeId = null): array { $rules = $this->earningRules($storeId); return $rules[$code] ?? ['code' => $code, 'points' => 0.0, 'name' => ucwords(str_replace('_', ' ', $code)), 'reason' => '']; } /** @return array */ public function earningRules(?int $storeId = null): array { $defaults = [ 'registration' => ['points' => (float)$this->int('earning/registration_points', $storeId), 'name' => 'Account Registration', 'reason' => 'Account registration reward'], 'first_shipping_address' => ['points' => (float)$this->int('earning/first_address_points', $storeId), 'name' => 'First Shipping Address', 'reason' => 'First saved shipping address reward'], 'order_earned' => ['points' => $this->float('earning/points_per_dollar', $storeId), 'name' => 'Order Reward', 'reason' => 'Points earned from order %1'], 'review_text' => ['points' => (float)$this->int('earning/review_text_points', $storeId), 'name' => 'Product Review', 'reason' => 'Approved product review reward'], 'review_media' => ['points' => (float)$this->int('earning/review_media_points', $storeId), 'name' => 'Product Review with Media', 'reason' => 'Approved product review reward'], 'social_share' => ['points' => (float)$this->int('earning/social_share_points', $storeId), 'name' => 'Social Share', 'reason' => 'Social share reward'], ]; foreach ($defaults as $code => &$rule) { $rule['code'] = $code; } unset($rule); $value = $this->value('earning/items', $storeId); try { $items = is_array($value) ? $value : (empty($value) ? [] : $this->serializer->unserialize((string)$value)); } catch (\InvalidArgumentException) { $items = []; } if (!is_array($items) || !$items) { return $defaults; } $rules = []; foreach ($items as $item) { if (!is_array($item)) { continue; } $code = strtolower(trim((string)($item['code'] ?? ''))); $name = trim((string)($item['name'] ?? '')); if ($code === '' || $name === '') { continue; } $rules[$code] = [ 'code' => $code, 'points' => max(0.0, (float)($item['points'] ?? 0)), 'name' => $name, 'reason' => trim((string)($item['reason'] ?? '')), ]; } return $rules ?: $defaults; } /** @return array */ public function tiers(?int $storeId = null): array { $value = $this->value('tiers/items', $storeId); try { $items = is_array($value) ? $value : (empty($value) ? [] : $this->serializer->unserialize((string)$value)); } catch (\InvalidArgumentException) { $items = []; } if (!is_array($items)) { $items = []; } $tiers = []; foreach ($items as $item) { if (!is_array($item)) { continue; } $label = trim((string)($item['label'] ?? '')); $threshold = max(0.0, (float)($item['threshold'] ?? 0)); $multiplier = (float)($item['multiplier'] ?? 0); if ($label === '' || $multiplier <= 0) { continue; } $tiers[] = [ 'code' => strtolower((string)preg_replace('/[^a-z0-9]+/i', '_', $label)), 'label' => $label, 'threshold' => $threshold, 'multiplier' => $multiplier, ]; } if (!$tiers) { $tiers = [ ['code' => 'bronze', 'label' => 'Bronze Maker', 'threshold' => 0.0, 'multiplier' => 1.0], ['code' => 'silver', 'label' => 'Silver Maker', 'threshold' => $this->silverThreshold($storeId), 'multiplier' => $this->silverMultiplier($storeId)], ['code' => 'gold', 'label' => 'Gold Maker', 'threshold' => $this->goldThreshold($storeId), 'multiplier' => $this->goldMultiplier($storeId)], ]; } usort($tiers, static fn(array $left, array $right): int => $left['threshold'] <=> $right['threshold']); return $tiers; } private function value(string $path, ?int $storeId): mixed { return $this->scopeConfig->getValue('esun_reward/' . $path, ScopeInterface::SCOPE_STORE, $storeId); } private function int(string $path, ?int $storeId): int { return max(0, (int)$this->value($path, $storeId)); } private function float(string $path, ?int $storeId): float { return max(0.0, (float)$this->value($path, $storeId)); } private function flag(string $path, ?int $storeId): bool { return $this->scopeConfig->isSetFlag('esun_reward/' . $path, ScopeInterface::SCOPE_STORE, $storeId); } }