PATH:
home
/
cardxfeb
/
Editing: .bash_history
'messages' => [['role' => 'user', 'content' => $prompt]], ]); if (!$response->ok()) { Log::error('[LlmAnalyzer] API error: ' . $response->status() . ' - ' . $response->body()); return $this->fallbackResult($merged); } $text = $response->json('content.0.text', ''); // Strip markdown code blocks if present $text = preg_replace('/^```(?:json)?\s*/m', '', $text); $text = preg_replace('/\s*```$/m', '', $text); $text = trim($text); $data = json_decode($text, true); if (json_last_error() !== JSON_ERROR_NONE) { Log::warning('[LlmAnalyzer] Invalid JSON from LLM: ' . substr($text, 0, 200)); return $this->fallbackResult($merged); } // Sanitize template_id $data['template_id'] = max(1, min(24, (int)($data['template_id'] ?? 1))); // If no valid color from LLM, use detected brand color if (empty($data['color_primary']) || !preg_match('/^#[0-9a-fA-F]{3,6}$/', $data['color_primary'])) { $data['color_primary'] = $brandColors['theme_color'] ?? $brandColors['css_primary'] ?? '#2563EB'; } return $data; } catch (\Exception $e) { Log::error('[LlmAnalyzer] Exception: ' . $e->getMessage()); return $this->fallbackResult($merged); } } /** * Legacy single-source analyze (backward compatibility) */ public function analyze(array $scrapedData, string $region = 'BR'): array { // Wrap single source as multi-source format $wrapped = [ 'social' => [$scrapedData], 'corporate' => [], 'merged' => [ 'name' => $scrapedData['title'] ?? '', 'bio_hint' => $scrapedData['description'] ?? '', 'brand_name' => '', 'brand_desc' => '', 'brand_colors' => [], 'platforms' => [$scrapedData['platform'] ?? 'website'], 'sources' => 1, ], ]; return $this->analyzeMultiple($wrapped, $region); } private function fallbackResult(array $merged): array { $brandColor = $merged['brand_colors']['theme_color'] ?? $merged['brand_colors']['css_primary'] ?? '#2563EB'; return [ 'name' => $merged['name'] ?? 'Meu Carto Digital', 'profession' => '', 'bio' => $merged['bio_hint'] ?? '', 'company' => $merged['brand_name'] ?? '', 'color_primary' => $brandColor, 'color_secondary' => '#F59E0B', 'template_id' => 1, 'links' => [], 'social_handles' => [], 'brand_personality' => 'profissional', 'confidence' => 'low', ]; } } ''' with open(f"{base}/app/Services/LlmAnalyzerService.php", 'w') as f: f.write(llm) print(f"UPGRADED: LlmAnalyzerService.php ({len(llm)} chars)") # ============================================================ # 3. AiCardGeneratorService.php use fetchMultiple + analyzeMultiple # ============================================================ generator = r'''<?php namespace App\Services; use App\Models\Vcard; use App\Models\User; use Illuminate\Support\Facades\Log; class AiCardGeneratorService { public function __construct( protected SocialScraperService $scraper, protected LlmAnalyzerService $llm ) {} /** * Full AI generation pipeline with MULTI-SOURCE support. * $sources can be a single URL string or an array of URLs. */ public function generate(User $user, string|array $sources, Vcard $vcard): array { // Normalize to array if (is_string($sources)) { $sources = [$sources]; } $sources = array_filter($sources, fn($s) => !empty(trim($s))); try { // Step 1: Fetch from ALL sources in parallel (sequential for shared hosting) Log::info("[AiCardGen] Fetching " . count($sources) . " sources for user#{$user->id}"); $multiSourceData = $this->scraper->fetchMultiple($sources); $socialCount = count($multiSourceData['social'] ?? []); $corporateCount = count($multiSourceData['corporate'] ?? []); Log::info("[AiCardGen] Fetched: {$socialCount} social, {$corporateCount} corporate sources"); // Step 2: LLM multi-source analysis $region = config('app.region', 'BR'); $aiResult = $this->llm->analyzeMultiple($multiSourceData, $region); Log::info("[AiCardGen] LLM result: template_id={$aiResult['template_id']}, confidence={$aiResult['confidence']}"); // Step 3: Apply to VCard $updateData = [ 'ai_status' => 'completed', 'ai_data' => json_encode($aiResult, JSON_UNESCAPED_UNICODE), 'template_id' => $aiResult['template_id'], ]; // Only fill fields if VCard doesn't already have them if (empty($vcard->name) && !empty($aiResult['name'])) { $updateData['name'] = $aiResult['name']; } if (empty($vcard->description) && !empty($aiResult['bio'])) { $updateData['description'] = $aiResult['bio']; } if (empty($vcard->occupation) && !empty($aiResult['profession'])) { $updateData['occupation'] = $aiResult['profession']; } if (empty($vcard->company) && !empty($aiResult['company'])) { $updateData['company'] = $aiResult['company']; } $vcard->update($updateData); return [ 'success' => true, 'vcard_id' => $vcard->id, 'template_id' => $aiResult['template_id'], 'completion_pct' => 90, 'sources_used' => count($sources), 'confidence' => $aiResult['confidence'] ?? 'medium', 'ai_result' => $aiResult, ]; } catch (\Exception $e) { Log::error("[AiCardGen] Failed for user#{$user->id}: " . $e->getMessage()); $vcard->update(['ai_status' => 'failed']); return ['success' => false, 'vcard_id' => $vcard->id, 'error' => $e->getMessage()]; } } } ''' with open(f"{base}/app/Services/AiCardGeneratorService.php", 'w') as f: f.write(generator) print(f"UPGRADED: AiCardGeneratorService.php ({len(generator)} chars)") # ============================================================ # 4. OnboardingController.php sources[] array support # ============================================================ onboarding = r'''<?php namespace App\Http\Controllers; use App\Jobs\GenerateAiCardJob; use App\Models\Vcard; use Illuminate\Http\Request; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Log; use Illuminate\Validation\Rule; class OnboardingController extends Controller { /** Show the onboarding form */ public function create() { return view('vcard.onboarding'); } /** * Store: accept 15 source URLs, create VCard skeleton, dispatch AI job. * Input: sources[] array (or legacy social_url string) */ public function store(Request $request): \Illuminate\Http\RedirectResponse { // Support both legacy single URL and new multi-source array if ($request->has('social_url') && !$request->has('sources')) { $request->merge(['sources' => [$request->input('social_url')]]); } $request->validate([ 'sources' => ['required', 'array', 'min:1', 'max:5'], 'sources.*' => ['required', 'url', 'max:500', 'distinct'], ], [ 'sources.required' => 'Fornea pelo menos uma URL de rede social ou site.', 'sources.max' => 'Pode fornecer no mximo 5 fontes.', 'sources.*.url' => 'Uma das URLs fornecidas no vlida.', 'sources.*.distinct' => 'No repita URLs.', ]); $user = Auth::user(); $sources = array_filter($request->sources, fn($s) => !empty(trim($s))); // Determine primary URL for backward compatibility $primaryUrl = $sources[0] ?? ''; // Create VCard skeleton $vcard = Vcard::create([ 'user_id' => $user->id, 'tenant_id' => $user->tenant_id ?? null, 'name' => $user->name, 'url_alias' => 'card-' . uniqid(), 'status' => 'pending_payment', 'ai_status' => 'pending', 'ai_social_url' => json_encode(array_values($sources), JSON_UNESCAPED_SLASHES), 'payment_deadline' => now()->addHours(24), 'template_id' => 1, ]); Log::info("[Onboarding] VCard#{$vcard->id} created. Sources: " . implode(', ', $sources)); // Dispatch AI job with full sources array GenerateAiCardJob::dispatch($user, $sources, $vcard->id) ->onQueue('ai-generation'); return redirect()->route('vcard.processing', ['vcard' => $vcard->id]) ->with('message', count($sources) > 1 ? 'A IA est a analisar ' . count($sources) . ' fontes para criar o seu carto...' : 'A IA est a preparar o seu carto...' ); } /** Show processing screen */ public function processing(Vcard $vcard) { if ($vcard->user_id !== Auth::id()) { abort(403); } return view('vcard.processing', compact('vcard')); } /** API: Poll AI status */ public function aiStatus(Vcard $vcard): JsonResponse { if ($vcard->user_id !== Auth::id()) { return response()->json(['error' => 'Unauthorized'], 403); } $aiData = null; if ($vcard->ai_status === 'completed' && $vcard->ai_data) { $decoded = json_decode($vcard->ai_data, true); $aiData = [ 'template_id' => $decoded['template_id'] ?? null, 'color_primary' => $decoded['color_primary'] ?? null, 'confidence' => $decoded['confidence'] ?? null, 'brand_personality' => $decoded['brand_personality'] ?? null, ]; } return response()->json([ 'vcard_id' => $vcard->id, 'ai_status' => $vcard->ai_status, 'template_id' => $vcard->template_id, 'ai_data' => $aiData, 'redirect_url' => $vcard->ai_status === 'completed' ? route('user.vcard.edit', $vcard->id) : null, ]); } } ''' with open(f"{base}/app/Http/Controllers/OnboardingController.php", 'w') as f: f.write(onboarding) print(f"UPGRADED: OnboardingController.php ({len(onboarding)} chars)") # ============================================================ # 5. GenerateAiCardJob.php support array sources # ============================================================ job = r'''<?php namespace App\Jobs; use App\Models\Vcard; use App\Models\User; use App\Services\AiCardGeneratorService; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Log; class GenerateAiCardJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public int $tries = 3; public int $timeout = 180; // 3 min multi-source takes longer public int $backoff = 30; public function __construct( public User $user, public string|array $sources, // single URL or array of URLs public int $vcardId ) {} public function handle(AiCardGeneratorService $aiService): void { $vcard = Vcard::findOrFail($this->vcardId); $vcard->update(['ai_status' => 'processing']); $sourcesCount = is_array($this->sources) ? count($this->sources) : 1; Log::info("[GenerateAiCardJob] Starting: user#{$this->user->id}, vcard#{$this->vcardId}, {$sourcesCount} source(s)"); $result = $aiService->generate($this->user, $this->sources, $vcard); if ($result['success']) { Log::info("[GenerateAiCardJob] Done: template_id={$result['template_id']}, confidence={$result['confidence']}"); } else { Log::error("[GenerateAiCardJob] Failed: " . ($result['error'] ?? 'unknown')); $this->fail(new \Exception($result['error'] ?? 'AI generation failed')); } } public function failed(\Throwable $exception): void { Vcard::find($this->vcardId)?->update(['ai_status' => 'failed']); Log::error("[GenerateAiCardJob] Permanently failed: " . $exception->getMessage()); } } ''' with open(f"{base}/app/Jobs/GenerateAiCardJob.php", 'w') as f: f.write(job) print(f"UPGRADED: GenerateAiCardJob.php ({len(job)} chars)") print("\nAll service upgrades complete!") PYEOF python3 /tmp/phase33_llm_onboarding.py cat > /tmp/fix_csrf.py << 'PYEOF' import os, re base = os.path.expanduser('~/public_html') # Find VerifyCsrfToken middleware csrf_paths = [ f'{base}/app/Http/Middleware/VerifyCsrfToken.php', f'{base}/bootstrap/app.php', ] for p in csrf_paths: if os.path.exists(p): with open(p, 'r') as f: content = f.read() print(f'Found: {p}') print(content[:600]) print('---') PYEOF python3 /tmp/fix_csrf.py grep -n 'csrf\|VerifyCsrf\|webhook\|except' bootstrap/app.php app/Http/Middleware/*.php 2>/dev/null | head -30 cat app/Http/Middleware/VerifyCsrfToken.php python3 -c " import os p = os.path.expanduser('~/public_html/app/Http/Middleware/VerifyCsrfToken.php') with open(p,'r') as f: c = f.read() if 'webhooks/brasil' not in c: old = \"'/phonepe-Product-response'\" new = old + \",\n // === CARD AO SEGUNDO CONTROL webhooks ===\n '/api/webhooks/brasil',\n '/api/webhooks/portugal',\n '/webhooks/brasil',\n '/webhooks/portugal'\" c = c.replace(old, new, 1) with open(p,'w') as f: f.write(c) print('CSRF exceptions added') else: print('Already added') " php artisan config:clear && php artisan cache:clear && php artisan route:clear && php artisan view:clear && echo 'CACHE CLEARED' python3 -c " import re p = '/home/cardxfeb/public_html/.env' with open(p,'r') as f: c = f.read() new_key = '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' c2 = re.sub(r'CLAUDE_API_KEY=.*', 'CLAUDE_API_KEY=' + new_key, c) if c2 != c: with open(p,'w') as f: f.write(c2) print('KEY UPDATED OK') else: print('KEY NOT FOUND - check .env') " php artisan config:clear && echo 'CONFIG CLEARED' cd /home/cardxfeb/public_html && php artisan tinker --no-interaction 2>&1 & cd /home/cardxfeb/public_html && php artisan tinker python3 << 'PYEOF' import re # 1. Create HTTP validation file token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkb21haW4iOiJjYXJkLmFvc2VndW5kby5jb20uYnIiLCJleHAiOjE3Nzc2ODAwMDB9.nMfClhVIR-5_MO0NCaFuKzb-b12j2-yH2IaCtV2Qhnk' with open('/home/cardxfeb/public_html/simpledcver.txt', 'w') as f: f.write(token) print('1. simpledcver.txt CRIADO OK') # 2. Update ASAAS_WEBHOOK_TOKEN in .env env_path = '/home/cardxfeb/public_html/.env' with open(env_path, 'r') as f: c = f.read() wt = 'whsec_W8UvAs029jxRsq1LheBK3Z99FHE8M9AVapBRGIq4bFI' c2 = re.sub(r'ASAAS_WEBHOOK_TOKEN=.*', 'ASAAS_WEBHOOK_TOKEN=' + wt, c) if c2 != c: with open(env_path, 'w') as f: f.write(c2) print('2. ASAAS_WEBHOOK_TOKEN ATUALIZADO OK') else: print('2. ASAAS_WEBHOOK_TOKEN nao encontrado - adicionando') with open(env_path, 'a') as f: f.write('\nASSAAS_WEBHOOK_TOKEN=' + wt + '\n') print('2. ASAAS_WEBHOOK_TOKEN ADICIONADO OK') # 3. Verify file is accessible import os size = os.path.getsize('/home/cardxfeb/public_html/simpledcver.txt') print(f'3. Arquivo simpledcver.txt: {size} bytes') PYEOF curl -s https://cardfyz.com/simpledcver.txt | head -5 head -20 /home/cardxfeb/public_html/.htaccess python3 << 'PYEOF' # Add exception for simpledcver.txt in .htaccess htpath = '/home/cardxfeb/public_html/.htaccess' with open(htpath, 'r') as f: c = f.read() exception = '\n # Domain Validation File - do not remove\n RewriteCond %{REQUEST_URI} !^/simpledcver\.txt$\n' # Insert before the main Laravel rewrite rule if 'simpledcver' not in c: c = c.replace(' RewriteCond %{REQUEST_FILENAME} !-d', exception + ' RewriteCond %{REQUEST_FILENAME} !-d') with open(htpath, 'w') as f: f.write(c) print('HTACCESS UPDATED - exception added') else: print('ALREADY HAS EXCEPTION') PYEOF curl -s https://cardfyz.com/simpledcver.txt python3 << 'PYEOF' htpath = '/home/cardxfeb/public_html/.htaccess' with open(htpath, 'r') as f: c = f.read() # Add rule at the very beginning to serve simpledcver.txt directly rule = 'RewriteEngine On\nRewriteRule ^simpledcver\.txt$ simpledcver.txt [L]\n\n' if 'simpledcver.txt [L]' not in c: c = rule + c with open(htpath, 'w') as f: f.write(c) print('RULE ADDED AT TOP') else: print('ALREADY EXISTS') PYEOF curl -s https://cardfyz.com/simpledcver.txt | head -3 tail -n 100 ~/public_html/storage/logs/laravel.log cat ~/public_html/app/Http/Middleware/DetectRegion.php head -n 30 ~/public_html/app/Http/Middleware/DetectRegion.php sed -n '1,25p' ~/public_html/app/Http/Middleware/DetectRegion.php sed -n '14,80p' ~/public_html/app/Http/Middleware/DetectRegion.php sed -n '14,50p' ~/public_html/app/Http/Middleware/DetectRegion.php | cat -n sed -i "s/'payment_gateway' => env('GATEWAY_BR', 'manual'),/'payment_gateway' => 'stripe_br',/g" ~/public_html/app/Http/Middleware/DetectRegion.php grep -n 'env(' ~/public_html/app/Http/Middleware/DetectRegion.php python3 -c "content = open('/home/cardxfeb/public_html/app/Http/Middleware/DetectRegion.php').read(); content = content.replace(\"'payment_gateway' => env('GATEWAY_BR', 'manual'),\", \"'payment_gateway' => 'stripe_br',\"); open('/home/cardxfeb/public_html/app/Http/Middleware/DetectRegion.php', 'w').write(content); print('Corrigido''')" grep -n 'env(' ~/public_html/app/Http/Middleware/DetectRegion.php sed -n '28,32p' ~/public_html/app/Http/Middleware/DetectRegion.php php -l ~/public_html/app/Http/Middleware/DetectRegion.php cd ~/public_html && php artisan config:clear && php artisan cache:clear && php artisan route:clear && php artisan view:clear echo 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkb21haW4iOiJjYXJkLmFvc2VndW5kby5wdCIsImFjY6MTc3NzY4MDAwMH0.cgNI_luwIx2vnV_uYufiQ-kcxJtE1f2KjDc9tjVFN5M' > ~/public_html/public/simpledcver.txt && echo 'Arquivo criado!' host card.aosegundo.pt host card.aosegundo.com.br && host aosegundo.pt cd ~/public_html && php artisan tinker --execute="Mail::raw('Teste de e-mail do servidor CARDFYZ - OK', function(\$m) { \$m->to('geral@cardfyz.com')->subject('Teste de Envio - Card Ao Segundo'); });" grep -E 'MAIL_|SMTP' ~/public_html/.env python3 -c "content = open('/home/cardxfeb/public_html/.env').read(); content = content.replace('MAIL_HOST=mail.cardfyz.com', 'MAIL_HOST=server335.web-hosting.com'); open('/home/cardxfeb/public_html/.env', 'w').write(content); print('MAIL_HOST corrigido''')" php artisan config:clear && php artisan tinker --execute="Mail::raw('Teste de e-mail do servidor CARDFYZ - OK', function(\$m) { \$m->to('geral@cardfyz.com')->subject('Teste de Envio - Card Ao Segundo'); });" grep 'MAIL_HOST' ~/public_html/.env cd ~/public_html && php artisan tinker --execute="Mail::raw('Teste CARDFYZ', function(\$m) { \$m->to('geral@cardfyz.com')->subject('Teste Cardfyz 2026'); }); echo 'OK';" tail -n 80 ~/public_html/storage/logs/laravel.log | grep -A 20 'local.ERROR' tail -n 50 ~/public_html/storage/logs/laravel.log tail -n 200 ~/public_html/storage/logs/laravel.log | grep -B2 'sadmin\|admin\|ERROR' | head -n 60 cd ~/public_html && php artisan migrate:status 2>&1 | tail -n 30 php artisan tinker --execute="echo DB::select('SHOW COLUMNS FROM vcards LIKE \"deleted_at\"') ? 'Coluna existe' : 'Coluna NAO existe';" php artisan make:migration add_soft_deletes_to_vcards_table --table=vcards cat ~/public_html/database/migrations/2026_04_25_213715_add_soft_deletes_to_vcards_table.php python3 << 'EOF' content = open('/home/cardxfeb/public_html/database/migrations/2026_04_25_213715_add_soft_deletes_to_vcards_table.php').read() content = content.replace( "Schema::table('vcards', function (Blueprint $table) {\n //\n });", "Schema::table('vcards', function (Blueprint $table) {\n $table->softDeletes();\n });", 1 ) content = content.replace( "Schema::table('vcards', function (Blueprint $table) {\n //\n });", "Schema::table('vcards', function (Blueprint $table) {\n $table->dropSoftDeletes();\n });", 1 ) open('/home/cardxfeb/public_html/database/migrations/2026_04_25_213715_add_soft_deletes_to_vcards_table.php', 'w').write(content) print('Migration atualizada!') EOF cat ~/public_html/database/migrations/2026_04_25_213715_add_soft_deletes_to_vcards_table.php | grep -A5 'function up\|function down' php artisan migrate --force 2>&1 ls -la ~/public_html/storage/framework/sessions/ | head -5 && stat -c '%a %n' ~/public_html/storage/framework/sessions/ cat ~/public_html/app/Http/Middleware/DetectRegion.php sed -n '38,80p' ~/public_html/app/Http/Middleware/DetectRegion.php sed -n '37,62p' ~/public_html/app/Http/Middleware/DetectRegion.php wc -l ~/public_html/app/Http/Middleware/DetectRegion.php python3 << 'PYEOF' new_content = '''<?php namespace App\\Http\\Middleware; use Closure; use Illuminate\\Http\\Request; use Symfony\\Component\\HttpFoundation\\Response; class DetectRegion { /** * Mapa de dominios para configuracoes regionais * CARD AO SEGUNDO CONTROL - Multi-Dominio */ protected array $regions = [ \'card.aosegundo.pt\' => [ \'currency\' => \'EUR\', \'currency_symbol\' => \'\', \'locale\' => \'pt_PT\', \'country\' => \'PT\', \'payment_gateway\' => \'ifthen\', \'stripe_key\' => \'STRIPE_KEY_PT\', \'stripe_secret\' => \'STRIPE_SECRET_PT\', \'flag\' => \'PT\', \'name\' => \'Portugal\', ], \'card.aosegundo.com.br\' => [ \'currency\' => \'BRL\', \'currency_symbol\' => \'R$\', \'locale\' => \'pt_BR\', \'country\' => \'BR\', \'payment_gateway\' => \'asaas\', \'stripe_key\' => \'STRIPE_KEY_BR\', \'stripe_secret\' => \'STRIPE_SECRET_BR\', \'flag\' => \'BR\', \'name\' => \'Brasil\', ], \'cardfyz.com\' => [ \'currency\' => \'EUR\', \'currency_symbol\' => \'\', \'locale\' => \'pt_BR\', \'country\' => \'BR\', \'payment_gateway\' => \'stripe_br\', \'stripe_key\' => \'STRIPE_KEY\', \'stripe_secret\' => \'STRIPE_SECRET\', \'flag\' => \'BR\', \'name\' => \'Card Ao Segundo (legado)\', ], ]; public function handle(Request $request, Closure $next): Response { $host = $request->getHost(); $region = $this->regions[$host] ?? $this->regions[\'cardfyz.com\']; // Determinar gateway activo baseado no .env (dinamico - seguro dentro do handle) if ($host === \'card.aosegundo.pt\') { $activeGateway = env(\'GATEWAY_PT\', $region[\'payment_gateway\']); } elseif ($host === \'card.aosegundo.com.br\') { $activeGateway = env(\'GATEWAY_BR\', $region[\'payment_gateway\']); } else { $activeGateway = env(\'GATEWAY_BR\', $region[\'payment_gateway\']); } // Injectar configuracoes regionais em runtime config([ \'app.currency\' => $region[\'currency\'], \'app.currency_symbol\' => $region[\'currency_symbol\'], \'app.locale\' => $region[\'locale\'], \'app.country\' => $region[\'country\'], \'app.payment_gateway\' => $activeGateway, \'app.region_flag\' => $region[\'flag\'], \'app.region_name\' => $region[\'name\'], \'app.url\' => \'https://\' . $host, ]); // Partilhar com todas as views Blade view()->share(\'appRegion\', $region); view()->share(\'appCurrency\', $region[\'currency\']); view()->share(\'appCountry\', $region[\'country\']); view()->share(\'currentDomain\', $host); view()->share(\'activeGateway\', $activeGateway); return $next($request); } } ''' open('/home/cardxfeb/public_html/app/Http/Middleware/DetectRegion.php', 'w').write(new_content) print('DetectRegion.php reescrito com sucesso!') PYEOF php -l ~/public_html/app/Http/Middleware/DetectRegion.php && php artisan config:clear && php artisan cache:clear && php artisan route:clear && php artisan view:clear find ~ -name 'artisan' 2>/dev/null | head -20 ls /home/cardxfeb/public_html/app/Http/Controllers/ | grep -i 'front\|subscription\|home' grep -n 'plan\|Plan\|currency\|currency_id' /home/cardxfeb/public_html/app/Http/Controllers/HomeController.php | head -40 grep -n 'choosePayment\|choose_payment\|paymentType\|currency\|BRL\|EUR' /home/cardxfeb/public_html/app/Http/Controllers/SubscriptionController.php | head -30 cd /home/cardxfeb/public_html && cat .env | grep -i 'app_name\|app_url\|app_env' | head -5 grep 'ASAAS' .env sed -i 's/ASAAS_API_KEY=CONFIGURE_ME/ASAAS_API_KEY=597b4340-f667-41f2-940c-90f4c8cc1f43/' .env && sed -i 's/ASAAS_SANDBOX=true/ASAAS_SANDBOX=false/' .env && grep 'ASAAS' .env find . -name '*saa*' -o -name '*Asaas*' 2>/dev/null | grep -v vendor | grep -v node_modules grep -r 'asaas\|Asaas' app/Http/Controllers/ --include='*.php' -l 2>/dev/null cat app/Http/Controllers/Webhooks/BrasilWebhookController.php head -80 app/Http/Controllers/Webhooks/BrasilWebhookController.php ls app/Http/Controllers/ && echo '---' && ls app/Http/Controllers/Webhooks/ 2>/dev/null python3 -c " conteudo = '''<?php namespace App\\Http\\Controllers; use App\\Models\\Plan; use App\\Models\\User; use Illuminate\\Http\\Request; use Illuminate\\Support\\Facades\\Http; use Illuminate\\Support\\Facades\\Auth; use Illuminate\\Support\\Facades\\Log; class AsaasController extends Controller { private string \$apiKey; private string \$baseUrl; public function __construct() { \$this->apiKey = env(\"ASAAS_API_KEY\"); \$this->baseUrl = env(\"ASAAS_BASE_URL\", \"https://api.asaas.com/v3\"); } public function showCheckout(int \$planId) { \$plan = Plan::with(\"currency\")->findOrFail(\$planId); if (\$plan->currency->currency_code !== \"BRL\") { abort(403, \"Plano nao disponivel para esta regiao.\"); } return view(\"asaas.checkout\", compact(\"plan\")); } public function processPayment(Request \$request, int \$planId) { \$request->validate([ \"cpf_cnpj\" => [\"required\", \"string\", \"min:11\", \"max:18\"], \"name\" => [\"required\", \"string\"], \"email\" => [\"required\", \"email\"], ]); \$plan = Plan::with(\"currency\")->findOrFail(\$planId); \$cpf = preg_replace(\"/\\D/\", \"\", \$request->cpf_cnpj); \$customer = \$this->createOrFindCustomer(\$cpf, \$request->name, \$request->email); return back()->withErrors([\"asaas\" => \"Erro cliente Asaas: \".json_encode(\$customer[\"errors\"] ?? [])]); } \$userId = Auth::id(); \$charge = \$this->createPixCharge(\$customer[\"id\"], \$plan->price, \"Plano {\$plan->name}\", \$planId, \$userId); return back()->withErrors([\"asaas\" => \"Erro cobranca: \".json_encode(\$charge[\"errors\"] ?? [])]); } \$pixData = \$this->getPixQrCode(\$charge[\"id\"]); return view(\"asaas.pix\", compact(\"plan\", \"charge\", \"pixData\")); } private function createOrFindCustomer(string \$cpfCnpj, string \$name, string \$email): array { \$search = Http::withHeaders([\"access_token\" => \$this->apiKey]) ->get(\"\$this->baseUrl/customers\", [\"cpfCnpj\" => \$cpfCnpj]); if (\$search->ok() && \$search->json(\"totalCount\") > 0) { return \$search->json(\"data.0\"); } return Http::withHeaders([\"access_token\" => \$this->apiKey]) ->post(\"\$this->baseUrl/customers\", [\"name\" => \$name, \"email\" => \$email, \"cpfCnpj\" => \$cpfCnpj]) ->json(); } private function createPixCharge(string \$customerId, float \$value, string \$description, int \$planId, int \$userId): array { return Http::withHeaders([\"access_token\" => \$this->apiKey]) ->post(\"\$this->baseUrl/payments\", [ \"customer\" => \$customerId, \"billingType\" => \"PIX\", \"value\" => \$value, \"dueDate\" => now()->addDay()->format(\"Y-m-d\"), \"description\" => \$description, \"externalReference\" => \"plan_{\$planId}_user_{\$userId}\", ])->json(); } private function getPixQrCode(string \$chargeId): array { return Http::withHeaders([\"access_token\" => \$this->apiKey]) ->get(\"\$this->baseUrl/payments/{\$chargeId}/pixQrCode\") ->json(); } } ''' with open('app/Http/Controllers/AsaasController.php', 'w') as f: f.write(conteudo) print('AsaasController criado com sucesso') " cat > /tmp/create_asaas.py << 'PYEOF' import os content = """<?php namespace App\\Http\\Controllers; use App\\Models\\Plan; use Illuminate\\Http\\Request; use Illuminate\\Support\\Facades\\Http; use Illuminate\\Support\\Facades\\Auth; use Illuminate\\Support\\Facades\\Log; class AsaasController extends Controller { private $apiKey; private $baseUrl; public function __construct() { $this->apiKey = env('ASAAS_API_KEY'); $this->baseUrl = env('ASAAS_BASE_URL', 'https://api.asaas.com/v3'); } public function showCheckout($planId) { $plan = Plan::with('currency')->findOrFail($planId); abort_unless($plan->currency->currency_code === 'BRL', 403); return view('asaas.checkout', compact('plan')); } public function processPayment(Request $request, $planId) { $request->validate([ 'cpf_cnpj' => ['required','string','min:11','max:18'], 'name' => ['required','string'], 'email' => ['required','email'], ]); $plan = Plan::with('currency')->findOrFail($planId); $cpf = preg_replace('/[^0-9]/', '', $request->cpf_cnpj); $customer = $this->createOrFindCustomer($cpf, $request->name, $request->email); if (!$customer || isset($customer['errors'])) { return back()->withErrors(['asaas' => 'Erro cliente: '.json_encode($customer['errors'] ?? [])]); } $userId = Auth::id(); $charge = $this->createPixCharge($customer['id'], $plan->price, 'Plano '.$plan->name, $planId, $userId); if (!$charge || isset($charge['errors'])) { return back()->withErrors(['asaas' => 'Erro cobranca: '.json_encode($charge['errors'] ?? [])]); } $pixData = $this->getPixQrCode($charge['id']); return view('asaas.pix', compact('plan', 'charge', 'pixData')); } private function createOrFindCustomer($cpfCnpj, $name, $email) { $search = Http::withHeaders(['access_token' => $this->apiKey]) ->get($this->baseUrl.'/customers', ['cpfCnpj' => $cpfCnpj]); if ($search->ok() && $search->json('totalCount') > 0) { return $search->json('data')[0]; } return Http::withHeaders(['access_token' => $this->apiKey]) ->post($this->baseUrl.'/customers', ['name' => $name, 'email' => $email, 'cpfCnpj' => $cpfCnpj]) ->json(); } private function createPixCharge($customerId, $value, $description, $planId, $userId) { return Http::withHeaders(['access_token' => $this->apiKey]) ->post($this->baseUrl.'/payments', [ 'customer' => $customerId, 'billingType' => 'PIX', 'value' => $value, 'dueDate' => date('Y-m-d', strtotime('+1 day')), 'description' => $description, 'externalReference' => 'plan_'.$planId.'_user_'.$userId, ])->json(); } private function getPixQrCode($chargeId) { return Http::withHeaders(['access_token' => $this->apiKey]) ->get($this->baseUrl.'/payments/'.$chargeId.'/pixQrCode') ->json(); } } """ with open('/home/cardxfeb/public_html/app/Http/Controllers/AsaasController.php', 'w') as f: f.write(content) print('OK: AsaasController.php criado') PYEOF python3 /tmp/create_asaas.py mkdir -p resources/views/asaas && ls resources/views/ | grep asaas ls resources/views/layouts/ | head -5 cat > /tmp/create_views.py << 'PYEOF' checkout = """@extends('layouts.app') @section('title', 'Checkout - Plano Brasil') @section('content') <div class="container mt-5"> <div class="row justify-content-center"> <div class="col-md-6"> <div class="card shadow"> <div class="card-header bg-primary text-white"> <h4 class="mb-0">Assinar {{ $plan->name }}</h4> </div> <div class="card-body"> <p class="lead">Valor: <strong>R$ {{ number_format($plan->price, 2, ',', '.') }}/ms</strong></p> <hr> <form method="POST" action="{{ route('asaas.checkout.process', $plan->id) }}"> @csrf <div class="mb-3"> <label class="form-label">Nome Completo *</label> <input type="text" name="name" class="form-control @error('name') is-invalid @enderror" value="{{ auth()->user()->name ?? '' }}" required> @error('name') <div class="invalid-feedback">{{ $message }}</div> @enderror </div> <div class="mb-3"> <label class="form-label">E-mail *</label> <input type="email" name="email" class="form-control @error('email') is-invalid @enderror" value="{{ auth()->user()->email ?? '' }}" required> @error('email') <div class="invalid-feedback">{{ $message }}</div> @enderror </div> <div class="mb-3"> <label class="form-label">CPF ou CNPJ * <small class="text-muted">(obrigatrio para Pix)</small></label> <input type="text" name="cpf_cnpj" class="form-control @error('cpf_cnpj') is-invalid @enderror" placeholder="000.000.000-00" maxlength="18" required> @error('cpf_cnpj') <div class="invalid-feedback">{{ $message }}</div> @enderror </div> @if($errors->has('asaas')) <div class="alert alert-danger">{{ $errors->first('asaas') }}</div> @endif <div class="d-grid"> <button type="submit" class="btn btn-success btn-lg">Gerar QR Code Pix</button> </div> </form> </div> </div> </div> </div> </div> @endsection """ pix = """@extends('layouts.app') @section('title', 'Pague via Pix') @section('content') <div class="container mt-5"> <div class="row justify-content-center"> <div class="col-md-6 text-center"> <div class="card shadow"> <div class="card-header bg-success text-white"> <h4 class="mb-0">Pague via Pix</h4> </div> <div class="card-body"> <p>Plano: <strong>{{ $plan->name }}</strong></p> <p>Valor: <strong>R$ {{ number_format($plan->price, 2, ',', '.') }}</strong></p> @if(isset($pixData['encodedImage'])) <img src="data:image/png;base64,{{ $pixData['encodedImage'] }}" alt="QR Code Pix" class="img-fluid" style="max-width:280px"> <div class="mt-3"> <label class="form-label"><small>Cdigo Copia e Cola:</small></label> <textarea class="form-control" rows="3" onclick="this.select()" readonly>{{ $pixData['payload'] ?? '' }}</textarea> </div> @else <div class="alert alert-warning">QR Code no disponvel. Tente novamente.</div> @endif <p class="mt-3 text-muted"><small>Aps o pagamento, seu plano ser ativado automaticamente.</small></p> <a href="{{ route('admin.dashboard') }}" class="btn btn-outline-primary mt-2">Ir para o Painel</a> </div> </div> </div> </div> </div> @endsection """ with open('/home/cardxfeb/public_html/resources/views/asaas/checkout.blade.php', 'w') as f: f.write(checkout) print('OK: checkout.blade.php criado') with open('/home/cardxfeb/public_html/resources/views/asaas/pix.blade.php', 'w') as f: f.write(pix) print('OK: pix.blade.php criado') PYEOF python3 /tmp/create_views.py tail -20 routes/web.php grep -n 'auth\|middleware.*auth\|role' routes/web.php | tail -10 wc -l routes/web.php cat >> routes/web.php << 'ROUTESEOF' // Asaas Checkout (Brasil/BRL) use App\Http\Controllers\AsaasController; Route::middleware(['auth', 'valid.user', 'role:admin'])->group(function () { Route::get('/admin/asaas/checkout/{planId}', [AsaasController::class, 'showCheckout'])->name('asaas.checkout.show'); Route::post('/admin/asaas/checkout/{planId}', [AsaasController::class, 'processPayment'])->name('asaas.checkout.process'); }); ROUTESEOF cat app/Http/Middleware/VerifyCsrfToken.php sed -n '45,55p' app/Http/Controllers/HomeController.php cp app/Http/Controllers/HomeController.php app/Http/Controllers/HomeController.php.bak && echo 'backup criado' cat > /tmp/patch_home.py << 'PYEOF' with open('app/Http/Controllers/HomeController.php', 'r') as f: content = f.read() old = """ $plans = Plan::with(['currency', 'planFeature', 'hasZeroPlan', 'planCustomFields'])->whereIsDefault(Plan::IS_DEACTIVE)->whereStatus(Plan::IS_ACTIVE)->get();""" new = """ // Isolamento regional de planos por hostname $host = request()->getHost(); $currencyCode = 'EUR'; // default Europa if (str_contains($host, '.com.br')) { $currencyCode = 'BRL'; } elseif (str_contains($host, '.pt')) { $currencyCode = 'EUR'; } $plans = Plan::with(['currency', 'planFeature', 'hasZeroPlan', 'planCustomFields']) ->whereIsDefault(Plan::IS_DEACTIVE) ->whereStatus(Plan::IS_ACTIVE) ->whereHas('currency', function ($q) use ($currencyCode) { $q->where('currency_code', $currencyCode); }) ->get();""" if old in content: content = content.replace(old, new) with open('app/Http/Controllers/HomeController.php', 'w') as f: f.write(content) print('OK: HomeController.php atualizado com filtro regional') else: print('AVISO: Linha nao encontrada exatamente - verificar manualmente') print('Trecho atual (linha 49):') lines = content.split(chr(10)) for i, l in enumerate(lines[47:52], 48): print(str(i) + ': ' + l) PYEOF python3 /tmp/patch_home.py sed -n '83,115p' app/Http/Controllers/SubscriptionController.php cp app/Http/Controllers/SubscriptionController.php app/Http/Controllers/SubscriptionController.php.bak && echo 'backup ok' cat > /tmp/patch_sub.py << 'PYEOF' with open('app/Http/Controllers/SubscriptionController.php', 'r') as f: content = f.read() old = " $subscriptionsPricingPlan = Plan::findOrFail($planId);" new = " $subscriptionsPricingPlan = Plan::with('currency')->findOrFail($planId);" "\n // Redirecionar planos BRL para o checkout Asaas" "\n if ($subscriptionsPricingPlan->currency && $subscriptionsPricingPlan->currency->currency_code === 'BRL') {" "\n return redirect()->route('asaas.checkout.show', $planId);" "\n }" if old in content: content = content.replace(old, new) with open('app/Http/Controllers/SubscriptionController.php', 'w') as f: f.write(content) print('OK: SubscriptionController.php atualizado - redirecionamento BRL para Asaas') else: print('AVISO: Linha nao encontrada. Verificar manualmente linha 109') PYEOF python3 /tmp/patch_sub.py python3 << 'EOF' content = open('app/Http/Controllers/SubscriptionController.php').read() old = " $subscriptionsPricingPlan = Plan::findOrFail($planId);" new_code = " $subscriptionsPricingPlan = Plan::with('currency')->findOrFail($planId);\n if ($subscriptionsPricingPlan->currency && $subscriptionsPricingPlan->currency->currency_code === 'BRL') {\n return redirect()->route('asaas.checkout.show', $planId);\n }" if old in content: open('app/Http/Controllers/SubscriptionController.php','w').write(content.replace(old,new_code)) print('OK: SubscriptionController atualizado') else: print('AVISO: nao encontrado') EOF php artisan config:clear && php artisan route:clear && php artisan view:clear && php artisan cache:clear && echo 'Cache limpo com sucesso' php artisan route:list | grep asaas cd /home/cardxfeb/public_html && curl -s -o /dev/null -w 'HTTP Status: %{http_code}\n' https://cardfyz.com/admin/asaas/checkout/7
SAVE
CANCEL