PDO::ERRMODE_EXCEPTION]); } catch (Exception $e) { http_response_code(500); echo json_encode(["error"=>"db-unreachable"]); exit; } $secrets = []; if (file_exists("/etc/weval/secrets.env")) { foreach (file("/etc/weval/secrets.env", FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES) as $l) { if (preg_match('/^([A-Z_]+)=(.+)$/', $l, $m)) $secrets[$m[1]] = trim($m[2], '"\''); } } function audit($pdo, $action, $target, $payload) { try { $pdo->prepare("INSERT INTO weval.audit_log (tenant_id, actor, action, target, payload, ip) VALUES (?,?,?,?,?,?)")->execute(["system","webhook",$action,$target,json_encode($payload),$_SERVER["REMOTE_ADDR"]??""]); } catch (Exception $e) {} } switch ($action) { case "stripe": $payload = file_get_contents("php://input"); $event = json_decode($payload, true); if (!$event) { http_response_code(400); echo json_encode(["error"=>"invalid-payload"]); break; } audit($pdo, "stripe_webhook", $event["type"] ?? "unknown", ["id"=>$event["id"]??""]); if (($event["type"] ?? "") === "checkout.session.completed") { $sess = $event["data"]["object"] ?? []; $email = $sess["customer_details"]["email"] ?? "demo@example.com"; $name = $sess["metadata"]["company"] ?? "New Client"; $plan = $sess["metadata"]["plan"] ?? "mvp"; $tenant_id = "em_" . substr(md5($email . time()), 0, 10); $pdo->prepare("INSERT INTO weval.tenants (tenant_id, name, plan_code, phase, contact_email) VALUES (?,?,?,?,?) ON CONFLICT (tenant_id) DO UPDATE SET phase=EXCLUDED.phase")->execute([$tenant_id, $name, $plan, $plan, $email]); $n = $plan === "enterprise" ? 15 : ($plan === "mvp" ? 5 : 1); $pdo->prepare("INSERT INTO weval.vsm_dept (tenant_id, dept_code, dept_name, icon, supplier, input, process, output, customer, kpis, agents) SELECT ?, dept_code, dept_name, icon, supplier, input, process, output, customer, kpis, agents FROM weval.vsm_dept WHERE tenant_id='weval' LIMIT ? ON CONFLICT DO NOTHING")->execute([$tenant_id, $n]); audit($pdo, "stripe_checkout_complete", $tenant_id, ["plan"=>$plan]); echo json_encode(["ok"=>true,"tenant_id"=>$tenant_id,"plan"=>$plan]); } else { echo json_encode(["received"=>true,"type"=>$event["type"] ?? "unknown"]); } break; case "cloudflare-dns": $raw = json_decode(file_get_contents("php://input"), true) ?? $_POST; $tenant = $raw["tenant"] ?? "demo"; $target_ip = "204.168.152.13"; $zone_id = $secrets["CF_ZONE_WEVUP"] ?? "53e067fbc5c532a1"; $cf_key = $secrets["CF_API_KEY"] ?? ""; $cf_email = $secrets["CF_EMAIL"] ?? ""; $dns_record = ["type"=>"A","name"=>"wevia-$tenant.wevup.app","content"=>$target_ip,"ttl"=>3600,"proxied"=>true]; if (!$cf_key) { audit($pdo, "dns_create_stub", $tenant, $dns_record); echo json_encode(["stub"=>true,"tenant"=>$tenant,"would_create"=>$dns_record]); break; } $ch = curl_init("https://api.cloudflare.com/client/v4/zones/$zone_id/dns_records"); curl_setopt_array($ch, [CURLOPT_POST=>1, CURLOPT_POSTFIELDS=>json_encode($dns_record), CURLOPT_HTTPHEADER=>["Content-Type: application/json", "X-Auth-Email: $cf_email", "X-Auth-Key: $cf_key"], CURLOPT_RETURNTRANSFER=>1, CURLOPT_TIMEOUT=>15]); $resp = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); audit($pdo, "dns_create", $tenant, ["cf_status"=>$code]); echo json_encode(["status"=>$code,"cloudflare_response"=>json_decode($resp, true)]); break; case "case-study-generate": $raw = json_decode(file_get_contents("php://input"), true) ?? $_GET; $tenant = $raw["tenant"] ?? "weval"; $vs = $raw["vs"] ?? null; $arg = escapeshellarg($tenant); if ($vs) $arg .= " " . escapeshellarg($vs); $out = shell_exec("timeout 30 python3 /usr/local/bin/weval-case-study-generator.py $arg 2>&1"); if (preg_match('|OK (/var[^\s]+\.docx)|', $out, $mm)) { $file = $mm[1]; audit($pdo, "case_study_generate", $tenant, ["vs"=>$vs,"file"=>$file,"format"=>"docx"]); echo json_encode(["ok"=>true,"file"=>$file,"size"=>filesize($file),"format"=>"docx","download_path"=>$file]); } else { http_response_code(500); echo json_encode(["error"=>"docx-generation-failed","log"=>substr($out, 0, 500)]); } break; case "video-tour": $raw = json_decode(file_get_contents("php://input"), true) ?? $_GET; $tenant = $raw["tenant"] ?? "weval"; $cmd = "timeout 180 /usr/local/bin/weval-video-tour.sh " . escapeshellarg($tenant) . " >/var/log/weval-video-tour.log 2>&1 &"; shell_exec($cmd); audit($pdo, "video_tour_start", $tenant, []); echo json_encode(["ok"=>true,"tenant"=>$tenant,"status"=>"triggered","log"=>"/var/log/weval-video-tour.log","output_dir"=>"/var/www/weval/deliverables/$tenant/"]); break; case "nonreg-tenant": $tenant = $_GET["tenant"] ?? "weval"; $checks = []; $stmt = $pdo->prepare("SELECT COUNT(*) FROM weval.vsm_dept WHERE tenant_id=?"); $stmt->execute([$tenant]); $checks[] = ["name"=>"vsm_depts","value"=>$stmt->fetchColumn(),"pass"=>true]; $stmt = $pdo->prepare("SELECT COUNT(*) FROM weval.dmaic_cycles WHERE tenant_id=?"); $stmt->execute([$tenant]); $checks[] = ["name"=>"dmaic_cycles","value"=>$stmt->fetchColumn(),"pass"=>true]; $stmt = $pdo->prepare("SELECT COUNT(*) FROM weval.kaizen_events WHERE tenant_id=?"); $stmt->execute([$tenant]); $checks[] = ["name"=>"kaizen","value"=>$stmt->fetchColumn(),"pass"=>true]; $stmt = $pdo->prepare("SELECT COUNT(*) FROM weval.muda_entries WHERE tenant_id=?"); $stmt->execute([$tenant]); $checks[] = ["name"=>"muda","value"=>$stmt->fetchColumn(),"pass"=>true]; $stmt = $pdo->prepare("SELECT 1 FROM weval.tenants WHERE tenant_id=?"); $stmt->execute([$tenant]); $exists = $stmt->fetchColumn() ? 1 : 0; $checks[] = ["name"=>"tenant_exists","value"=>$exists,"pass"=>$exists===1]; $passed = count(array_filter($checks, fn($c)=>$c["pass"])); echo json_encode(["tenant"=>$tenant,"checks"=>$checks,"pass"=>$passed,"total"=>count($checks),"score"=>round($passed/count($checks)*100)]); break; case "em-nonreg": // Full EM NonReg - all APIs + core URLs $results = []; foreach (["plans","vsm","agents-registry","bpmn-routines","dmaic/weval","kpi/live","erp-connectors","ai-providers","industry-templates","scalability","muda","poka-yoke","kaizen","lean6sigma-dashboard"] as $ep) { $ch = curl_init("https://weval-consulting.com/api/em/$ep"); curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>1, CURLOPT_SSL_VERIFYPEER=>0, CURLOPT_FOLLOWLOCATION=>1, CURLOPT_TIMEOUT=>5]); curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $results[] = ["endpoint"=>"/api/em/$ep","http"=>$code,"pass"=>$code==200]; } foreach (["vsm-hub.html","brain-center-tenant.html","dmaic-workbench.html","kpi-live-dashboard.html","onboarding-em.html","bpmn-studio-live.html","integrations-marketplace.html","lean6sigma-dashboard.html"] as $page) { $ch = curl_init("https://weval-consulting.com/$page"); curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>1, CURLOPT_SSL_VERIFYPEER=>0, CURLOPT_TIMEOUT=>5]); curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $results[] = ["endpoint"=>"/$page","http"=>$code,"pass"=>in_array($code,[200,302])]; } $passed = count(array_filter($results, fn($r)=>$r["pass"])); $total = count($results); echo json_encode(["category"=>"EM","pass"=>$passed,"total"=>$total,"score"=>round($passed/$total*100),"fail"=>$total-$passed,"details"=>$results]); break; default: echo json_encode([ "service" => "EM Webhooks V2", "endpoints" => [ "POST /api/em-webhooks.php?action=stripe", "POST /api/em-webhooks.php?action=cloudflare-dns", "POST /api/em-webhooks.php?action=case-study-generate (docx)", "POST /api/em-webhooks.php?action=video-tour", "GET /api/em-webhooks.php?action=nonreg-tenant&tenant=X", "GET /api/em-webhooks.php?action=em-nonreg" ] ]); }