- StripeKeyChecker: 2-Schritt-Check (/v1/balance + /v1/account), Live/Test-Erkennung, Balance, Account-Details - KeyPatterns: Stripe-Patterns einkommentiert (sk_live_, sk_test_), Krea AI Pattern (UUID:Token), Mindest-Suchlänge 4 - KeyScannerService: Stripe-Check integriert, Auto-Check nach Scan entfernt (manuell separat), Progress-Tracking (Phase, Detail, Current/Total, KeysFound) - ApiKeysEndpoint: Stripe in HandleCheck/HandleCheckAll/HandleAdopt, Progress im Status-Endpoint - Frontend: Live-Fortschrittsanzeige mit Spinner, Phase, Query, Counter, Polling 1.5s, Krea AI in typeNames/typeIcons
202 lines
8.2 KiB
C#
202 lines
8.2 KiB
C#
using System;
|
|
using System.Net.Http;
|
|
using System.Net.Http.Headers;
|
|
using System.Text.Json;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Gott.Services
|
|
{
|
|
public class StripeCheckResult
|
|
{
|
|
public string Status { get; set; } // "ok", "invalid", "error"
|
|
public string Message { get; set; }
|
|
public bool IsLiveKey { get; set; }
|
|
public string Currency { get; set; }
|
|
public long? BalanceAvailable { get; set; }
|
|
public long? BalancePending { get; set; }
|
|
public string AccountId { get; set; }
|
|
public string AccountName { get; set; }
|
|
public string AccountEmail { get; set; }
|
|
public string AccountCountry { get; set; }
|
|
public bool? PayoutsEnabled { get; set; }
|
|
public bool? ChargesEnabled { get; set; }
|
|
}
|
|
|
|
public static class StripeKeyChecker
|
|
{
|
|
public static async Task<StripeCheckResult> CheckKeyAsync(IHttpClientFactory httpFactory, string apiKey)
|
|
{
|
|
try
|
|
{
|
|
var client = httpFactory.CreateClient();
|
|
client.Timeout = TimeSpan.FromSeconds(30);
|
|
|
|
var isLive = apiKey.StartsWith("sk_live_", StringComparison.Ordinal);
|
|
|
|
// Step 1: Check /v1/balance (read-only, no cost)
|
|
var balanceRequest = new HttpRequestMessage(HttpMethod.Get, "https://api.stripe.com/v1/balance");
|
|
balanceRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
|
|
|
|
var balanceResp = await client.SendAsync(balanceRequest);
|
|
var balanceBody = await balanceResp.Content.ReadAsStringAsync();
|
|
var balanceCode = (int)balanceResp.StatusCode;
|
|
|
|
if (balanceCode == 401)
|
|
{
|
|
return new StripeCheckResult
|
|
{
|
|
Status = "invalid",
|
|
Message = "Ungültig oder revoked (401)",
|
|
IsLiveKey = isLive
|
|
};
|
|
}
|
|
|
|
if (balanceCode >= 400)
|
|
{
|
|
// Parse error
|
|
var errorMsg = "Unbekannter Fehler";
|
|
try
|
|
{
|
|
using var errDoc = JsonDocument.Parse(balanceBody);
|
|
if (errDoc.RootElement.TryGetProperty("error", out var errObj) &&
|
|
errObj.TryGetProperty("message", out var msg))
|
|
errorMsg = msg.GetString();
|
|
}
|
|
catch { }
|
|
|
|
return new StripeCheckResult
|
|
{
|
|
Status = "error",
|
|
Message = $"API-Fehler ({balanceCode}): {errorMsg}",
|
|
IsLiveKey = isLive
|
|
};
|
|
}
|
|
|
|
// Parse balance
|
|
string currency = null;
|
|
long balanceAvailable = 0;
|
|
long balancePending = 0;
|
|
|
|
try
|
|
{
|
|
using var balDoc = JsonDocument.Parse(balanceBody);
|
|
var root = balDoc.RootElement;
|
|
|
|
if (root.TryGetProperty("available", out var available) && available.GetArrayLength() > 0)
|
|
{
|
|
var first = available[0];
|
|
if (first.TryGetProperty("currency", out var cur))
|
|
currency = cur.GetString()?.ToUpperInvariant();
|
|
if (first.TryGetProperty("amount", out var amt))
|
|
balanceAvailable = amt.GetInt64();
|
|
}
|
|
|
|
if (root.TryGetProperty("pending", out var pending) && pending.GetArrayLength() > 0)
|
|
{
|
|
var first = pending[0];
|
|
if (first.TryGetProperty("amount", out var amt))
|
|
balancePending = amt.GetInt64();
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
// Step 2: Get account info via /v1/account
|
|
string accountId = null;
|
|
string accountName = null;
|
|
string accountEmail = null;
|
|
string accountCountry = null;
|
|
bool? payoutsEnabled = null;
|
|
bool? chargesEnabled = null;
|
|
|
|
try
|
|
{
|
|
var accountRequest = new HttpRequestMessage(HttpMethod.Get, "https://api.stripe.com/v1/account");
|
|
accountRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
|
|
|
|
var accountResp = await client.SendAsync(accountRequest);
|
|
if ((int)accountResp.StatusCode == 200)
|
|
{
|
|
var accountBody = await accountResp.Content.ReadAsStringAsync();
|
|
using var accDoc = JsonDocument.Parse(accountBody);
|
|
var acc = accDoc.RootElement;
|
|
|
|
if (acc.TryGetProperty("id", out var id))
|
|
accountId = id.GetString();
|
|
if (acc.TryGetProperty("business_profile", out var bp) && bp.TryGetProperty("name", out var name))
|
|
accountName = name.GetString();
|
|
if (acc.TryGetProperty("email", out var email))
|
|
accountEmail = email.GetString();
|
|
if (acc.TryGetProperty("country", out var country))
|
|
accountCountry = country.GetString();
|
|
if (acc.TryGetProperty("payouts_enabled", out var pe))
|
|
payoutsEnabled = pe.GetBoolean();
|
|
if (acc.TryGetProperty("charges_enabled", out var ce))
|
|
chargesEnabled = ce.GetBoolean();
|
|
}
|
|
}
|
|
catch { }
|
|
|
|
// Build message
|
|
var parts = new System.Collections.Generic.List<string>();
|
|
|
|
parts.Add(isLive ? "🔴 LIVE Key" : "🟡 Test Key");
|
|
|
|
if (accountName != null)
|
|
parts.Add($"Account: {accountName}");
|
|
else if (accountId != null)
|
|
parts.Add($"Account: {accountId}");
|
|
|
|
if (accountEmail != null)
|
|
parts.Add($"Email: {accountEmail}");
|
|
|
|
if (accountCountry != null)
|
|
parts.Add($"Land: {accountCountry}");
|
|
|
|
if (currency != null)
|
|
{
|
|
var availFormatted = FormatAmount(balanceAvailable, currency);
|
|
var pendingFormatted = FormatAmount(balancePending, currency);
|
|
parts.Add($"Balance: {availFormatted} verfügbar, {pendingFormatted} ausstehend");
|
|
}
|
|
|
|
if (chargesEnabled.HasValue)
|
|
parts.Add(chargesEnabled.Value ? "✅ Charges aktiv" : "❌ Charges deaktiviert");
|
|
|
|
if (payoutsEnabled.HasValue)
|
|
parts.Add(payoutsEnabled.Value ? "✅ Payouts aktiv" : "❌ Payouts deaktiviert");
|
|
|
|
return new StripeCheckResult
|
|
{
|
|
Status = "ok",
|
|
Message = string.Join(" · ", parts),
|
|
IsLiveKey = isLive,
|
|
Currency = currency,
|
|
BalanceAvailable = balanceAvailable,
|
|
BalancePending = balancePending,
|
|
AccountId = accountId,
|
|
AccountName = accountName,
|
|
AccountEmail = accountEmail,
|
|
AccountCountry = accountCountry,
|
|
PayoutsEnabled = payoutsEnabled,
|
|
ChargesEnabled = chargesEnabled
|
|
};
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new StripeCheckResult
|
|
{
|
|
Status = "error",
|
|
Message = ex.Message,
|
|
IsLiveKey = apiKey.StartsWith("sk_live_", StringComparison.Ordinal)
|
|
};
|
|
}
|
|
}
|
|
|
|
private static string FormatAmount(long amountInCents, string currency)
|
|
{
|
|
var amount = amountInCents / 100.0;
|
|
return $"{amount:N2} {currency}";
|
|
}
|
|
}
|
|
}
|