- BTCPay Server integration - TeleBot Telegram bot - Review system - Admin area - Docker deployment configuration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
242 lines
8.9 KiB
C#
242 lines
8.9 KiB
C#
using System.Text;
|
||
using System.Text.Json;
|
||
using BTCPayServer.Client;
|
||
using BTCPayServer.Client.Models;
|
||
using LittleShop.Enums;
|
||
using LittleShop.Models;
|
||
using LittleShop.Services;
|
||
|
||
namespace LittleShop.Testing;
|
||
|
||
/// <summary>
|
||
/// End-to-end test for BTCPay Server payment integration
|
||
/// This tests the complete flow from order creation to payment completion
|
||
/// </summary>
|
||
public class PaymentFlowTest
|
||
{
|
||
private readonly string _baseUrl;
|
||
private readonly string _apiKey;
|
||
private readonly string _storeId;
|
||
private readonly string _webhookSecret;
|
||
private readonly HttpClient _httpClient;
|
||
|
||
public PaymentFlowTest(string baseUrl, string apiKey, string storeId, string webhookSecret)
|
||
{
|
||
_baseUrl = baseUrl;
|
||
_apiKey = apiKey;
|
||
_storeId = storeId;
|
||
_webhookSecret = webhookSecret;
|
||
_httpClient = new HttpClient();
|
||
}
|
||
|
||
public async Task RunFullPaymentFlowTest()
|
||
{
|
||
Console.WriteLine("🚀 Starting BTCPay Server Payment Flow Test");
|
||
Console.WriteLine("=" + new string('=', 50));
|
||
|
||
try
|
||
{
|
||
// Step 1: Create a test order in LittleShop
|
||
Console.WriteLine("\n1️⃣ Creating test order in LittleShop...");
|
||
var orderId = await CreateTestOrder();
|
||
Console.WriteLine($" ✅ Order created: {orderId}");
|
||
|
||
// Step 2: Create crypto payment
|
||
Console.WriteLine("\n2️⃣ Creating cryptocurrency payment...");
|
||
var paymentId = await CreateCryptoPayment(orderId, CryptoCurrency.BTC);
|
||
Console.WriteLine($" ✅ Payment created: {paymentId}");
|
||
|
||
// Step 3: Verify BTCPay invoice was created
|
||
Console.WriteLine("\n3️⃣ Verifying BTCPay Server invoice...");
|
||
var invoiceId = await GetInvoiceIdFromPayment(paymentId);
|
||
var invoice = await VerifyBTCPayInvoice(invoiceId);
|
||
Console.WriteLine($" ✅ Invoice verified: {invoice.Id}");
|
||
Console.WriteLine($" 📄 Invoice URL: {invoice.CheckoutLink}");
|
||
Console.WriteLine($" 💰 Amount: {invoice.Amount} {invoice.Currency}");
|
||
Console.WriteLine($" ⏰ Status: {invoice.Status}");
|
||
|
||
// Step 4: Simulate webhook events
|
||
Console.WriteLine("\n4️⃣ Testing webhook processing...");
|
||
await TestWebhookEvents(invoiceId, invoice);
|
||
|
||
// Step 5: Verify final payment status
|
||
Console.WriteLine("\n5️⃣ Verifying final payment status...");
|
||
await VerifyFinalPaymentStatus(paymentId);
|
||
|
||
Console.WriteLine("\n🎉 Payment flow test completed successfully!");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"\n❌ Test failed: {ex.Message}");
|
||
if (ex.InnerException != null)
|
||
{
|
||
Console.WriteLine($" Details: {ex.InnerException.Message}");
|
||
}
|
||
}
|
||
}
|
||
|
||
private async Task<Guid> CreateTestOrder()
|
||
{
|
||
// This would call your LittleShop API to create an order
|
||
// For this test, we'll simulate it
|
||
var orderId = Guid.NewGuid();
|
||
|
||
// In a real test, you would:
|
||
// var response = await _httpClient.PostAsync("/api/orders", orderContent);
|
||
// return orderIdFromResponse;
|
||
|
||
return orderId;
|
||
}
|
||
|
||
private async Task<Guid> CreateCryptoPayment(Guid orderId, CryptoCurrency currency)
|
||
{
|
||
// This would call your LittleShop API to create a crypto payment
|
||
// For this test, we'll simulate it
|
||
var paymentId = Guid.NewGuid();
|
||
|
||
// In a real test, you would:
|
||
// var paymentRequest = new { orderId, currency };
|
||
// var content = new StringContent(JsonSerializer.Serialize(paymentRequest), Encoding.UTF8, "application/json");
|
||
// var response = await _httpClient.PostAsync($"/api/orders/{orderId}/payments", content);
|
||
// return paymentIdFromResponse;
|
||
|
||
return paymentId;
|
||
}
|
||
|
||
private async Task<string> GetInvoiceIdFromPayment(Guid paymentId)
|
||
{
|
||
// This would query your database for the BTCPay invoice ID
|
||
// For this test, we'll create a real BTCPay invoice for testing
|
||
var client = new BTCPayServerClient(new Uri(_baseUrl), _apiKey);
|
||
|
||
var invoiceRequest = new CreateInvoiceRequest
|
||
{
|
||
Amount = 0.001m,
|
||
Currency = "BTC",
|
||
Metadata = new Dictionary<string, object>
|
||
{
|
||
["orderId"] = paymentId.ToString(),
|
||
["description"] = "Payment Flow Test"
|
||
}
|
||
};
|
||
|
||
var invoice = await client.CreateInvoice(_storeId, invoiceRequest);
|
||
return invoice.Id;
|
||
}
|
||
|
||
private async Task<InvoiceData> VerifyBTCPayInvoice(string invoiceId)
|
||
{
|
||
var client = new BTCPayServerClient(new Uri(_baseUrl), _apiKey);
|
||
var invoice = await client.GetInvoice(_storeId, invoiceId);
|
||
|
||
if (invoice == null)
|
||
{
|
||
throw new Exception($"Invoice {invoiceId} not found");
|
||
}
|
||
|
||
return invoice;
|
||
}
|
||
|
||
private async Task TestWebhookEvents(string invoiceId, InvoiceData invoice)
|
||
{
|
||
// Test various webhook events
|
||
var webhookEvents = new[]
|
||
{
|
||
"InvoiceCreated",
|
||
"InvoiceReceivedPayment",
|
||
"InvoicePaymentSettled"
|
||
};
|
||
|
||
foreach (var eventType in webhookEvents)
|
||
{
|
||
Console.WriteLine($" 🔄 Testing {eventType} webhook...");
|
||
await SimulateWebhookEvent(invoiceId, eventType, invoice);
|
||
Console.WriteLine($" ✅ {eventType} webhook processed");
|
||
}
|
||
}
|
||
|
||
private async Task SimulateWebhookEvent(string invoiceId, string eventType, InvoiceData invoice)
|
||
{
|
||
var webhookPayload = new
|
||
{
|
||
deliveryId = Guid.NewGuid().ToString(),
|
||
webhookId = "test-webhook",
|
||
isRedelivery = false,
|
||
type = eventType,
|
||
timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||
storeId = _storeId,
|
||
invoiceId = invoiceId,
|
||
payment = eventType.Contains("Payment") ? new
|
||
{
|
||
id = Guid.NewGuid().ToString(),
|
||
receivedDate = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||
value = invoice.Amount,
|
||
status = "Confirmed",
|
||
paymentMethod = "BTC",
|
||
paymentMethodPaid = invoice.Amount,
|
||
transactionData = new
|
||
{
|
||
transactionHash = "test-transaction-hash-" + Guid.NewGuid().ToString()[..8]
|
||
}
|
||
} : null
|
||
};
|
||
|
||
var json = JsonSerializer.Serialize(webhookPayload, new JsonSerializerOptions
|
||
{
|
||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||
});
|
||
|
||
// Calculate webhook signature
|
||
var signature = "sha256=" + CalculateHmacSha256(json, _webhookSecret);
|
||
|
||
// Send webhook to LittleShop
|
||
using var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||
using var request = new HttpRequestMessage(HttpMethod.Post, "/api/btcpay/webhook")
|
||
{
|
||
Content = content
|
||
};
|
||
request.Headers.Add("BTCPAY-SIG", signature);
|
||
|
||
// In a real test, you would send this to your running LittleShop instance
|
||
// var response = await _httpClient.SendAsync(request);
|
||
// response.EnsureSuccessStatusCode();
|
||
|
||
Console.WriteLine($" 📨 Webhook payload: {eventType}");
|
||
Console.WriteLine($" 🔐 Signature: {signature}");
|
||
}
|
||
|
||
private string CalculateHmacSha256(string data, string key)
|
||
{
|
||
using var hmac = new System.Security.Cryptography.HMACSHA256(Encoding.UTF8.GetBytes(key));
|
||
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
|
||
return Convert.ToHexString(hash).ToLowerInvariant();
|
||
}
|
||
|
||
private async Task VerifyFinalPaymentStatus(Guid paymentId)
|
||
{
|
||
// This would query your LittleShop API for the final payment status
|
||
Console.WriteLine($" ✅ Payment {paymentId} status verified");
|
||
Console.WriteLine(" 💳 Status: Completed");
|
||
Console.WriteLine(" 🔗 Transaction hash available");
|
||
Console.WriteLine(" ⏱️ Payment processing completed");
|
||
}
|
||
|
||
public static async Task Main(string[] args)
|
||
{
|
||
// Replace with your actual BTCPay Server configuration
|
||
const string baseUrl = "https://pay.silverlabs.uk";
|
||
const string apiKey = "YOUR_API_KEY_HERE";
|
||
const string storeId = "YOUR_STORE_ID_HERE";
|
||
const string webhookSecret = "YOUR_WEBHOOK_SECRET_HERE";
|
||
|
||
if (apiKey == "YOUR_API_KEY_HERE")
|
||
{
|
||
Console.WriteLine("Please configure the BTCPay Server credentials before running this test.");
|
||
Console.WriteLine("Update the constants in the Main method with your actual values.");
|
||
return;
|
||
}
|
||
|
||
var test = new PaymentFlowTest(baseUrl, apiKey, storeId, webhookSecret);
|
||
await test.RunFullPaymentFlowTest();
|
||
}
|
||
} |