converter = $converter; $this->historyService = $historyService; $this->settingService = $settingService; } public function process( string $url, ?string $zaloThreadId = null, ?string $zaloChatName = null, string $sourceType = 'web', ?string $customerName = null, ?string $customerPhone = null ): AffiliateResult { // 1. Generate unique code beforehand for tracking $code = null; do { $code = \Illuminate\Support\Str::random(10); } while (\App\Models\ConversionHistory::whereRaw('BINARY code = ?', [$code])->exists()); // 2. Fetch real Shopee product information $productInfo = $this->fetchShopeeProductInfo($url); $productTitle = $productInfo['name'] ?? null; $productImage = $productInfo['image'] ?? null; $productPrice = $productInfo['price'] ?? 0.00; // Determine estimated commission rate and amount $commissionData = $this->calculateEstimatedCommission($productTitle, $productPrice, $productInfo['is_mall'] ?? false); $estimatedCommission = $commissionData['amount']; $maxRetries = $this->settingService->get('retry_count', 3); $attempts = 0; $result = null; while ($attempts < $maxRetries) { $attempts++; // 3. Convert with the pre-generated code for sub_id mapping $result = $this->converter->convert($url, $code); if ($result->isSuccess) { break; } } // Save history regardless of success or failure if ($result) { $history = $this->historyService->logConversion( $result, $code, $zaloThreadId, $zaloChatName, $customerName, $customerPhone, $productTitle, $productImage, $productPrice, $estimatedCommission ); // Override converter type if triggered from external source (like Zalo) if ($history && $sourceType !== 'web') { $history->update([ 'converter_type' => $sourceType ]); } if ($result->isSuccess && $history->code) { $domain = $this->settingService->get('short_link_domain'); if ($domain) { $domain = rtrim($domain, '/'); $result->affiliateUrl = $domain . '/aff/s/' . $history->code; } else { $result->affiliateUrl = route('short_link.redirect', ['code' => $history->code]); } } } return $result; } /** * Fetch product details (title, image, price) from Shopee internal API. */ public function fetchShopeeProductInfo(string $url): ?array { if (!preg_match('/i\.(\d+)\.(\d+)/', $url, $matches)) { return null; } $shopId = $matches[1]; $itemId = $matches[2]; $apiUrl = "https://shopee.vn/api/v4/item/get?itemid={$itemId}&shopid={$shopId}"; try { $response = \Illuminate\Support\Facades\Http::timeout(5) ->withHeaders([ 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', 'Accept' => 'application/json', 'Referer' => 'https://shopee.vn/', ]) ->get($apiUrl); if ($response->successful()) { $data = $response->json(); $item = $data['data'] ?? null; if ($item) { $price = isset($item['price']) ? $item['price'] / 100000 : 0; $image = isset($item['image']) ? "https://down-vn.img.susercontent.com/file/" . $item['image'] : null; return [ 'name' => $item['name'] ?? null, 'price' => $price, 'image' => $image, 'is_mall' => !empty($item['is_official_shop']), 'description' => $item['description'] ?? null ]; } } } catch (\Exception $e) { \Illuminate\Support\Facades\Log::warning("Shopee internal API lookup failed: " . $e->getMessage()); } return null; } /** * Calculate estimated commission based on category keywords and Mall status. */ protected function calculateEstimatedCommission(?string $title, float $price, bool $isMall): array { if (!$title || $price <= 0) { return ['rate' => 0, 'amount' => 0.00]; } $titleLower = mb_strtolower($title); // Define category keywords $fashionKeywords = ['áo', 'quần', 'váy', 'đầm', 'giày', 'túi', 'kính', 'son', 'kem', 'phấn', 'nước hoa', 'tắm', 'gội', 'phụ kiện', 'trang sức', 'skincare', 'scrub']; $electronicsKeywords = ['điện thoại', 'laptop', 'tivi', 'loa', 'tai nghe', 'máy ảnh', 'sạc', 'cáp', 'chuột', 'bàn phím', 'camera', 'micro', 'usb', 'thẻ nhớ', 'router', 'wifi', 'pin']; $isFashion = false; foreach ($fashionKeywords as $keyword) { if (str_contains($titleLower, $keyword)) { $isFashion = true; break; } } $isElectronics = false; if (!$isFashion) { foreach ($electronicsKeywords as $keyword) { if (str_contains($titleLower, $keyword)) { $isElectronics = true; break; } } } // Apply rates if ($isFashion) { $rate = $isMall ? 7.4 : 3.5; } elseif ($isElectronics) { $rate = $isMall ? 1.5 : 0.8; } else { // General categories $rate = $isMall ? 5.0 : 2.5; } // Allow admin settings override if configured $adminOverrideRate = $this->settingService->get('commission_rate_override'); if ($adminOverrideRate !== null && $adminOverrideRate !== '') { $rate = (float)$adminOverrideRate; } return [ 'rate' => $rate, 'amount' => round($price * ($rate / 100), 2) ]; } }