Files
html/api/em-webhooks.php

192 lines
8.9 KiB
PHP

<?php
/**
* EM Webhooks: Stripe + Cloudflare DNS automation
* Stripe: verify sig, on checkout.session.completed → create tenant + trigger bootstrap
* Cloudflare: create DNS A record wevia-{tenant}.wevup.app
*/
header("Content-Type: application/json");
$action = $_GET["action"] ?? $_POST["action"] ?? ($_SERVER["PATH_INFO"] ?? "");
$action = ltrim($action, "/");
$DB_HOST = "127.0.0.1";
$DB = ["pgsql:host=$DB_HOST;port=5432;dbname=adx_system", "admin", "admin123"];
try { $pdo = new PDO($DB[0], $DB[1], $DB[2], [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); }
catch (Exception $e) { http_response_code(500); echo json_encode(["error"=>"db-unreachable"]); exit; }
// Read secrets
$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");
$sig = $_SERVER["HTTP_STRIPE_SIGNATURE"] ?? "";
$secret = $secrets["STRIPE_WEBHOOK_SECRET"] ?? "";
// Simplified sig check (real implementation should use stripe-php library)
if ($secret && $sig) {
$expected = hash_hmac("sha256", $payload, $secret);
// For prod, parse t= and v1= from sig header
}
$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"]??"", "type"=>$event["type"]??""]);
if (($event["type"] ?? "") === "checkout.session.completed") {
$sess = $event["data"]["object"] ?? [];
$email = $sess["customer_details"]["email"] ?? "demo@example.com";
$name = $sess["metadata"]["company"] ?? ($sess["customer_details"]["name"] ?? "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]);
// Clone VSM depts based on plan
$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, ["email"=>$email,"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":
// Create A record wevia-{tenant}.wevup.app → S204 IP
$raw = json_decode(file_get_contents("php://input"), true) ?? $_POST;
$tenant = $raw["tenant"] ?? "demo";
$target_ip = "204.168.152.13"; // S204
$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,"note"=>"CF_API_KEY not set in /etc/weval/secrets.env — add for live DNS"]);
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;
// Fetch DMAIC cycle(s)
if ($vs) {
$stmt = $pdo->prepare("SELECT * FROM weval.dmaic_cycles WHERE tenant_id=? AND vs_id=?");
$stmt->execute([$tenant, $vs]);
$cycles = $stmt->fetchAll(PDO::FETCH_ASSOC);
} else {
$stmt = $pdo->prepare("SELECT * FROM weval.dmaic_cycles WHERE tenant_id=? AND phase='control'");
$stmt->execute([$tenant]);
$cycles = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
if (empty($cycles)) { echo json_encode(["error"=>"no-completed-dmaic-cycles"]); break; }
// Generate markdown case study (simpler than docx, can be converted)
$md = "# Case Study — $tenant\n\n";
$md .= "Generated: " . date('Y-m-d H:i') . "\n\n";
foreach ($cycles as $c) {
$md .= "## " . $c["name"] . "\n\n";
$md .= "- **VS ID**: " . $c["vs_id"] . "\n";
$md .= "- **Phase**: " . strtoupper($c["phase"]) . " (" . $c["progress"] . "%)\n";
$md .= "- **Started**: " . ($c["created_at"] ?? "") . "\n\n";
$md .= "### DMAIC Progression\n\n";
foreach (["define","measure","analyze","improve","control"] as $p) {
$data = json_decode($c[$p . "_data"] ?? "{}", true);
if (!empty($data)) {
$md .= "**" . ucfirst($p) . "**: " . implode(" · ", array_map(fn($k,$v)=>"$k=$v", array_keys($data), array_values($data))) . "\n";
}
}
$md .= "\n---\n\n";
}
// Save to /var/www/weval/deliverables/{tenant}/case-study-{date}.md
$dir = "/var/www/weval/deliverables/$tenant";
@mkdir($dir, 0755, true);
$file = "$dir/case-study-" . date('Ymd-His') . ".md";
file_put_contents($file, $md);
audit($pdo, "case_study_generate", $tenant, ["vs"=>$vs,"file"=>$file]);
echo json_encode(["ok"=>true,"file"=>$file,"size"=>strlen($md),"cycles"=>count($cycles),"content"=>$md]);
break;
case "video-tour":
$raw = json_decode(file_get_contents("php://input"), true) ?? $_GET;
$tenant = $raw["tenant"] ?? "weval";
// Stub: trigger playwright + ffmpeg script
$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":
// Multi-tenant NonReg — run scoped tests per tenant
$tenant = $_GET["tenant"] ?? "weval";
$checks = [];
// Check tenant has all expected resources
$stmt = $pdo->prepare("SELECT COUNT(*) FROM weval.vsm_dept WHERE tenant_id=?");
$stmt->execute([$tenant]);
$vsm_count = $stmt->fetchColumn();
$checks[] = ["name"=>"vsm_depts","value"=>$vsm_count,"pass"=>$vsm_count>0];
$stmt = $pdo->prepare("SELECT COUNT(*) FROM weval.dmaic_cycles WHERE tenant_id=?");
$stmt->execute([$tenant]);
$dmaic_count = $stmt->fetchColumn();
$checks[] = ["name"=>"dmaic_cycles","value"=>$dmaic_count,"pass"=>$dmaic_count>=0];
$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;
default:
echo json_encode([
"service" => "EM Webhooks + Automation",
"endpoints" => [
"POST /api/em/webhooks.php?action=stripe (Stripe webhook)",
"POST /api/em/webhooks.php?action=cloudflare-dns (DNS auto)",
"POST /api/em/webhooks.php?action=case-study-generate",
"POST /api/em/webhooks.php?action=video-tour",
"GET /api/em/webhooks.php?action=nonreg-tenant&tenant=X"
]
]);
}