diff --git a/.github/workflows/balance-nuget.yml b/.github/workflows/balance-nuget.yml new file mode 100644 index 0000000..42abd26 --- /dev/null +++ b/.github/workflows/balance-nuget.yml @@ -0,0 +1,48 @@ +name: "Deploy to NuGet" + +on: + workflow_dispatch: + push: + branches: + - main + +env: + PROJECT_PATH: "src/Balance/SubsBase.SDK.Balance.csproj" + PACKAGE_OUTPUT_DIRECTORY: ${{ github.workspace }}/output + NUGET_SOURCE_URL: 'https://api.nuget.org/v3/index.json' + +jobs: + deploy: + name: 'Deploy' + runs-on: 'ubuntu-latest' + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + + - name: 'Install dotnet' + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '8.x' + + - name: 'Restore packages' + run: dotnet restore ${{ env.PROJECT_PATH }} + + - name: 'Build project' + run: dotnet build ${{ env.PROJECT_PATH }} --no-restore --configuration Release + + - name: 'Get version' + uses: kzrnm/get-net-sdk-project-versions-action@v1.3.0 + id: get-version + with: + proj-path: ${{ env.PROJECT_PATH }} + + - name: 'Get package name' + id: get-package-name + run: echo "::set-output name=package-name::SubsBaseBalance.${{ steps.get-version.outputs.version }}" + + - name: 'Pack project' + run: dotnet pack ${{ env.PROJECT_PATH }} --no-restore --no-build --configuration Release --include-symbols -p:PackageVersion=${{ steps.get-version.outputs.version }} --output ${{ env.PACKAGE_OUTPUT_DIRECTORY }} + + - name: 'Push package' + run: dotnet nuget push ${{ env.PACKAGE_OUTPUT_DIRECTORY }}/${{ steps.get-package-name.outputs.package-name }}.nupkg -k ${{ secrets.NUGET_AUTH_TOKEN }} -s ${{ env.NUGET_SOURCE_URL }} + \ No newline at end of file diff --git a/src/.idea/.idea.SubsBase.SDK/.idea/vcs.xml b/src/.idea/.idea.SubsBase.SDK/.idea/vcs.xml index 64713b8..6c0b863 100644 --- a/src/.idea/.idea.SubsBase.SDK/.idea/vcs.xml +++ b/src/.idea/.idea.SubsBase.SDK/.idea/vcs.xml @@ -1,7 +1,6 @@ - \ No newline at end of file diff --git a/src/Balance/SubsBase.SDK.Balance.Test/BalanceTests.cs b/src/Balance/SubsBase.SDK.Balance.Test/BalanceTests.cs new file mode 100644 index 0000000..506cd68 --- /dev/null +++ b/src/Balance/SubsBase.SDK.Balance.Test/BalanceTests.cs @@ -0,0 +1,315 @@ +using FluentAssertions; +using SubsBase.SDK.Balance.Contracts; + +namespace SubsBase.SDK.Balance.Test; + +[NonParallelizable] +[TestFixture] +[SingleThreaded] +public class BalanceTests +{ + private static readonly string _publicKey = "publicKey"; + private static readonly string _privateKey = "privateKey"; + private static Guid _1StBalanceId; + private static Guid _2NdBalanceId; + private static string _releasedAmountId; + private static string _capturedAmountId; + private static BalanceSdk balanceSdk; + + [OneTimeSetUp] + public void OneTimeInit() + { + balanceSdk = new BalanceSdk(_publicKey, _privateKey, environment: Environment.Development); + } + + [Test, Order(1)] + public void Step_01_AddNewBalanceWithoutMetaData_ShouldCreateNewBalance() + { + var balance = balanceSdk.Balance.CreateAsync(new BalanceInfoNew() + { + Unit = "EGP", + AllowTotalBalanceToBeNegative = false, + Metadata = new Dictionary() + { + {"customerId" , "subsbase_customer1"}, + {"age", "25"} + }, + }).GetAwaiter().GetResult(); + + balance.IsSuccess.Should().BeTrue(); + balance.Value.Should().NotBeNull(); + _1StBalanceId = balance.Value!.BalanceId; + } + + [Test, Order(2)] + public void Step_02_AddNewBalance_ShouldCreateNewBalance() + { + var balance = balanceSdk.Balance.CreateAsync(new BalanceInfoNew() + { + Unit = "EGP", + Metadata = new Dictionary() + { + {"customerId" , "subsbase_customer2"}, + {"age", "35"} + }, + AllowTotalBalanceToBeNegative = false, + }).GetAwaiter().GetResult(); + + balance.IsSuccess.Should().BeTrue(); + balance.Value.Should().NotBeNull(); + _2NdBalanceId = balance.Value!.BalanceId; + } + + [Test, Order(3)] + public void Step_03_AddCreditBalanceMovement_ShouldLoadBalanceWithSpecificAmount() + { + var balance = balanceSdk.BalanceMovement.CreateAsync(new BalanceMovementNew() + { + BalanceId = _1StBalanceId, + Type = MovementType.Credit, + Amount = 1000, + Description = "Load Balance With 1000 EGP", + }).GetAwaiter().GetResult(); + + balance.IsSuccess.Should().BeTrue(); + balance.Value.Should().NotBeNull(); + balance.Value!.AvailableAmount.Should().Be(1000.0M); + balance.Value.TotalAmount.Should().Be(1000.0M); + balance.Value.OnHoldAmount.Should().Be(0.0M); + } + + [Test, Order(4)] + public void Step_04_HoldBalanceAmountToBeCaptured_ShouldHoldAmount() + { + var balance = balanceSdk.OnHoldAmount.CreateAsync(new HoldAmountNew() + { + BalanceId = _1StBalanceId, + Amount = 1000, + Description = "Hold 1000 EGP", + ReleaseDate = DateTime.UtcNow.AddSeconds(10) + }).GetAwaiter().GetResult(); + + balance.IsSuccess.Should().BeTrue(); + balance.Value.Should().NotBeNull(); + balance.Value!.OnHoldAmountId.Should().NotBeNullOrWhiteSpace(); + _capturedAmountId = balance.Value!.OnHoldAmountId; + } + + [Test, Order(5)] + public void Step_05_CaptureOnHoldAmount_ShouldCaptureOnHoldAmount() + { + var balance = balanceSdk.OnHoldAmount.DeleteAsync(_capturedAmountId, isCaptured: true).GetAwaiter().GetResult(); + + balance.IsSuccess.Should().BeTrue(); + balance.Value.Should().NotBeNull(); + balance.Value!.AvailableAmount.Should().Be(0.0M); + balance.Value.TotalAmount.Should().Be(0.0M); + balance.Value.OnHoldAmount.Should().Be(0.0M); + } + + [Test, Order(6)] + public void Step_06_AddCreditBalanceMovement_ShouldLoadBalanceWithSpecificAmount() + { + var balance = balanceSdk.BalanceMovement.CreateAsync(new BalanceMovementNew() + { + BalanceId = _1StBalanceId, + Type = MovementType.Credit, + Amount = 1000, + Description = "Load Balance With 1000 EGP", + }).GetAwaiter().GetResult(); + + balance.IsSuccess.Should().BeTrue(); + balance.Value.Should().NotBeNull(); + balance.Value!.AvailableAmount.Should().Be(1000.0M); + balance.Value.TotalAmount.Should().Be(1000.0M); + balance.Value.OnHoldAmount.Should().Be(0.0M); + } + + + + [Test, Order(7)] + public void Step_07_AddDebitBalanceMovementWithAmountLargerThanAvailable_ShouldReturnErrorInsufficientFund() + { + var balance = balanceSdk.BalanceMovement.CreateAsync(new BalanceMovementNew() + { + BalanceId = _1StBalanceId, + Type = MovementType.Debit, + Amount = 1500, + Description = "Unload Balance With 1500 EGP", + }).GetAwaiter().GetResult(); + + balance.IsSuccess.Should().BeFalse(); + balance.Value.Should().BeNull(); + balance.Message.Should().NotBeNullOrWhiteSpace(); + } + + [Test, Order(8)] + public void Step_08_AddDebitBalanceMovementWithAmountLessThanAvailable_ShouldUnloadBalanceWithSpecificAmount() + { + var balance = balanceSdk.BalanceMovement.CreateAsync(new BalanceMovementNew() + { + BalanceId = _1StBalanceId, + Type = MovementType.Debit, + Amount = 500, + Description = "Unload Balance With 500 EGP", + }).GetAwaiter().GetResult(); + + balance.IsSuccess.Should().BeTrue(); + balance.Value.Should().NotBeNull(); + balance.Value!.AvailableAmount.Should().Be(500.0M); + balance.Value!.TotalAmount.Should().Be(500.0M); + balance.Value!.OnHoldAmount.Should().Be(0.0M); + } + + [Test, Order(9)] + public void Step_09_HoldBalanceAmount_ShouldHoldAmount() + { + var balance = balanceSdk.OnHoldAmount.CreateAsync(new HoldAmountNew() + { + BalanceId = _1StBalanceId, + Amount = 200, + Description = "Hold 200 EGP", + }).GetAwaiter().GetResult(); + + balance.IsSuccess.Should().BeTrue(); + balance.Value.Should().NotBeNull(); + balance.Value!.OnHoldAmountId.Should().NotBeNullOrWhiteSpace(); + _releasedAmountId = balance.Value!.OnHoldAmountId; + } + + [Test, Order(10)] + public void Step_10_GetOnHoldAmountWithId_ShouldGetOnHoldAmountDetails() + { + var onHoldAmountResult = balanceSdk.OnHoldAmount.GetAsync(_releasedAmountId).GetAwaiter().GetResult(); + + onHoldAmountResult.IsSuccess.Should().BeTrue(); + onHoldAmountResult.Value.Should().NotBeNull(); + onHoldAmountResult.Value!.BalanceId.Should().Be(_1StBalanceId); + onHoldAmountResult.Value!.Amount.Should().Be(200.0M); + } + + + [Test, Order(10)] + public void Step_10_ReleaseOnHoldAmount_ShouldReleaseOnHoldAmount() + { + var balance = balanceSdk.OnHoldAmount.DeleteAsync(_releasedAmountId, isCaptured: false).GetAwaiter().GetResult(); + + balance.IsSuccess.Should().BeTrue(); + balance.Value.Should().NotBeNull(); + balance.Value!.AvailableAmount.Should().Be(500.0M); + balance.Value!.TotalAmount.Should().Be(500.0M); + balance.Value!.OnHoldAmount.Should().Be(0.0M); + + } + + [Test, Order(11)] + public void Step_11_HoldBalanceAmountToBeCaptured_ShouldHoldAmount() + { + var balance = balanceSdk.OnHoldAmount.CreateAsync(new HoldAmountNew() + { + BalanceId = _1StBalanceId, + Amount = 200, + Description = "Hold 200 EGP", + ReleaseDate = DateTime.UtcNow.AddHours(1) + }).GetAwaiter().GetResult(); + + balance.IsSuccess.Should().BeTrue(); + balance.Value.Should().NotBeNull(); + balance.Value!.OnHoldAmountId.Should().NotBeNullOrWhiteSpace(); + _capturedAmountId = balance.Value!.OnHoldAmountId; + } + + [Test, Order(12)] + public void Step_12_CaptureOnHoldAmount_ShouldCaptureOnHoldAmount() + { + var balance = balanceSdk.OnHoldAmount.DeleteAsync(_capturedAmountId, isCaptured: true).GetAwaiter().GetResult(); + + balance.IsSuccess.Should().BeTrue(); + balance.Value.Should().NotBeNull(); + balance.Value!.AvailableAmount.Should().Be(300.0M); + balance.Value!.TotalAmount.Should().Be(300.0M); + balance.Value!.OnHoldAmount.Should().Be(0.0M); + } + + [Test, Order(13)] + public void Step_13_HoldBalanceAmountWithExpirationDate_ShouldHoldAmount() + { + var balance = balanceSdk.OnHoldAmount.CreateAsync(new HoldAmountNew() + { + BalanceId = _1StBalanceId, + Amount = 200, + Description = "Hold 200 EGP", + ReleaseDate = DateTime.UtcNow.AddDays(1) + }).GetAwaiter().GetResult(); + + balance.IsSuccess.Should().BeTrue(); + balance.Value.Should().NotBeNull(); + balance.Value!.OnHoldAmountId.Should().NotBeNullOrWhiteSpace(); + _releasedAmountId = balance.Value!.OnHoldAmountId; + + var onHoldAmount = balanceSdk.OnHoldAmount.GetAsync(balance.Value!.OnHoldAmountId).GetAwaiter().GetResult(); + onHoldAmount.IsSuccess.Should().BeTrue(); + onHoldAmount.Value.Should().NotBeNull(); + onHoldAmount.Value.Amount.Should().Be(expected: 200.0M); + onHoldAmount.Value.ReleaseDate.Should().NotBeNull(); + onHoldAmount.Value.ReleaseDate.Value.Date.Should().Be(DateTime.UtcNow.AddDays(1).Date); + } + + + [Test, Order(14)] + public void Step_14_AddCreditBalanceMovementWithExpirationDate_ShouldLoadBalanceWithSpecificAmount() + { + var balance = balanceSdk.BalanceMovement.CreateAsync(new BalanceMovementNew() + { + BalanceId = _1StBalanceId, + Type = MovementType.Credit, + Amount = 1000, + Description = "Load Balance With 1000 EGP", + ExpirationDate = DateTime.UtcNow.AddSeconds(5) + }).GetAwaiter().GetResult(); + + balance.IsSuccess.Should().BeTrue(); + balance.Value.Should().NotBeNull(); + balance.Value!.AvailableAmount.Should().Be(1100.0M); + balance.Value!.TotalAmount.Should().Be(1300.0M); + balance.Value!.OnHoldAmount.Should().Be(200.0M); + + } + + [Test, Order(15)] + public void Step_15_GetBalanceDetails_ShouldGetAllBalanceDetails() + { + Thread.Sleep(20 * 1000); + var balance = balanceSdk.Balance.GetAsync(_1StBalanceId).GetAwaiter().GetResult(); + balance.Should().NotBeNull(); + balance.IsSuccess.Should().BeTrue(); + balance.Value!.BalanceSummary.AvailableAmount.Should().Be(100.0M); + balance.Value!.BalanceSummary.TotalAmount.Should().Be(300.0M); + balance.Value!.BalanceSummary.OnHoldAmount.Should().Be(200.0M); + } + + [Test, Order(16)] + public void Step_16_GetAllBalancesDetails_ShouldGetAllBalances() + { + var balance = balanceSdk.Balance.GetAllAsync(new List{_1StBalanceId.ToString(),_2NdBalanceId.ToString()}).GetAwaiter().GetResult(); + balance.Should().NotBeNull(); + balance.IsSuccess.Should().BeTrue(); + balance.Value.Count.Should().Be(expected: 2); + balance.Value[0].BalanceId.Should().Be(_1StBalanceId); + balance.Value[0].AvailableAmount.Should().BePositive(); + balance.Value[0].OnHoldAmount.Should().BePositive(); + balance.Value[0].Unit.Should().NotBeNullOrEmpty(); + } + + [Test, Order(17)] + public void Step_17_GetAllBalancesOnHoldAmountsDetails_ShouldGetAllOnHoldAmountsDetails() + { + var balance = balanceSdk.OnHoldAmount.GetBalanceOnHoldAmountsAsync(_1StBalanceId).GetAwaiter().GetResult(); + balance.Should().NotBeNull(); + balance.IsSuccess.Should().BeTrue(); + balance.Value.OnHoldAmounts.Should().NotBeEmpty(); + balance.Value.BalanceId.Should().Be(_1StBalanceId); + balance.Value.OnHoldAmounts.Sum(x => x.Amount).Should().Be(200); + } + +} \ No newline at end of file diff --git a/src/Balance/SubsBase.SDK.Balance.Test/SubsBase.SDK.Balance.Test.csproj b/src/Balance/SubsBase.SDK.Balance.Test/SubsBase.SDK.Balance.Test.csproj new file mode 100644 index 0000000..3acc110 --- /dev/null +++ b/src/Balance/SubsBase.SDK.Balance.Test/SubsBase.SDK.Balance.Test.csproj @@ -0,0 +1,30 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Balance/SubsBase.SDK.Balance/BalanceConfiguration.cs b/src/Balance/SubsBase.SDK.Balance/BalanceConfiguration.cs new file mode 100644 index 0000000..726400d --- /dev/null +++ b/src/Balance/SubsBase.SDK.Balance/BalanceConfiguration.cs @@ -0,0 +1,14 @@ +namespace SubsBase.SDK.Balance; + +public class BalanceConfiguration +{ + public string PublicKey { get; set; } + public string PrivateKey { get; set; } + + public BalanceConfiguration(string publicKey, string privateKey) + { + PublicKey = publicKey ?? throw new ArgumentNullException(nameof(PublicKey)); + PrivateKey = privateKey ?? throw new ArgumentNullException(nameof(PrivateKey)); + } + +} \ No newline at end of file diff --git a/src/Balance/SubsBase.SDK.Balance/BalanceSdk.cs b/src/Balance/SubsBase.SDK.Balance/BalanceSdk.cs new file mode 100644 index 0000000..aa81c94 --- /dev/null +++ b/src/Balance/SubsBase.SDK.Balance/BalanceSdk.cs @@ -0,0 +1,29 @@ + +using SubsBase.Common.ApiClientHelper; +using SubsBase.SDK.Balance.Client; + +namespace SubsBase.SDK.Balance; + +public class BalanceSdk +{ + private readonly BalanceConfiguration _configuration; + private readonly IConfigurationConstants _configurationConstants; + + public BalanceSdk( + string publicKey, + string privateKey, + Environment environment = Environment.Development, + string? environmentBaseUrl = null + ) + { + _configuration = new BalanceConfiguration(publicKey, privateKey); + _configurationConstants = environment == Environment.Development + ? new DevelopmentConstants(environmentBaseUrl) + : new ProductionConstants(); + } + + public BalanceClient Balance => new BalanceClient(new ApiClient(new HttpClient(), _configurationConstants.BalanceUri), _configuration); + public BalanceMovementClient BalanceMovement => new BalanceMovementClient(new ApiClient(new HttpClient(), _configurationConstants.BalanceMovementUri), _configuration); + public OnHoldAmountClient OnHoldAmount => new OnHoldAmountClient(new ApiClient(new HttpClient(), _configurationConstants.OnHoldAmountUri), _configuration); + +} \ No newline at end of file diff --git a/src/Balance/SubsBase.SDK.Balance/Client/BalanceClient.cs b/src/Balance/SubsBase.SDK.Balance/Client/BalanceClient.cs new file mode 100644 index 0000000..23f7fb6 --- /dev/null +++ b/src/Balance/SubsBase.SDK.Balance/Client/BalanceClient.cs @@ -0,0 +1,98 @@ +using System.Text.Json; +using SubsBase.Common.ApiClientHelper; +using SubsBase.SDK.Balance.Contracts; +using SubsBase.SDK.Balance.Services; + +namespace SubsBase.SDK.Balance.Client; + +public class BalanceClient +{ + private readonly ApiClient _apiClient; + private readonly SigningService _signingService; + private readonly BalanceConfiguration _balanceConfiguration; + + public BalanceClient(ApiClient apiClient, BalanceConfiguration balanceConfiguration) + { + _apiClient = apiClient; + _balanceConfiguration = balanceConfiguration; + _signingService = new SigningService(); + } + + public async Task> CreateAsync(BalanceInfoNew balanceInfoNew) + { + var signaturePayload = new SortedDictionary() + { + { "allowTotalBalanceToBeNegative", balanceInfoNew.AllowTotalBalanceToBeNegative }, + { "metadata", balanceInfoNew.Metadata}, + { "unit", balanceInfoNew.Unit}, + }; + + return await _apiClient.PostAsync( + uri: "", + request: balanceInfoNew, + headers: new Dictionary() + { + { "publicKey", _balanceConfiguration.PublicKey }, + { "signature", _signingService.SignPayload(JsonSerializer.Serialize(signaturePayload), _balanceConfiguration.PrivateKey)} + }); + } + + public async Task>> GetAllAsync(IEnumerable ids) + { + var signaturePayload = new Dictionary() + { + { "ids",string.Join(',',ids)} + }; + + return await _apiClient.GetAsync>( + uri: $"?ids={string.Join(',', ids)}", + headers: new Dictionary() + { + { "publicKey", _balanceConfiguration.PublicKey }, + { + "signature", _signingService.SignPayload(JsonSerializer.Serialize(signaturePayload), + _balanceConfiguration.PrivateKey) + }, + }); + } + + public async Task> GetAsync(Guid id) + { + var signaturePayload = new Dictionary() + { + { "id", id.ToString()} + }; + + return await _apiClient.GetAsync( + uri: @$"{id}", + headers: new Dictionary() + { + { "publicKey", _balanceConfiguration.PublicKey }, + { + "signature", _signingService.SignPayload(JsonSerializer.Serialize(signaturePayload), + _balanceConfiguration.PrivateKey) + }, + }); + } + + public async Task> UpdateAsync(Guid id, BalanceInfoUpdate balanceInfoToUpdate) + { + var signaturePayload = new Dictionary() + { + { "id", id }, + { "metadata", JsonSerializer.Serialize(balanceInfoToUpdate.Metadata) } + }; + + return await _apiClient.PutAsync( + uri: $"{id}", + request: balanceInfoToUpdate, + headers: new Dictionary() + { + { "publicKey", _balanceConfiguration.PublicKey }, + { + "signature", _signingService.SignPayload(JsonSerializer.Serialize(signaturePayload), + _balanceConfiguration.PrivateKey) + }, + }); + } +} \ No newline at end of file diff --git a/src/Balance/SubsBase.SDK.Balance/Client/BalanceMovementClient.cs b/src/Balance/SubsBase.SDK.Balance/Client/BalanceMovementClient.cs new file mode 100644 index 0000000..776cf48 --- /dev/null +++ b/src/Balance/SubsBase.SDK.Balance/Client/BalanceMovementClient.cs @@ -0,0 +1,45 @@ +using System.Text.Json; +using SubsBase.Common.ApiClientHelper; +using SubsBase.SDK.Balance.Contracts; +using SubsBase.SDK.Balance.Services; + +namespace SubsBase.SDK.Balance.Client; + +public class BalanceMovementClient +{ + private readonly ApiClient _apiClient; + private readonly SigningService _signingService; + private readonly BalanceConfiguration _balanceConfiguration; + + public BalanceMovementClient(ApiClient apiClient, BalanceConfiguration balanceConfiguration) + { + _signingService = new SigningService(); + _apiClient = apiClient; + _balanceConfiguration = balanceConfiguration; + } + + public async Task> CreateAsync(BalanceMovementNew balanceMovementNew) + { + var signaturePayload = new SortedDictionary() + { + { "amount", balanceMovementNew.Amount }, + { "balanceId", balanceMovementNew.BalanceId.ToString() }, + { "description", balanceMovementNew.Description }, + { "expirationDate", balanceMovementNew.ExpirationDate ?? null}, + { "type", balanceMovementNew.Type.ToString() } + }; + + var result = await _apiClient.PostAsync( + uri: "", + headers: new Dictionary() + { + {"publicKey", _balanceConfiguration.PublicKey}, + {"signature", _signingService.SignPayload(JsonSerializer.Serialize(signaturePayload),_balanceConfiguration.PrivateKey)} + }, + request: balanceMovementNew); + + return result; + } + + +} \ No newline at end of file diff --git a/src/Balance/SubsBase.SDK.Balance/Client/OnHoldAmountClient.cs b/src/Balance/SubsBase.SDK.Balance/Client/OnHoldAmountClient.cs new file mode 100644 index 0000000..b030d47 --- /dev/null +++ b/src/Balance/SubsBase.SDK.Balance/Client/OnHoldAmountClient.cs @@ -0,0 +1,100 @@ +using System.Text.Json; +using SubsBase.Common.ApiClientHelper; +using SubsBase.SDK.Balance.Contracts; +using SubsBase.SDK.Balance.Services; + +namespace SubsBase.SDK.Balance.Client; + +public class OnHoldAmountClient +{ + private readonly ApiClient _apiClient; + private readonly SigningService _signingService; + private readonly BalanceConfiguration _balanceConfiguration; + + + public OnHoldAmountClient(ApiClient apiClient, BalanceConfiguration balanceConfiguration) + { + _signingService = new SigningService(); + _apiClient = apiClient; + _balanceConfiguration = balanceConfiguration; + } + + public async Task> GetAsync(string onHoldAmountId) + { + var signaturePayload = new Dictionary() + { + { "id", onHoldAmountId} + }; + + return await _apiClient.GetAsync( + uri: @$"{onHoldAmountId}", + headers: new Dictionary() + { + { "publicKey", _balanceConfiguration.PublicKey }, + { + "signature", _signingService.SignPayload(JsonSerializer.Serialize(signaturePayload), + _balanceConfiguration.PrivateKey) + }, + }); + } + + public async Task> CreateAsync(HoldAmountNew holdAmountNew) + { + var signaturePayload = new SortedDictionary() + { + { "balanceId", holdAmountNew.BalanceId }, + { "amount", holdAmountNew.Amount }, + { "description", holdAmountNew.Description }, + { "releaseDate", holdAmountNew.ReleaseDate } + }; + + var result = await _apiClient.PostAsync( + uri: "", + request: holdAmountNew, + headers: new Dictionary() + { + { "publicKey", _balanceConfiguration.PublicKey }, + { "signature", _signingService.SignPayload(JsonSerializer.Serialize(signaturePayload), _balanceConfiguration.PrivateKey) } + }); + return result; + } + + public async Task> DeleteAsync(string onHoldAmountId, bool isCaptured = false) + { + var signaturePayload = new SortedDictionary() + { + {"onHoldAmountId", onHoldAmountId}, + {"isCaptured", isCaptured.ToString()} + }; + + var result = await _apiClient.DeleteAsync( + uri: $"?onHoldAmountId={onHoldAmountId}&isCaptured={isCaptured}", + headers: new Dictionary() + { + {"publicKey" , _balanceConfiguration.PublicKey}, + {"signature", _signingService.SignPayload(JsonSerializer.Serialize(signaturePayload),_balanceConfiguration.PrivateKey)} + }); + return result; + } + + public async Task> GetBalanceOnHoldAmountsAsync(Guid balanceId) + { + var signaturePayload = new Dictionary() + { + { "balanceId", balanceId.ToString()} + }; + + return await _apiClient.GetAsync( + uri: @$"?balanceId={balanceId}", + headers: new Dictionary() + { + { "publicKey", _balanceConfiguration.PublicKey }, + { + "signature", _signingService.SignPayload(JsonSerializer.Serialize(signaturePayload), + _balanceConfiguration.PrivateKey) + }, + }); + } + + +} \ No newline at end of file diff --git a/src/Balance/SubsBase.SDK.Balance/Constants.cs b/src/Balance/SubsBase.SDK.Balance/Constants.cs new file mode 100644 index 0000000..7cee432 --- /dev/null +++ b/src/Balance/SubsBase.SDK.Balance/Constants.cs @@ -0,0 +1,35 @@ +namespace SubsBase.SDK.Balance; + +public class IConfigurationConstants +{ + public string BalanceUri; + public string BalanceMovementUri; + public string OnHoldAmountUri; +} + +public class DevelopmentConstants : IConfigurationConstants +{ + private const string DefaultBaseUrl = "http://api.dev.subsbase.xyz"; + + public DevelopmentConstants(string? baseUrl = null) + { + if (string.IsNullOrEmpty(baseUrl)) + { + baseUrl = DefaultBaseUrl; + } + + BalanceUri = $"{baseUrl}/balance/"; + BalanceMovementUri = $"{baseUrl}/balance/balance-movement/"; + OnHoldAmountUri = $"{baseUrl}/balance/on-hold-amount/"; + } +} + +public class ProductionConstants : IConfigurationConstants +{ + public ProductionConstants() + { + BalanceUri = "http://api.subsbase.io/balance/"; + BalanceMovementUri = "http://api.subsbase.io/balance/balance-movement/"; + OnHoldAmountUri = "http://api.subsbase.io/balance/on-hold-amount/"; + } +} \ No newline at end of file diff --git a/src/Balance/SubsBase.SDK.Balance/Contracts/BalanceDetails.cs b/src/Balance/SubsBase.SDK.Balance/Contracts/BalanceDetails.cs new file mode 100644 index 0000000..7affe0c --- /dev/null +++ b/src/Balance/SubsBase.SDK.Balance/Contracts/BalanceDetails.cs @@ -0,0 +1,31 @@ +using System.Text.Json.Serialization; + +namespace SubsBase.SDK.Balance.Contracts; + +public class BalanceDetails +{ + [JsonPropertyName("balanceId")] public Guid BalanceId { get; set; } + [JsonPropertyName("metadata")] public Dictionary? Metadata { get; set; } + [JsonPropertyName("unit")] public string Unit { get; set; } + [JsonPropertyName("allowTotalBalanceToBeNegative")] public bool AllowTotalBalanceToBeNegative { get; set; } = false; + [JsonPropertyName("balanceSummary")] public BalanceSummary BalanceSummary { get; set; } = new(); + [JsonPropertyName("movements")] public List? Movements { get; set; } + [JsonPropertyName("onHoldAmounts")] public List? OnHoldAmounts { get; set; } +} + +public class BalanceMovement +{ + [JsonPropertyName("utcTimestamp")] public DateTime UtcTimestamp { get; set; } + [JsonPropertyName("amount")] public decimal Amount { get; set; } + [JsonPropertyName("description")] public string Description { get; set; } = string.Empty; +} + +public class OnHoldAmount +{ + [JsonPropertyName("id")] public string Id { get; set; } = string.Empty; + [JsonPropertyName("balanceId")] public Guid BalanceId { get; set; } + [JsonPropertyName("amount")] public decimal Amount { get; set; } + [JsonPropertyName("onHoldDate")] public DateTime OnHoldDate { get; set; } + [JsonPropertyName("releaseDate")] public DateTime? ReleaseDate { get; set; } + [JsonPropertyName("description")] public string Description { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/src/Balance/SubsBase.SDK.Balance/Contracts/BalanceHold.cs b/src/Balance/SubsBase.SDK.Balance/Contracts/BalanceHold.cs new file mode 100644 index 0000000..9553250 --- /dev/null +++ b/src/Balance/SubsBase.SDK.Balance/Contracts/BalanceHold.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; + +namespace SubsBase.SDK.Balance.Contracts; + +public class HoldAmountNew +{ + [JsonPropertyName("balanceId")] public Guid BalanceId { get; set; } + [JsonPropertyName("amount")] public decimal Amount { get; set; } + [JsonPropertyName("description")] public string Description { get; set; } = string.Empty; + [JsonPropertyName("releaseDate")] public DateTime? ReleaseDate { get; set; } +} + +public class HoldAmountResponse +{ + [JsonPropertyName("onHoldAmountId")] public string OnHoldAmountId { get; set; } +} + +public class OnHoldAmountDetails +{ + [JsonPropertyName("balanceId")] public Guid BalanceId { get; set; } + [JsonPropertyName("onHoldAmounts")] public List OnHoldAmounts { get; set; } +} \ No newline at end of file diff --git a/src/Balance/SubsBase.SDK.Balance/Contracts/BalanceInfo.cs b/src/Balance/SubsBase.SDK.Balance/Contracts/BalanceInfo.cs new file mode 100644 index 0000000..044013d --- /dev/null +++ b/src/Balance/SubsBase.SDK.Balance/Contracts/BalanceInfo.cs @@ -0,0 +1,25 @@ +using System.Text.Json.Serialization; + +namespace SubsBase.SDK.Balance.Contracts; + +public class BalanceInfoNew +{ + [JsonPropertyName("allowTotalBalanceToBeNegative")] public bool AllowTotalBalanceToBeNegative { get; set; } = false; + [JsonPropertyName("metadata")] public Dictionary? Metadata { get; set; } + [JsonPropertyName("unit")] public string Unit { get; set; } = string.Empty; +} + + +public class BalanceInfoUpdate +{ + [JsonPropertyName("metadata")] public Dictionary? Metadata { get; set; } +} + +public class BalanceSummary +{ + [JsonPropertyName("balanceId")] public Guid BalanceId { get; set; } + [JsonPropertyName("unit")] public string Unit { get; set; } + [JsonPropertyName("totalAmount")] public decimal TotalAmount { get; set; } + [JsonPropertyName("onHoldAmount")] public decimal OnHoldAmount { get; set; } + [JsonPropertyName("availableAmount")] public decimal AvailableAmount { get; set; } +} \ No newline at end of file diff --git a/src/Balance/SubsBase.SDK.Balance/Contracts/BalanceMovement.cs b/src/Balance/SubsBase.SDK.Balance/Contracts/BalanceMovement.cs new file mode 100644 index 0000000..955ad6b --- /dev/null +++ b/src/Balance/SubsBase.SDK.Balance/Contracts/BalanceMovement.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; + +namespace SubsBase.SDK.Balance.Contracts; + +public enum MovementType +{ + Debit, + Credit +} + +public class BalanceMovementNew +{ + [JsonPropertyName("balanceId")] public Guid BalanceId { get; set; } + [JsonPropertyName("type")] [JsonConverter(typeof(JsonStringEnumConverter))] public MovementType Type { get; set; } + [JsonPropertyName("amount")] public decimal Amount { get; set; } + [JsonPropertyName("expirationDate") ] public DateTime? ExpirationDate { get; set; } + [JsonPropertyName("description")] public string Description { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/src/Balance/SubsBase.SDK.Balance/Environment.cs b/src/Balance/SubsBase.SDK.Balance/Environment.cs new file mode 100644 index 0000000..e5ed6a0 --- /dev/null +++ b/src/Balance/SubsBase.SDK.Balance/Environment.cs @@ -0,0 +1,7 @@ +namespace SubsBase.SDK.Balance; + +public enum Environment +{ + Development, + Production +} \ No newline at end of file diff --git a/src/Balance/SubsBase.SDK.Balance/Install.cs b/src/Balance/SubsBase.SDK.Balance/Install.cs new file mode 100644 index 0000000..1991a74 --- /dev/null +++ b/src/Balance/SubsBase.SDK.Balance/Install.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.DependencyInjection; +using SubsBase.SDK.Balance.Client; + +namespace SubsBase.SDK.Balance; + +public static class Install +{ + + public static IServiceCollection AddBalanceSdk(this IServiceCollection services, string publicKey, string privateKey, Environment environment, string? testingEnvironmentUrl = null) + { + return services + .AddSingleton( provider => new BalanceSdk(publicKey, privateKey, environment, testingEnvironmentUrl)) + .AddSingleton( provider => provider.GetRequiredService().Balance) + .AddSingleton( provider => provider.GetRequiredService().BalanceMovement) + .AddSingleton( provider => provider.GetRequiredService().OnHoldAmount); + } +} \ No newline at end of file diff --git a/src/Balance/SubsBase.SDK.Balance/Services/SigningService.cs b/src/Balance/SubsBase.SDK.Balance/Services/SigningService.cs new file mode 100644 index 0000000..1dc0a35 --- /dev/null +++ b/src/Balance/SubsBase.SDK.Balance/Services/SigningService.cs @@ -0,0 +1,15 @@ +using System.Security.Cryptography; +using System.Text; + +namespace SubsBase.SDK.Balance.Services; + +public class SigningService +{ + public string SignPayload(string payload, string secret) + { + using var hasher = new HMACSHA256(Encoding.ASCII.GetBytes(secret)); + var computedHashBytes = hasher.ComputeHash(Encoding.UTF8.GetBytes(payload)); + var computedHash = string.Join("", computedHashBytes.Select(b => b.ToString("x2"))); + return computedHash; + } +} \ No newline at end of file diff --git a/src/Balance/SubsBase.SDK.Balance/SubsBase.SDK.Balance.csproj b/src/Balance/SubsBase.SDK.Balance/SubsBase.SDK.Balance.csproj new file mode 100644 index 0000000..bca4a50 --- /dev/null +++ b/src/Balance/SubsBase.SDK.Balance/SubsBase.SDK.Balance.csproj @@ -0,0 +1,47 @@ + + + + net8.0 + enable + enable + SubsBase + true + true + true + 1.5.1 + 12 + Subsbase Balance SDK + Subsbase + https://github.com/subsbase/balance-service + Private + + + + + bin\Debug\ + true + + + + + + + + $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage + + + + + + <_ReferenceCopyLocalPaths Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference')->WithMetadataValue('PrivateAssets', 'All'))" /> + + + + + + + + + + + diff --git a/src/Balance/SubsBase.SDK.Balance/SubsBase.SDK.Balance.csproj.nuspec b/src/Balance/SubsBase.SDK.Balance/SubsBase.SDK.Balance.csproj.nuspec new file mode 100644 index 0000000..310192e --- /dev/null +++ b/src/Balance/SubsBase.SDK.Balance/SubsBase.SDK.Balance.csproj.nuspec @@ -0,0 +1,17 @@ + + + + SubsBase.SDK.Balance.csproj + 1.5.1 + Subsbase + false + MIT + + Package description + Summary of changes made in this release of the package. + $copyright$ + + + + + \ No newline at end of file diff --git a/src/Shared/SubsBase.Common.ApiClientHelper/ApiClient.cs b/src/Shared/SubsBase.Common.ApiClientHelper/ApiClient.cs new file mode 100644 index 0000000..99ddb06 --- /dev/null +++ b/src/Shared/SubsBase.Common.ApiClientHelper/ApiClient.cs @@ -0,0 +1,91 @@ +using System.Text; +using System.Text.Json; + +namespace SubsBase.Common.ApiClientHelper; + +public partial class ApiClient +{ + private readonly HttpClient _httpClient; + + public ApiClient(HttpClient httpClient, string baseAddress) + { + _httpClient = httpClient; + _httpClient.BaseAddress = new Uri(baseAddress); + } + + public async Task> ResponseAsync( + string uri, + HttpMethod httpMethod, + TRequest? request = null, + string? mediaType = null, + Encoding? encoding = null, + Dictionary? headers = null, + JsonSerializerOptions? jsonSerializerOptions = null) + where TResult : class + where TRequest : class + { + mediaType ??= "application/json"; + encoding ??= Encoding.UTF8; + + var content = request != null ? JsonSerializer.Serialize(request, jsonSerializerOptions) : ""; + + var httpRequest = new HttpRequestMessage(httpMethod, uri); + httpRequest.Content = new StringContent(content, encoding, mediaType); + + if (headers != null) + { + foreach (var header in headers) + { + if (_httpClient.DefaultRequestHeaders.Contains(header.Key)) + { + _httpClient.DefaultRequestHeaders.Remove(header.Key); + } + + _httpClient.DefaultRequestHeaders.Add(header.Key, header.Value); + } + } + + var httpResponseMessage = await _httpClient.SendAsync(httpRequest); + + var stringResponse = await httpResponseMessage.Content.ReadAsStringAsync(); + + if (!httpResponseMessage.IsSuccessStatusCode) return Result.Fail(stringResponse); + + var result = JsonSerializer.Deserialize(stringResponse, jsonSerializerOptions); + + return Result.Ok(result); + } + + public async Task> ResponseAsync( + string uri, + HttpMethod httpMethod, + Dictionary? headers = null, + JsonSerializerOptions? jsonSerializerOptions = null) + where TResult : class + { + var httpRequest = new HttpRequestMessage(httpMethod, uri); + + if (headers != null) + { + foreach (var header in headers) + { + if (_httpClient.DefaultRequestHeaders.Contains(header.Key)) + { + _httpClient.DefaultRequestHeaders.Remove(header.Key); + } + + _httpClient.DefaultRequestHeaders.Add(header.Key, header.Value); + } + } + + var httpResponseMessage = await _httpClient.SendAsync(httpRequest); + + var stringResponse = await httpResponseMessage.Content.ReadAsStringAsync(); + + if (!httpResponseMessage.IsSuccessStatusCode) return Result.Fail(stringResponse); + + var result = JsonSerializer.Deserialize(stringResponse, jsonSerializerOptions); + + return Result.Ok(result); + } +} \ No newline at end of file diff --git a/src/Shared/SubsBase.Common.ApiClientHelper/DeleteHttpMethod.cs b/src/Shared/SubsBase.Common.ApiClientHelper/DeleteHttpMethod.cs new file mode 100644 index 0000000..a73dc46 --- /dev/null +++ b/src/Shared/SubsBase.Common.ApiClientHelper/DeleteHttpMethod.cs @@ -0,0 +1,40 @@ +using System.Text; +using System.Text.Json; + +namespace SubsBase.Common.ApiClientHelper; + +public partial class ApiClient +{ + public async Task> DeleteAsync( + string uri, + TRequest? request = null, + string? mediaType = null, + Encoding? encoding = null, + Dictionary? headers = null, + JsonSerializerOptions? jsonSerializerOptions = null) + where TResult : class + where TRequest : class + { + return await ResponseAsync( + uri, + httpMethod: HttpMethod.Delete, + request, + mediaType, + encoding, + headers, + jsonSerializerOptions); + } + + public async Task> DeleteAsync( + string uri, + Dictionary? headers = null, + JsonSerializerOptions? jsonSerializerOptions = null) + where TResult : class + { + return await ResponseAsync( + uri, + httpMethod: HttpMethod.Delete, + headers, + jsonSerializerOptions); + } +} \ No newline at end of file diff --git a/src/Shared/SubsBase.Common.ApiClientHelper/GetHttpMethod.cs b/src/Shared/SubsBase.Common.ApiClientHelper/GetHttpMethod.cs new file mode 100644 index 0000000..ac0997e --- /dev/null +++ b/src/Shared/SubsBase.Common.ApiClientHelper/GetHttpMethod.cs @@ -0,0 +1,40 @@ +using System.Text; +using System.Text.Json; + +namespace SubsBase.Common.ApiClientHelper; + +public partial class ApiClient +{ + public async Task> GetAsync( + string uri, + TRequest? request = null, + string? mediaType = null, + Encoding? encoding = null, + Dictionary? headers = null, + JsonSerializerOptions? jsonSerializerOptions = null) + where TResult : class + where TRequest : class + { + return await ResponseAsync( + uri, + httpMethod: HttpMethod.Get, + request, + mediaType, + encoding, + headers, + jsonSerializerOptions); + } + + public async Task> GetAsync( + string uri, + Dictionary? headers = null, + JsonSerializerOptions? jsonSerializerOptions = null) + where TResult : class + { + return await ResponseAsync( + uri, + httpMethod: HttpMethod.Get, + headers, + jsonSerializerOptions); + } +} \ No newline at end of file diff --git a/src/Shared/SubsBase.Common.ApiClientHelper/PostHttpMethod.cs b/src/Shared/SubsBase.Common.ApiClientHelper/PostHttpMethod.cs new file mode 100644 index 0000000..4c3f843 --- /dev/null +++ b/src/Shared/SubsBase.Common.ApiClientHelper/PostHttpMethod.cs @@ -0,0 +1,40 @@ +using System.Text; +using System.Text.Json; + +namespace SubsBase.Common.ApiClientHelper; + +public partial class ApiClient +{ + public async Task> PostAsync( + string uri, + TRequest? request = null, + string? mediaType = null, + Encoding? encoding = null, + Dictionary? headers = null, + JsonSerializerOptions? jsonSerializerOptions = null) + where TResult : class + where TRequest : class + { + return await ResponseAsync( + uri, + httpMethod: HttpMethod.Post, + request, + mediaType, + encoding, + headers, + jsonSerializerOptions); + } + + public async Task> PostAsync( + string uri, + Dictionary? headers = null, + JsonSerializerOptions? jsonSerializerOptions = null) + where TResult : class + { + return await ResponseAsync( + uri, + httpMethod: HttpMethod.Post, + headers, + jsonSerializerOptions); + } +} \ No newline at end of file diff --git a/src/Shared/SubsBase.Common.ApiClientHelper/PutHttpMethod.cs b/src/Shared/SubsBase.Common.ApiClientHelper/PutHttpMethod.cs new file mode 100644 index 0000000..6fffcae --- /dev/null +++ b/src/Shared/SubsBase.Common.ApiClientHelper/PutHttpMethod.cs @@ -0,0 +1,40 @@ +using System.Text; +using System.Text.Json; + +namespace SubsBase.Common.ApiClientHelper; + +public partial class ApiClient +{ + public async Task> PutAsync( + string uri, + TRequest? request = null, + string? mediaType = null, + Encoding? encoding = null, + Dictionary? headers = null, + JsonSerializerOptions? jsonSerializerOptions = null) + where TResult : class + where TRequest : class + { + return await ResponseAsync( + uri, + httpMethod: HttpMethod.Put, + request, + mediaType, + encoding, + headers, + jsonSerializerOptions); + } + + public async Task> PutAsync( + string uri, + Dictionary? headers = null, + JsonSerializerOptions? jsonSerializerOptions = null) + where TResult : class + { + return await ResponseAsync( + uri, + httpMethod: HttpMethod.Put, + headers, + jsonSerializerOptions); + } +} \ No newline at end of file diff --git a/src/Shared/SubsBase.Common.ApiClientHelper/Result.cs b/src/Shared/SubsBase.Common.ApiClientHelper/Result.cs new file mode 100644 index 0000000..c97083a --- /dev/null +++ b/src/Shared/SubsBase.Common.ApiClientHelper/Result.cs @@ -0,0 +1,53 @@ +namespace SubsBase.Common.ApiClientHelper; + +public class Result +{ + public bool IsFailed { get; set; } + public bool IsSuccess { get; set; } + public string? Message { get; set; } + + public static Result Fail(string message) + { + return new Result + { + IsFailed = true, + Message = message + }; + } + + public static Result Ok() + { + return new Result + { + IsSuccess = true + }; + } + + public static Result Ok(T result) where T : class + { + return new Result + { + IsSuccess = true, + Value = result + }; + } + + public static Result Fail(string message) where T : class + { + return new Result + { + IsFailed = true, + Message = message + }; + } +} + + +public class Result +{ + public bool IsFailed { get; set; } + public bool IsSuccess { get; set; } + public string? Message { get; set; } + public T? Value { get; set; } + +} \ No newline at end of file diff --git a/src/Shared/SubsBase.Common.ApiClientHelper/SubsBase.Common.ApiClientHelper.csproj b/src/Shared/SubsBase.Common.ApiClientHelper/SubsBase.Common.ApiClientHelper.csproj new file mode 100644 index 0000000..8ac9e47 --- /dev/null +++ b/src/Shared/SubsBase.Common.ApiClientHelper/SubsBase.Common.ApiClientHelper.csproj @@ -0,0 +1,29 @@ + + + + net8.0 + enable + enable + 12 + + + + + + + + + ApiClient.cs + + + ApiClient.cs + + + ApiClient.cs + + + ApiClient.cs + + + + diff --git a/src/SubsBase.SDK.Authentication/SubsBase.SDK.Authentication.csproj b/src/SubsBase.SDK.Authentication/SubsBase.SDK.Authentication.csproj index 58df15d..80ba924 100644 --- a/src/SubsBase.SDK.Authentication/SubsBase.SDK.Authentication.csproj +++ b/src/SubsBase.SDK.Authentication/SubsBase.SDK.Authentication.csproj @@ -1,10 +1,10 @@ - netstandard2.0;net462;net5.0;net6.0 + net8.0 enable enable - 10 + 12 diff --git a/src/SubsBase.SDK.Common/SubsBase.SDK.Common.csproj b/src/SubsBase.SDK.Common/SubsBase.SDK.Common.csproj index 564ad21..92a2289 100644 --- a/src/SubsBase.SDK.Common/SubsBase.SDK.Common.csproj +++ b/src/SubsBase.SDK.Common/SubsBase.SDK.Common.csproj @@ -1,10 +1,10 @@ - netstandard2.0;net462;net5.0;net6.0 + net8.0 enable enable - 10 + 12 diff --git a/src/SubsBase.SDK.Subscription/SubsBase.SDK.Subscription.csproj b/src/SubsBase.SDK.Subscription/SubsBase.SDK.Subscription.csproj index f13bcce..d634bbe 100644 --- a/src/SubsBase.SDK.Subscription/SubsBase.SDK.Subscription.csproj +++ b/src/SubsBase.SDK.Subscription/SubsBase.SDK.Subscription.csproj @@ -1,10 +1,10 @@ - netstandard2.0;net462;net5.0;net6.0 + net8.0 enable enable - 10 + 12 diff --git a/src/SubsBase.SDK.sln b/src/SubsBase.SDK.sln index b08fb98..31b05ed 100644 --- a/src/SubsBase.SDK.sln +++ b/src/SubsBase.SDK.sln @@ -8,6 +8,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubsBase.SDK", "SubsBase.SD EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubsBase.SDK.Authentication", "SubsBase.SDK.Authentication\SubsBase.SDK.Authentication.csproj", "{40599CF0-FFB2-4F95-B67F-CBA1955CDBBC}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{9D4D7C57-3397-45AE-AB3B-566FA31DC7BF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubsBase.Common.ApiClientHelper", "Shared\SubsBase.Common.ApiClientHelper\SubsBase.Common.ApiClientHelper.csproj", "{C2C2B932-89CE-4D7F-9F34-AD2DCDD7ABFB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Balance", "Balance", "{F8A7443A-C083-4C7C-A1AE-EF4572F36FFA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubsBase.SDK.Balance.Test", "Balance\SubsBase.SDK.Balance.Test\SubsBase.SDK.Balance.Test.csproj", "{4EB52FBE-B014-4475-876C-CEF550FDF6A2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubsBase.SDK.Balance", "Balance\SubsBase.SDK.Balance\SubsBase.SDK.Balance.csproj", "{F808184E-AA25-4FF4-B703-FB2F19172468}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -30,5 +40,22 @@ Global {40599CF0-FFB2-4F95-B67F-CBA1955CDBBC}.Debug|Any CPU.Build.0 = Debug|Any CPU {40599CF0-FFB2-4F95-B67F-CBA1955CDBBC}.Release|Any CPU.ActiveCfg = Release|Any CPU {40599CF0-FFB2-4F95-B67F-CBA1955CDBBC}.Release|Any CPU.Build.0 = Release|Any CPU + {C2C2B932-89CE-4D7F-9F34-AD2DCDD7ABFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2C2B932-89CE-4D7F-9F34-AD2DCDD7ABFB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2C2B932-89CE-4D7F-9F34-AD2DCDD7ABFB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2C2B932-89CE-4D7F-9F34-AD2DCDD7ABFB}.Release|Any CPU.Build.0 = Release|Any CPU + {4EB52FBE-B014-4475-876C-CEF550FDF6A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4EB52FBE-B014-4475-876C-CEF550FDF6A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4EB52FBE-B014-4475-876C-CEF550FDF6A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4EB52FBE-B014-4475-876C-CEF550FDF6A2}.Release|Any CPU.Build.0 = Release|Any CPU + {F808184E-AA25-4FF4-B703-FB2F19172468}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F808184E-AA25-4FF4-B703-FB2F19172468}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F808184E-AA25-4FF4-B703-FB2F19172468}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F808184E-AA25-4FF4-B703-FB2F19172468}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {C2C2B932-89CE-4D7F-9F34-AD2DCDD7ABFB} = {9D4D7C57-3397-45AE-AB3B-566FA31DC7BF} + {4EB52FBE-B014-4475-876C-CEF550FDF6A2} = {F8A7443A-C083-4C7C-A1AE-EF4572F36FFA} + {F808184E-AA25-4FF4-B703-FB2F19172468} = {F8A7443A-C083-4C7C-A1AE-EF4572F36FFA} EndGlobalSection EndGlobal diff --git a/src/SubsBase.SDK/SubsBase.SDK.csproj b/src/SubsBase.SDK/SubsBase.SDK.csproj index 1989c2d..1f2953e 100644 --- a/src/SubsBase.SDK/SubsBase.SDK.csproj +++ b/src/SubsBase.SDK/SubsBase.SDK.csproj @@ -1,17 +1,18 @@ - netstandard2.0;net462;net5.0;net6.0 + net8.0 enable enable - 10 + 12 1.0.2 - - - + + + +