using Microsoft.AspNetCore.Mvc; using BTCPayServer.Client; using BTCPayServer.Client.Models; using Microsoft.AspNetCore.Authorization; using Newtonsoft.Json.Linq; using LittleShop.Services; using LittleShop.Enums; namespace LittleShop.Controllers; [ApiController] [Route("api/btcpay-test")] [Authorize(AuthenticationSchemes = "Cookies", Roles = "Admin")] public class BTCPayTestController : ControllerBase { private readonly IConfiguration _configuration; private readonly IBTCPayServerService _btcPayService; private readonly ILogger _logger; public BTCPayTestController( IConfiguration configuration, IBTCPayServerService btcPayService, ILogger logger) { _configuration = configuration; _btcPayService = btcPayService; _logger = logger; } [HttpGet("connection")] public async Task TestConnection() { try { var baseUrl = _configuration["BTCPayServer:BaseUrl"]; var apiKey = _configuration["BTCPayServer:ApiKey"]; if (string.IsNullOrEmpty(baseUrl) || string.IsNullOrEmpty(apiKey)) { return BadRequest(new { error = "BTCPay Server configuration missing" }); } // Create HttpClient with certificate bypass for internal networks var httpClient = new HttpClient(new HttpClientHandler() { ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true }); var client = new BTCPayServerClient(new Uri(baseUrl), apiKey, httpClient); // Test basic connection by getting server info var serverInfo = await client.GetServerInfo(); return Ok(new { status = "Connected", baseUrl = baseUrl, serverVersion = serverInfo?.Version, supportedPaymentMethods = serverInfo?.SupportedPaymentMethods, message = "BTCPay Server connection successful" }); } catch (Exception ex) { return StatusCode(500, new { error = ex.Message, type = ex.GetType().Name, baseUrl = _configuration["BTCPayServer:BaseUrl"] }); } } [HttpGet("stores")] public async Task GetStores() { try { var baseUrl = _configuration["BTCPayServer:BaseUrl"]; var apiKey = _configuration["BTCPayServer:ApiKey"]; // Create HttpClient with certificate bypass for internal networks var httpClient = new HttpClient(new HttpClientHandler() { ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true }); var client = new BTCPayServerClient(new Uri(baseUrl), apiKey, httpClient); // Get available stores var stores = await client.GetStores(); return Ok(new { stores = stores.Select(s => new { id = s.Id, name = s.Name, website = s.Website, defaultCurrency = s.DefaultCurrency }).ToList(), message = "Stores retrieved successfully" }); } catch (Exception ex) { return StatusCode(500, new { error = ex.Message, type = ex.GetType().Name }); } } [HttpGet("invoice/{invoiceId}")] public async Task GetInvoiceDetails(string invoiceId) { try { var invoice = await _btcPayService.GetInvoiceAsync(invoiceId); if (invoice == null) { return NotFound(new { error = "Invoice not found" }); } // BTCPay Server v2 manages addresses internally // Customers use the CheckoutLink for payments var paymentInfo = new { checkoutMethod = "BTCPay Checkout", info = "Use the checkout link to complete payment" }; return Ok(new { invoiceId = invoice.Id, status = invoice.Status, amount = invoice.Amount, currency = invoice.Currency, checkoutLink = invoice.CheckoutLink, expiresAt = invoice.ExpirationTime, paymentMethods = paymentInfo, metadata = invoice.Metadata, message = "Invoice details retrieved successfully" }); } catch (Exception ex) { _logger.LogError(ex, "Failed to get invoice {InvoiceId}", invoiceId); return StatusCode(500, new { error = ex.Message, type = ex.GetType().Name }); } } [HttpPost("test-invoice")] public async Task CreateTestInvoice([FromBody] TestInvoiceRequest request) { try { var baseUrl = _configuration["BTCPayServer:BaseUrl"]; var apiKey = _configuration["BTCPayServer:ApiKey"]; var storeId = _configuration["BTCPayServer:StoreId"]; if (string.IsNullOrEmpty(storeId)) { return BadRequest(new { error = "Store ID not configured" }); } // Create HttpClient with certificate bypass for internal networks var httpClient = new HttpClient(new HttpClientHandler() { ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true }); var client = new BTCPayServerClient(new Uri(baseUrl), apiKey, httpClient); // Create test invoice var invoiceRequest = new CreateInvoiceRequest { Amount = request.Amount, Currency = request.Currency ?? "GBP", Metadata = JObject.FromObject(new { orderId = $"test-{Guid.NewGuid()}", source = "LittleShop-Test" }) }; var invoice = await client.CreateInvoice(storeId, invoiceRequest); return Ok(new { status = "Invoice Created", invoiceId = invoice.Id, amount = invoice.Amount, currency = invoice.Currency, checkoutLink = invoice.CheckoutLink, expiresAt = invoice.ExpirationTime, message = "Test invoice created successfully" }); } catch (Exception ex) { return StatusCode(500, new { error = ex.Message, type = ex.GetType().Name }); } } [HttpPost("test-payment")] public async Task CreateTestPayment([FromBody] TestPaymentRequest request) { try { // Create a test order ID var testOrderId = $"test-order-{Guid.NewGuid():N}".Substring(0, 20); _logger.LogInformation("Creating test payment for {Currency} with amount {Amount} GBP", request.CryptoCurrency, request.Amount); // Use the actual service to create an invoice var invoiceId = await _btcPayService.CreateInvoiceAsync( request.Amount, request.CryptoCurrency, testOrderId, "Test payment from BTCPay diagnostic endpoint" ); // Get the invoice details var invoice = await _btcPayService.GetInvoiceAsync(invoiceId); // BTCPay Server v2 uses checkout links instead of exposing raw addresses var checkoutUrl = invoice?.CheckoutLink; return Ok(new { status = "Success", invoiceId = invoiceId, orderId = testOrderId, amount = request.Amount, currency = "GBP", requestedCrypto = request.CryptoCurrency.ToString(), checkoutLink = checkoutUrl, paymentUrl = checkoutUrl ?? $"https://{_configuration["BTCPayServer:BaseUrl"]}/i/{invoiceId}", message = !string.IsNullOrEmpty(checkoutUrl) ? "✅ Test payment created successfully - Use checkout link to complete payment" : "⚠️ Invoice created but checkout link not available - Check BTCPay configuration" }); } catch (Exception ex) { _logger.LogError(ex, "Failed to create test payment"); return StatusCode(500, new { error = ex.Message, type = ex.GetType().Name, hint = "Check that BTCPay Server has wallets configured for the requested currency" }); } } } public class TestInvoiceRequest { public decimal Amount { get; set; } = 0.01m; public string? Currency { get; set; } = "GBP"; } public class TestPaymentRequest { public decimal Amount { get; set; } = 10.00m; public CryptoCurrency CryptoCurrency { get; set; } = CryptoCurrency.BTC; }