150 lines
6.2 KiB
PHP
150 lines
6.2 KiB
PHP
<?php
|
||
/**
|
||
* Stripe Live Bridge v1.0 · 18 avril 2026
|
||
*
|
||
* Reads real Stripe credentials from /etc/weval/secrets.env
|
||
* Fetches MRR / ARR / customers / subscriptions from live Stripe account
|
||
* Returns v83-business-kpi compatible format
|
||
*
|
||
* GOLD: nouveau fichier, zero modif existant
|
||
* Source: Stripe API (live mode, acct_1RviYXCpdcPNJE6S)
|
||
*/
|
||
|
||
header('Content-Type: application/json');
|
||
header('Access-Control-Allow-Origin: *');
|
||
header('X-Stripe-Bridge: v1.0');
|
||
|
||
$t0 = microtime(true);
|
||
|
||
// ========== 1. Load secrets ==========
|
||
$secrets_file = '/etc/weval/secrets.env';
|
||
if (!is_readable($secrets_file)) {
|
||
http_response_code(500);
|
||
echo json_encode(['ok'=>false, 'error'=>'secrets_unreadable', 'source'=>$secrets_file]);
|
||
exit;
|
||
}
|
||
$secrets_raw = @file_get_contents($secrets_file);
|
||
preg_match('/STRIPE_SK_LIVE=([^\s\n]+)/', $secrets_raw, $mk);
|
||
$sk = $mk[1] ?? '';
|
||
preg_match('/STRIPE_ACCOUNT=([^\s\n]+)/', $secrets_raw, $ma);
|
||
$acct = $ma[1] ?? '';
|
||
|
||
if (empty($sk) || !str_starts_with($sk, 'sk_live_')) {
|
||
http_response_code(500);
|
||
echo json_encode(['ok'=>false, 'error'=>'invalid_key', 'livemode'=>false]);
|
||
exit;
|
||
}
|
||
|
||
// ========== 2. Helper: call Stripe API ==========
|
||
function stripe_get($sk, $path, $params = []) {
|
||
$url = 'https://api.stripe.com/v1/' . ltrim($path, '/');
|
||
if ($params) $url .= '?' . http_build_query($params);
|
||
$ch = curl_init($url);
|
||
curl_setopt_array($ch, [
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_USERPWD => $sk . ':',
|
||
CURLOPT_TIMEOUT => 6,
|
||
CURLOPT_HTTPHEADER => ['Stripe-Version: 2024-12-18.acacia'],
|
||
]);
|
||
$r = curl_exec($ch);
|
||
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||
curl_close($ch);
|
||
return ['code' => $code, 'data' => json_decode($r, true)];
|
||
}
|
||
|
||
// ========== 3. Cache layer (60s TTL) ==========
|
||
$cache_file = '/tmp/stripe-live-cache.json';
|
||
$cache_ttl = 60;
|
||
if (is_readable($cache_file) && (time() - filemtime($cache_file)) < $cache_ttl) {
|
||
$cached = @json_decode(@file_get_contents($cache_file), true);
|
||
if ($cached && !isset($_GET['nocache'])) {
|
||
$cached['cached'] = true;
|
||
$cached['elapsed_ms'] = round((microtime(true) - $t0) * 1000, 1);
|
||
echo json_encode($cached, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||
exit;
|
||
}
|
||
}
|
||
|
||
// ========== 4. Fetch live data ==========
|
||
$balance = stripe_get($sk, 'balance');
|
||
$customers = stripe_get($sk, 'customers', ['limit' => 100]);
|
||
$subs = stripe_get($sk, 'subscriptions', ['limit' => 100, 'status' => 'active']);
|
||
$invoices = stripe_get($sk, 'invoices', ['limit' => 20, 'status' => 'paid']);
|
||
|
||
// ========== 5. Compute KPIs ==========
|
||
$customer_list = $customers['data']['data'] ?? [];
|
||
$active_customer_count = count($customer_list);
|
||
|
||
$active_subs = $subs['data']['data'] ?? [];
|
||
$mrr_cents = 0;
|
||
$currency = 'eur';
|
||
foreach ($active_subs as $s) {
|
||
foreach (($s['items']['data'] ?? []) as $item) {
|
||
$unit = $item['price']['unit_amount'] ?? 0;
|
||
$interval = $item['price']['recurring']['interval'] ?? 'month';
|
||
$qty = $item['quantity'] ?? 1;
|
||
// Normalize to month
|
||
$monthly = ($interval === 'year') ? $unit / 12 : $unit;
|
||
$mrr_cents += $monthly * $qty;
|
||
$currency = $item['price']['currency'] ?? 'eur';
|
||
}
|
||
}
|
||
$mrr = round($mrr_cents / 100, 2);
|
||
$arr = round($mrr * 12, 2);
|
||
|
||
// Invoices total paid (revenue YTD)
|
||
$revenue_paid_cents = 0;
|
||
foreach (($invoices['data']['data'] ?? []) as $inv) {
|
||
if (($inv['status'] ?? '') === 'paid') {
|
||
$revenue_paid_cents += $inv['amount_paid'] ?? 0;
|
||
}
|
||
}
|
||
$revenue_paid = round($revenue_paid_cents / 100, 2);
|
||
|
||
// Balance
|
||
$bal = $balance['data']['available'][0] ?? [];
|
||
$balance_available = ($bal['amount'] ?? 0) / 100;
|
||
$balance_currency = $bal['currency'] ?? 'usd';
|
||
$livemode = $balance['data']['livemode'] ?? false;
|
||
|
||
// ========== 6. Build response (v83 compatible) ==========
|
||
$output = [
|
||
'ok' => true,
|
||
'source' => 'Stripe Live API',
|
||
'account' => $acct,
|
||
'livemode' => $livemode,
|
||
'currency' => strtoupper($currency),
|
||
'ts' => date('c'),
|
||
'cached' => false,
|
||
'kpis' => [
|
||
'mrr' => ['value' => $mrr, 'unit' => strtoupper($currency), 'target' => 50000, 'status' => $mrr >= 50000 ? 'ok' : ($mrr > 0 ? 'warn' : 'starting'), 'source' => 'Stripe subscriptions active'],
|
||
'arr' => ['value' => $arr, 'unit' => strtoupper($currency), 'target' => 600000, 'status' => $arr >= 600000 ? 'ok' : ($arr > 0 ? 'warn' : 'starting'), 'source' => 'MRR × 12'],
|
||
'active_customers' => ['value' => $active_customer_count, 'unit' => 'clients', 'target' => 20, 'status' => $active_customer_count >= 20 ? 'ok' : ($active_customer_count > 0 ? 'warn' : 'starting'), 'source' => 'Stripe customers'],
|
||
'active_subscriptions' => ['value' => count($active_subs), 'unit' => 'subs', 'target' => 20, 'status' => count($active_subs) >= 20 ? 'ok' : (count($active_subs) > 0 ? 'warn' : 'starting'), 'source' => 'Stripe subs active'],
|
||
'revenue_paid_recent' => ['value' => $revenue_paid, 'unit' => strtoupper($currency), 'target' => null, 'status' => 'info', 'source' => '20 last paid invoices'],
|
||
'balance_available' => ['value' => $balance_available, 'unit' => strtoupper($balance_currency), 'target' => null, 'status' => 'info', 'source' => 'Stripe balance'],
|
||
],
|
||
'customers_sample' => array_slice(array_map(function($c) {
|
||
return [
|
||
'id' => $c['id'] ?? '?',
|
||
'email' => $c['email'] ?? '?',
|
||
'name' => $c['name'] ?? '?',
|
||
'created' => date('Y-m-d', $c['created'] ?? 0),
|
||
];
|
||
}, $customer_list), 0, 10),
|
||
'badge' => [
|
||
'color' => $mrr > 0 ? '#10b981' : '#f59e0b',
|
||
'label' => $mrr > 0 ? sprintf('MRR %s%d', strtoupper($currency), $mrr) : 'Starting phase',
|
||
'phase' => $mrr >= 50000 ? 'scaling' : ($mrr > 0 ? 'validating' : 'launching'),
|
||
],
|
||
'honest_note' => $active_customer_count > 0 ?
|
||
sprintf('WEVAL Consulting SaaS · %d customer(s) · MRR %s%.2f · live Stripe data', $active_customer_count, strtoupper($currency), $mrr) :
|
||
'SaaS phase: no paying customer yet · integration ready · Stripe live connected',
|
||
'elapsed_ms' => round((microtime(true) - $t0) * 1000, 1),
|
||
];
|
||
|
||
// Cache result
|
||
@file_put_contents($cache_file, json_encode($output));
|
||
|
||
echo json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|