Add kaigen debit credit for backtest
This commit is contained in:
165
src/Managing.Infrastructure.Web3/Services/KaigenService.cs
Normal file
165
src/Managing.Infrastructure.Web3/Services/KaigenService.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Nethereum.Signer;
|
||||
|
||||
namespace Managing.Infrastructure.Evm.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration settings for Kaigen credit management service.
|
||||
/// The PrivateKey should be set via the KAIGEN_PRIVATE_KEY environment variable for security.
|
||||
/// </summary>
|
||||
public class KaigenSettings
|
||||
{
|
||||
public string BaseUrl { get; set; } = "https://api.kaigen.managing.live";
|
||||
public string DebitEndpoint { get; set; } = "/api/credits/debit";
|
||||
public string RefundEndpoint { get; set; } = "/api/credits/refund";
|
||||
public string PrivateKey { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class KaigenService : IKaigenService
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly KaigenSettings _settings;
|
||||
private readonly ILogger<KaigenService> _logger;
|
||||
private readonly JsonSerializerOptions _jsonOptions;
|
||||
|
||||
public KaigenService(IOptions<KaigenSettings> options, ILogger<KaigenService> logger)
|
||||
{
|
||||
_httpClient = new HttpClient();
|
||||
_settings = options.Value;
|
||||
_logger = logger;
|
||||
_jsonOptions = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||
};
|
||||
|
||||
// Validate required settings
|
||||
if (string.IsNullOrEmpty(_settings.PrivateKey))
|
||||
{
|
||||
throw new InvalidOperationException("Kaigen PrivateKey is not configured. Please set the KAIGEN_PRIVATE_KEY environment variable.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(_settings.BaseUrl))
|
||||
{
|
||||
throw new InvalidOperationException("Kaigen BaseUrl is not configured.");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> DebitUserCreditsAsync(string userName, decimal debitAmount)
|
||||
{
|
||||
try
|
||||
{
|
||||
var requestId = Guid.NewGuid().ToString();
|
||||
|
||||
// Create the payload for signing
|
||||
var payload = new
|
||||
{
|
||||
RequestId = requestId,
|
||||
UserName = userName,
|
||||
DebitAmount = debitAmount
|
||||
};
|
||||
|
||||
// Create the message to sign (concatenate the values)
|
||||
var message = $"{requestId}{userName}{debitAmount}";
|
||||
|
||||
// Sign the message
|
||||
var signature = SignMessage(message, _settings.PrivateKey);
|
||||
|
||||
// Create the request payload
|
||||
var requestPayload = new
|
||||
{
|
||||
requestId = requestId,
|
||||
userName = userName,
|
||||
debitAmount = debitAmount,
|
||||
signature = signature
|
||||
};
|
||||
|
||||
_logger.LogInformation("Debiting {Amount} credits for user {UserName} with request ID {RequestId}",
|
||||
debitAmount, userName, requestId);
|
||||
|
||||
var response = await _httpClient.PutAsJsonAsync(
|
||||
$"{_settings.BaseUrl}{_settings.DebitEndpoint}",
|
||||
requestPayload,
|
||||
_jsonOptions);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var errorContent = await response.Content.ReadAsStringAsync();
|
||||
_logger.LogError("Failed to debit credits. Status: {StatusCode}, Error: {Error}",
|
||||
response.StatusCode, errorContent);
|
||||
throw new Exception($"Failed to debit credits: {response.StatusCode} - {errorContent}");
|
||||
}
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<KaigenResponse>(_jsonOptions);
|
||||
_logger.LogInformation("Successfully debited {Amount} credits for user {UserName}",
|
||||
debitAmount, userName);
|
||||
|
||||
return requestId;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error debiting credits for user {UserName}", userName);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> RefundUserCreditsAsync(string requestId, string userName)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the message to sign (concatenate the values)
|
||||
var message = $"{requestId}{userName}";
|
||||
|
||||
// Sign the message
|
||||
var signature = SignMessage(message, _settings.PrivateKey);
|
||||
|
||||
// Create the request payload
|
||||
var requestPayload = new
|
||||
{
|
||||
requestId = requestId,
|
||||
userName = userName,
|
||||
signature = signature
|
||||
};
|
||||
|
||||
_logger.LogInformation("Refunding credits for user {UserName} with request ID {RequestId}",
|
||||
userName, requestId);
|
||||
|
||||
var response = await _httpClient.PutAsJsonAsync(
|
||||
$"{_settings.BaseUrl}{_settings.RefundEndpoint}",
|
||||
requestPayload,
|
||||
_jsonOptions);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var errorContent = await response.Content.ReadAsStringAsync();
|
||||
_logger.LogError("Failed to refund credits. Status: {StatusCode}, Error: {Error}",
|
||||
response.StatusCode, errorContent);
|
||||
return false;
|
||||
}
|
||||
|
||||
_logger.LogInformation("Successfully refunded credits for user {UserName}", userName);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error refunding credits for user {UserName}", userName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private string SignMessage(string message, string privateKey)
|
||||
{
|
||||
var signer = new EthereumMessageSigner();
|
||||
var signature = signer.EncodeUTF8AndSign(message, new EthECKey(privateKey));
|
||||
return signature;
|
||||
}
|
||||
|
||||
private class KaigenResponse
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user