using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using LittleShop.Enums; namespace LittleShop.Services; /// /// Mock SilverPAY service for testing when the real server is unavailable /// This generates realistic-looking crypto addresses and manages payments in memory /// public class MockSilverPayService : ISilverPayService { private readonly ILogger _logger; private readonly Dictionary _orders = new(); private readonly Random _random = new(); public MockSilverPayService(ILogger logger) { _logger = logger; _logger.LogWarning("🚧 Using MOCK SilverPAY service - payments won't be real!"); } public async Task CreateOrderAsync( string externalId, decimal amount, CryptoCurrency currency, string? description = null, string? webhookUrl = null) { await Task.Delay(100); // Simulate network delay var orderId = Guid.NewGuid().ToString(); var address = GenerateMockAddress(currency); var cryptoAmount = ConvertToCrypto(amount, currency); var order = new MockOrder { Id = orderId, ExternalId = externalId, Amount = cryptoAmount, Currency = currency.ToString(), PaymentAddress = address, Status = "pending", CreatedAt = DateTime.UtcNow, ExpiresAt = DateTime.UtcNow.AddHours(24), WebhookUrl = webhookUrl }; _orders[orderId] = order; _logger.LogInformation("✅ [MOCK] Created payment order {OrderId} for {Amount} {Currency} to address {Address}", orderId, cryptoAmount, currency, address); // Simulate payment confirmation after 5 seconds _ = Task.Run(async () => { await Task.Delay(5000); await SimulatePaymentConfirmation(orderId); }); return new SilverPayOrderResponse { Id = orderId, ExternalId = externalId, Amount = cryptoAmount, Currency = currency.ToString(), PaymentAddress = address, Status = "pending", CreatedAt = order.CreatedAt, ExpiresAt = order.ExpiresAt, CryptoAmount = cryptoAmount }; } public async Task GetOrderStatusAsync(string orderId) { await Task.Delay(50); // Simulate network delay if (!_orders.TryGetValue(orderId, out var order)) return null; return new SilverPayOrderResponse { Id = order.Id, ExternalId = order.ExternalId, Amount = order.Amount, Currency = order.Currency, PaymentAddress = order.PaymentAddress, Status = order.Status, CreatedAt = order.CreatedAt, ExpiresAt = order.ExpiresAt, PaidAt = order.PaidAt, TransactionHash = order.TransactionHash }; } public async Task ValidateWebhookAsync(string signature, string payload) { await Task.Delay(10); // In mock mode, always validate successfully return true; } public async Task GetExchangeRateAsync(string cryptoCurrency, string fiatCurrency = "GBP") { await Task.Delay(50); // Simulate network delay // Mock exchange rates (crypto to GBP) var rates = new Dictionary { { "BTC", 47500.00m }, { "ETH", 3100.00m }, { "LTC", 102.50m }, { "XMR", 220.00m }, { "DASH", 40.00m }, { "DOGE", 0.128m }, { "ZEC", 55.50m }, { "USDT", 0.80m } }; if (rates.TryGetValue(cryptoCurrency.ToUpper(), out var rate)) { _logger.LogInformation("📈 [MOCK] Exchange rate for {Currency}: £{Rate}", cryptoCurrency, rate); return rate; } _logger.LogWarning("⚠️ [MOCK] No exchange rate available for {Currency}", cryptoCurrency); return null; } public async Task ParseWebhookAsync(string payload) { await Task.Delay(10); try { var json = JsonDocument.Parse(payload); var root = json.RootElement; return new SilverPayWebhookNotification { OrderId = root.GetProperty("order_id").GetString() ?? "", ExternalId = root.GetProperty("external_id").GetString() ?? "", Status = root.GetProperty("status").GetString() ?? "confirmed", Amount = root.GetProperty("amount").GetDecimal(), Address = root.GetProperty("address").GetString() ?? "", TxHash = root.GetProperty("tx_hash").GetString(), Confirmations = root.TryGetProperty("confirmations", out var conf) ? conf.GetInt32() : 1, Timestamp = DateTime.UtcNow }; } catch (Exception ex) { _logger.LogError(ex, "Failed to parse webhook payload"); return null; } } private string GenerateMockAddress(CryptoCurrency currency) { return currency switch { CryptoCurrency.BTC => $"bc1q{GenerateRandomString(39)}", CryptoCurrency.ETH => $"0x{GenerateRandomHex(40)}", CryptoCurrency.LTC => $"ltc1q{GenerateRandomString(39)}", CryptoCurrency.XMR => $"4{GenerateRandomString(94)}", CryptoCurrency.DASH => $"X{GenerateRandomString(33)}", CryptoCurrency.DOGE => $"D{GenerateRandomString(33)}", CryptoCurrency.ZEC => $"t1{GenerateRandomString(33)}", CryptoCurrency.USDT => $"0x{GenerateRandomHex(40)}", _ => $"mock_{GenerateRandomString(32)}" }; } private decimal ConvertToCrypto(decimal gbpAmount, CryptoCurrency currency) { // Mock exchange rates (GBP to crypto) var rates = new Dictionary { { CryptoCurrency.BTC, 0.000021m }, { CryptoCurrency.ETH, 0.00032m }, { CryptoCurrency.LTC, 0.0098m }, { CryptoCurrency.XMR, 0.0045m }, { CryptoCurrency.DASH, 0.025m }, { CryptoCurrency.DOGE, 9.8m }, { CryptoCurrency.ZEC, 0.018m }, { CryptoCurrency.USDT, 1.25m } }; return gbpAmount * rates.GetValueOrDefault(currency, 0.00001m); } private string GenerateRandomString(int length) { const string chars = "abcdefghijklmnopqrstuvwxyz0123456789"; return new string(Enumerable.Range(0, length) .Select(_ => chars[_random.Next(chars.Length)]) .ToArray()); } private string GenerateRandomHex(int length) { const string chars = "0123456789abcdef"; return new string(Enumerable.Range(0, length) .Select(_ => chars[_random.Next(chars.Length)]) .ToArray()); } private async Task SimulatePaymentConfirmation(string orderId) { if (_orders.TryGetValue(orderId, out var order)) { order.Status = "confirmed"; order.PaidAt = DateTime.UtcNow; order.TransactionHash = $"0x{GenerateRandomHex(64)}"; _logger.LogInformation("💰 [MOCK] Payment confirmed for order {OrderId} - TX: {TxHash}", orderId, order.TransactionHash); // Simulate webhook callback if (!string.IsNullOrEmpty(order.WebhookUrl)) { await SendMockWebhook(order); } } } private async Task SendMockWebhook(MockOrder order) { try { using var client = new HttpClient(); var webhook = new { @event = "payment.confirmed", order_id = order.Id, external_id = order.ExternalId, status = "confirmed", amount = order.Amount, currency = order.Currency, tx_hash = order.TransactionHash, confirmations = 1, timestamp = DateTime.UtcNow }; var json = JsonSerializer.Serialize(webhook); var content = new StringContent(json, Encoding.UTF8, "application/json"); // Add mock signature client.DefaultRequestHeaders.Add("X-SilverPay-Signature", "mock_signature_" + Guid.NewGuid()); var response = await client.PostAsync(order.WebhookUrl, content); _logger.LogInformation("📤 [MOCK] Webhook sent to {Url} - Status: {Status}", order.WebhookUrl, response.StatusCode); } catch (Exception ex) { _logger.LogWarning(ex, "Failed to send mock webhook"); } } private class MockOrder { public string Id { get; set; } = ""; public string ExternalId { get; set; } = ""; public decimal Amount { get; set; } public string Currency { get; set; } = ""; public string PaymentAddress { get; set; } = ""; public string Status { get; set; } = ""; public DateTime CreatedAt { get; set; } public DateTime ExpiresAt { get; set; } public DateTime? PaidAt { get; set; } public string? TransactionHash { get; set; } public string? WebhookUrl { get; set; } } }