Fix SilverPay payment integration JSON serialization

- Changed JSON naming policy from CamelCase to SnakeCaseLower for SilverPay API compatibility
- Fixed field name from 'fiat_amount' to 'amount' in request body
- Used unique payment ID instead of order ID to avoid duplicate external_id conflicts
- Modified SilverPayApiResponse to handle string amounts from API
- Added [JsonIgnore] attributes to computed properties to prevent JSON serialization conflicts
- Fixed test compilation errors (mock service and enum casting issues)
- Updated SilverPay endpoint to http://10.0.0.52:8001/

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
SysAdmin 2025-09-22 04:38:28 +01:00
parent 034b8facee
commit 5138242a99
5 changed files with 23 additions and 11 deletions

View File

@ -269,7 +269,7 @@ public class OrdersWithVariantsTests : IClassFixture<TestWebApplicationFactory>
Price = 15.00m, Price = 15.00m,
CategoryId = category.Id, CategoryId = category.Id,
Weight = 1.0m, Weight = 1.0m,
WeightUnit = (int)LittleShop.Enums.ProductWeightUnit.Kilograms WeightUnit = LittleShop.Enums.ProductWeightUnit.Kilograms
}; };
var response = await _client.PostAsJsonAsync("/api/admin/products", productDto); var response = await _client.PostAsJsonAsync("/api/admin/products", productDto);

View File

@ -19,14 +19,16 @@ namespace LittleShop.Tests.Unit;
public class PushNotificationControllerTests public class PushNotificationControllerTests
{ {
private readonly Mock<IPushNotificationService> _pushServiceMock; private readonly Mock<IPushNotificationService> _pushServiceMock;
private readonly Mock<ITeleBotMessagingService> _teleBotServiceMock;
private readonly PushNotificationController _controller; private readonly PushNotificationController _controller;
private readonly Guid _testUserId = Guid.NewGuid(); private readonly Guid _testUserId = Guid.NewGuid();
public PushNotificationControllerTests() public PushNotificationControllerTests()
{ {
_pushServiceMock = new Mock<IPushNotificationService>(); _pushServiceMock = new Mock<IPushNotificationService>();
_controller = new PushNotificationController(_pushServiceMock.Object); _teleBotServiceMock = new Mock<ITeleBotMessagingService>();
_controller = new PushNotificationController(_pushServiceMock.Object, _teleBotServiceMock.Object);
// Setup controller context for authenticated user // Setup controller context for authenticated user
SetupControllerContext(); SetupControllerContext();
} }

View File

@ -57,8 +57,11 @@ public class CryptoPaymentService : ICryptoPaymentService
// Use SilverPAY // Use SilverPAY
_logger.LogInformation("Creating SilverPAY order for {Currency}", currency); _logger.LogInformation("Creating SilverPAY order for {Currency}", currency);
// Generate payment ID first to use as external_id
var paymentId = Guid.NewGuid();
var silverPayOrder = await _silverPayService.CreateOrderAsync( var silverPayOrder = await _silverPayService.CreateOrderAsync(
order.Id.ToString(), paymentId.ToString(), // Use unique payment ID instead of order ID
order.TotalAmount, order.TotalAmount,
currency, currency,
$"Order #{order.Id} - {order.Items.Count} items", $"Order #{order.Id} - {order.Items.Count} items",
@ -67,7 +70,7 @@ public class CryptoPaymentService : ICryptoPaymentService
var cryptoPayment = new CryptoPayment var cryptoPayment = new CryptoPayment
{ {
Id = Guid.NewGuid(), Id = paymentId, // Use the same payment ID
OrderId = orderId, OrderId = orderId,
Currency = currency, Currency = currency,
WalletAddress = silverPayOrder.PaymentAddress, WalletAddress = silverPayOrder.PaymentAddress,

View File

@ -54,7 +54,7 @@ public class SilverPayService : ISilverPayService
var request = new var request = new
{ {
external_id = externalId, external_id = externalId,
fiat_amount = amount, // Amount in GBP amount = amount, // Amount in GBP
fiat_currency = "GBP", fiat_currency = "GBP",
currency = currencyCode, currency = currencyCode,
webhook_url = webhookUrl ?? _configuration["SilverPay:DefaultWebhookUrl"], webhook_url = webhookUrl ?? _configuration["SilverPay:DefaultWebhookUrl"],
@ -63,14 +63,15 @@ public class SilverPayService : ISilverPayService
var json = JsonSerializer.Serialize(request, new JsonSerializerOptions var json = JsonSerializer.Serialize(request, new JsonSerializerOptions
{ {
PropertyNamingPolicy = JsonNamingPolicy.CamelCase, PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
}); });
var content = new StringContent(json, Encoding.UTF8, "application/json"); var content = new StringContent(json, Encoding.UTF8, "application/json");
_logger.LogDebug("Creating SilverPAY order - External ID: {ExternalId}, Amount: {Amount} GBP, Currency: {Currency}", _logger.LogInformation("Creating SilverPAY order - External ID: {ExternalId}, Amount: {Amount} GBP, Currency: {Currency}",
externalId, amount, currencyCode); externalId, amount, currencyCode);
_logger.LogInformation("SilverPAY request body: {RequestBody}", json);
// Add timeout to prevent hanging // Add timeout to prevent hanging
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
@ -260,7 +261,10 @@ public class SilverPayService : ISilverPayService
public string ExternalId { get; set; } = string.Empty; public string ExternalId { get; set; } = string.Empty;
[JsonPropertyName("amount")] [JsonPropertyName("amount")]
public decimal Amount { get; set; } public string AmountString { get; set; } = string.Empty;
[JsonIgnore]
public decimal Amount => decimal.TryParse(AmountString, out var amount) ? amount : 0;
[JsonPropertyName("currency")] [JsonPropertyName("currency")]
public string Currency { get; set; } = string.Empty; public string Currency { get; set; } = string.Empty;
@ -284,7 +288,10 @@ public class SilverPayService : ISilverPayService
public Dictionary<string, object>? PaymentDetails { get; set; } public Dictionary<string, object>? PaymentDetails { get; set; }
[JsonPropertyName("crypto_amount")] [JsonPropertyName("crypto_amount")]
public decimal? CryptoAmount { get; set; } public string? CryptoAmountString { get; set; }
[JsonIgnore]
public decimal? CryptoAmount => decimal.TryParse(CryptoAmountString, out var amount) ? amount : null;
[JsonPropertyName("tx_hash")] [JsonPropertyName("tx_hash")]
public string? TransactionHash { get; set; } public string? TransactionHash { get; set; }

View File

@ -9,7 +9,7 @@
"ExpiryInHours": 24 "ExpiryInHours": 24
}, },
"SilverPay": { "SilverPay": {
"BaseUrl": "http://31.97.57.205:8001", "BaseUrl": "http://10.0.0.52:8001",
"ApiKey": "sp_live_key_2025_production", "ApiKey": "sp_live_key_2025_production",
"WebhookSecret": "webhook_secret_2025", "WebhookSecret": "webhook_secret_2025",
"DefaultWebhookUrl": "http://localhost:8080/api/orders/payments/webhook", "DefaultWebhookUrl": "http://localhost:8080/api/orders/payments/webhook",