diff --git a/AppifySheets.Immutable.BankIntegrationTypes/BankAccountWithCurrencyV.cs b/AppifySheets.Immutable.BankIntegrationTypes/BackwardCompatibility.cs similarity index 50% rename from AppifySheets.Immutable.BankIntegrationTypes/BankAccountWithCurrencyV.cs rename to AppifySheets.Immutable.BankIntegrationTypes/BackwardCompatibility.cs index 7e3f878..85011c3 100644 --- a/AppifySheets.Immutable.BankIntegrationTypes/BankAccountWithCurrencyV.cs +++ b/AppifySheets.Immutable.BankIntegrationTypes/BackwardCompatibility.cs @@ -1,17 +1,30 @@ -using System; +using System; +using CSharpFunctionalExtensions; namespace AppifySheets.Immutable.BankIntegrationTypes; -public class BankAccountWithCurrencyV(BankAccountV bankAccountNumber, CurrencyV currencyV) -{ - // public static Result Create(BankAccountV bankAccountNumber, CurrencyV currencyV) => Result.Try(() => new BankAccountWithCurrencyV(bankAccountNumber, currencyV)); +/// +/// Type aliases for backward compatibility +/// These are kept for backward compatibility only +/// New code should use Iban, Currency, and BankAccount instead +/// - public BankAccountV BankAccountNumber { get; } = bankAccountNumber ?? throw new ArgumentNullException(nameof(bankAccountNumber)); - public CurrencyV CurrencyV { get; } = currencyV ?? throw new ArgumentNullException(nameof(currencyV)); +// Keep original classes but mark as obsolete +[Obsolete("Use Iban instead")] +public record BankAccountV +{ + public BankAccountV(string accountNumber) + { + const string pattern2Match = @"(^[a-zA-Z]{2}\d{2}[a-zA-Z]{2}\d{16})(\w{3})?$"; + var iban = System.Text.RegularExpressions.Regex.Match(accountNumber, pattern2Match).Groups[1].Value; + AccountNumber = accountNumber; + } - public override string ToString() => $"{BankAccountNumber}{CurrencyV}"; + public string AccountNumber { get; } + public override string ToString() => AccountNumber; } +[Obsolete("Use Currency instead")] public record CurrencyV { public static readonly CurrencyV GEL = new CurrencyV("GEL"); @@ -27,4 +40,16 @@ public CurrencyV(string code) } public override string ToString() => Code; +} + +[Obsolete("Use BankAccount instead")] +public class BankAccountWithCurrencyV(BankAccountV bankAccountNumber, CurrencyV currencyV) +{ + public BankAccountV BankAccountNumber { get; } = bankAccountNumber ?? throw new ArgumentNullException(nameof(bankAccountNumber)); + public CurrencyV CurrencyV { get; } = currencyV ?? throw new ArgumentNullException(nameof(currencyV)); + + public static Result Create(BankAccountV bankAccountNumber, CurrencyV currencyV) + => Result.Try(() => new BankAccountWithCurrencyV(bankAccountNumber, currencyV)); + + public override string ToString() => $"{BankAccountNumber}{CurrencyV}"; } \ No newline at end of file diff --git a/AppifySheets.Immutable.BankIntegrationTypes/BankAccount.cs b/AppifySheets.Immutable.BankIntegrationTypes/BankAccount.cs new file mode 100644 index 0000000..7a4c199 --- /dev/null +++ b/AppifySheets.Immutable.BankIntegrationTypes/BankAccount.cs @@ -0,0 +1,54 @@ +using CSharpFunctionalExtensions; + +namespace AppifySheets.Immutable.BankIntegrationTypes; + +/// +/// Represents a bank account with IBAN and currency +/// +public record BankAccount +{ + BankAccount(Iban iban, Currency currency) + { + Iban = iban; + Currency = currency; + } + + public Iban Iban { get; } + public Currency Currency { get; } + + /// + /// Creates a bank account with validation + /// + public static Result Create(string iban, string currencyCode) + { + var ibanResult = Iban.Create(iban); + if (ibanResult.IsFailure) + return Result.Failure($"Invalid IBAN: {ibanResult.Error}"); + + var currencyResult = Currency.Create(currencyCode); + if (currencyResult.IsFailure) + return Result.Failure($"Invalid currency: {currencyResult.Error}"); + + return Result.Success(new BankAccount(ibanResult.Value, currencyResult.Value)); + } + + /// + /// Creates a bank account with pre-validated components + /// + public static Result Create(Iban iban, Currency currency) + { + if (iban == null) + return Result.Failure("IBAN cannot be null"); + if (currency == null) + return Result.Failure("Currency cannot be null"); + + return Result.Success(new BankAccount(iban, currency)); + } + + + public override string ToString() => $"{Iban}{Currency}"; + + // For backward compatibility - maps to old property names + public Iban BankAccountNumber => Iban; + public Currency CurrencyV => Currency; +} \ No newline at end of file diff --git a/AppifySheets.Immutable.BankIntegrationTypes/BankAccountV.cs b/AppifySheets.Immutable.BankIntegrationTypes/BankAccountV.cs deleted file mode 100644 index 9caa80c..0000000 --- a/AppifySheets.Immutable.BankIntegrationTypes/BankAccountV.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Text.RegularExpressions; - -namespace AppifySheets.Immutable.BankIntegrationTypes; - -// ReSharper disable once InconsistentNaming -public record BankAccountV -{ - public BankAccountV(string accountNumber) - { - const string pattern2Match = @"(^[a-zA-Z]{2}\d{2}[a-zA-Z]{2}\d{16})(\w{3})?$"; - - var iban = Regex.Match(accountNumber, pattern2Match).Groups[1].Value; - - // if(string.IsNullOrEmpty(iban)) throw new InvalidOperationException($"Account#: [{accountNumber}] doesn't seem to be in an IBAN format!"); - - AccountNumber = accountNumber; - } - - public string AccountNumber { get; } - - public override string ToString() => AccountNumber; -} \ No newline at end of file diff --git a/AppifySheets.Immutable.BankIntegrationTypes/Currency.cs b/AppifySheets.Immutable.BankIntegrationTypes/Currency.cs new file mode 100644 index 0000000..365789d --- /dev/null +++ b/AppifySheets.Immutable.BankIntegrationTypes/Currency.cs @@ -0,0 +1,69 @@ +using System; +using System.Linq; +using CSharpFunctionalExtensions; + +namespace AppifySheets.Immutable.BankIntegrationTypes; + +/// +/// Represents a currency with ISO 4217 3-letter code +/// +public record Currency +{ + // Common currencies as static instances + // ReSharper disable once InconsistentNaming + public static readonly Currency GEL = new("GEL"); + // ReSharper disable once InconsistentNaming + public static readonly Currency USD = new("USD"); + // ReSharper disable once InconsistentNaming + public static readonly Currency EUR = new("EUR"); + // ReSharper disable once InconsistentNaming + public static readonly Currency GBP = new("GBP"); + // ReSharper disable once InconsistentNaming + public static readonly Currency CHF = new("CHF"); + // ReSharper disable once InconsistentNaming + public static readonly Currency JPY = new("JPY"); + // ReSharper disable once InconsistentNaming + public static readonly Currency CNY = new("CNY"); + + Currency(string code) + { + Code = code; + } + + public string Code { get; } + + /// + /// Creates a currency instance with validation + /// + public static Result Create(string code) + { + if (string.IsNullOrWhiteSpace(code)) + return Result.Failure("Currency code cannot be empty"); + + var normalized = code.Trim().ToUpperInvariant(); + + if (normalized.Length != 3) + return Result.Failure("Currency code must be exactly 3 characters (ISO 4217)"); + + if (!normalized.All(char.IsLetter)) + return Result.Failure("Currency code must contain only letters"); + + return Result.Success(new Currency(normalized)); + } + + /// + /// Parses a currency code, throwing exception if invalid + /// + public static Currency Parse(string code) + { + var result = Create(code); + if (result.IsFailure) + throw new ArgumentException(result.Error, nameof(code)); + return result.Value; + } + + public override string ToString() => Code; + + // Implicit conversion to string for convenience + public static implicit operator string(Currency currency) => currency.Code; +} \ No newline at end of file diff --git a/AppifySheets.Immutable.BankIntegrationTypes/Iban.cs b/AppifySheets.Immutable.BankIntegrationTypes/Iban.cs new file mode 100644 index 0000000..9b18eb0 --- /dev/null +++ b/AppifySheets.Immutable.BankIntegrationTypes/Iban.cs @@ -0,0 +1,48 @@ +using System; +using System.Text.RegularExpressions; +using CSharpFunctionalExtensions; + +namespace AppifySheets.Immutable.BankIntegrationTypes; + +/// +/// Represents an International Bank Account Number (IBAN) +/// +public record Iban +{ + const string IbanPattern = @"^[A-Z]{2}\d{2}[A-Z0-9]+$"; + const int MinIbanLength = 15; + const int MaxIbanLength = 34; + + Iban(string value) + { + Value = value; + } + + public string Value { get; } + + /// + /// Creates an IBAN instance with validation + /// + public static Result Create(string value) + { + if (string.IsNullOrWhiteSpace(value)) + return Result.Failure("IBAN cannot be empty"); + + // Normalize: remove spaces and convert to uppercase + var normalized = value.Replace(" ", "").ToUpperInvariant(); + + if (normalized.Length is < MinIbanLength or > MaxIbanLength) + return Result.Failure($"IBAN length must be between {MinIbanLength} and {MaxIbanLength} characters"); + + if (!Regex.IsMatch(normalized, IbanPattern)) + return Result.Failure("Invalid IBAN format"); + + return Result.Success(new Iban(normalized)); + } + + + public override string ToString() => Value; + + // Implicit conversion to string for convenience + public static implicit operator string(Iban iban) => iban.Value; +} \ No newline at end of file diff --git a/AppifySheets.TBC.IntegrationService.Client.DemoConsole/AppifySheets.TBC.IntegrationService.Client.DemoConsole.csproj b/AppifySheets.TBC.IntegrationService.Client.DemoConsole/AppifySheets.TBC.IntegrationService.Client.DemoConsole.csproj index aa3d8f5..97fa3bf 100644 --- a/AppifySheets.TBC.IntegrationService.Client.DemoConsole/AppifySheets.TBC.IntegrationService.Client.DemoConsole.csproj +++ b/AppifySheets.TBC.IntegrationService.Client.DemoConsole/AppifySheets.TBC.IntegrationService.Client.DemoConsole.csproj @@ -9,6 +9,7 @@ + diff --git a/AppifySheets.TBC.IntegrationService.Client.DemoConsole/Program.cs b/AppifySheets.TBC.IntegrationService.Client.DemoConsole/Program.cs index afbb356..285bede 100644 --- a/AppifySheets.TBC.IntegrationService.Client.DemoConsole/Program.cs +++ b/AppifySheets.TBC.IntegrationService.Client.DemoConsole/Program.cs @@ -24,10 +24,10 @@ var checkStatus2 = await tbcSoapCaller.GetDeserialized(new GetPaymentOrderStatusRequestIo(1632027071)); -var ownAccountGEL = new BankAccountWithCurrencyV(new BankAccountV("GE31TB7467936080100003"), CurrencyV.GEL); -var ownAccountUSD = new BankAccountWithCurrencyV(new BankAccountV("GE47TB7467936170100001"), CurrencyV.USD); +var ownAccountGEL = BankAccount.Create("GE31TB7467936080100003", "GEL").Value; +var ownAccountUSD = BankAccount.Create("GE47TB7467936170100001", "USD").Value; -var transferTypeRecordSpecific = new TransferTypeRecordSpecific +var bankTransferCommonDetails = new BankTransferCommonDetails { DocumentNumber = 123, Amount = 0.01m, @@ -40,42 +40,42 @@ var withinBankGel2 = await tbcSoapCaller.GetDeserialized(new ImportSinglePaymentOrdersRequestIo( new TransferWithinBankPaymentOrderIo { - RecipientAccountWithCurrency = new BankAccountWithCurrencyV(new BankAccountV("GE86TB1144836120100002"), CurrencyV.GEL), - TransferTypeRecordSpecific = transferTypeRecordSpecific + RecipientAccountWithCurrency = BankAccount.Create("GE86TB1144836120100002", "GEL").Value, + BankTransferCommonDetails = bankTransferCommonDetails })); var withinBankCurrency = await tbcSoapCaller.GetDeserialized(new ImportSinglePaymentOrdersRequestIo( new TransferWithinBankPaymentOrderIo { - TransferTypeRecordSpecific = transferTypeRecordSpecific with + BankTransferCommonDetails = bankTransferCommonDetails with { SenderAccountWithCurrency = ownAccountUSD }, - RecipientAccountWithCurrency = new BankAccountWithCurrencyV(new BankAccountV("GE86TB1144836120100002"), CurrencyV.USD), + RecipientAccountWithCurrency = BankAccount.Create("GE86TB1144836120100002", "USD").Value, })); var toAnotherBankGel = await tbcSoapCaller.GetDeserialized( new ImportSinglePaymentOrdersRequestIo( new TransferToOtherBankNationalCurrencyPaymentOrderIo( - new BankAccountWithCurrencyV(new BankAccountV("GE33BG0000000263255500"), CurrencyV.GEL), "123123123") + BankAccount.Create("GE33BG0000000263255500", "GEL").Value, "123123123") { - TransferTypeRecordSpecific = transferTypeRecordSpecific + BankTransferCommonDetails = bankTransferCommonDetails })); var toAnotherBankCurrencyGood = await tbcSoapCaller.GetDeserialized( new ImportSinglePaymentOrdersRequestIo( new TransferToOtherBankForeignCurrencyPaymentOrderIo("test", "test", "SHA", "TEST", - new BankAccountWithCurrencyV(new BankAccountV("GE33BG0000000263255500"), CurrencyV.USD)) + BankAccount.Create("GE33BG0000000263255500", "USD").Value) { - TransferTypeRecordSpecific = transferTypeRecordSpecific with { SenderAccountWithCurrency = ownAccountUSD } + BankTransferCommonDetails = bankTransferCommonDetails with { SenderAccountWithCurrency = ownAccountUSD } })); var toAnotherBankCurrencyBad = await tbcSoapCaller.GetDeserialized( new ImportSinglePaymentOrdersRequestIo( new TransferToOtherBankForeignCurrencyPaymentOrderIo("test", "test", "SHA", "TEST", - new BankAccountWithCurrencyV(new BankAccountV("GE33BG0000000263255500"), CurrencyV.USD)) + BankAccount.Create("GE33BG0000000263255500", "USD").Value) { - TransferTypeRecordSpecific = transferTypeRecordSpecific with { SenderAccountWithCurrency = ownAccountUSD } + BankTransferCommonDetails = bankTransferCommonDetails with { SenderAccountWithCurrency = ownAccountUSD } })); var toChina = await tbcSoapCaller.GetDeserialized( @@ -83,9 +83,9 @@ new TransferToOtherBankForeignCurrencyPaymentOrderIo( "China", // "ICBKCNBJSZN", "INDUSTRIAL AND COMMERCIAL BANK OF CHINA SHENZHEN BRANCH", "SHA", "Invoice(LZSK202311028)", "ICBKCNBJSZN", "INDUSTRIAL AND COMMERCIAL BANK OF CHINA SHENZHEN BRANCH", "SHA", - new BankAccountWithCurrencyV(new BankAccountV("4000109819100186641"), CurrencyV.USD)) + BankAccount.Create("4000109819100186641", "USD").Value) { - TransferTypeRecordSpecific = transferTypeRecordSpecific with + BankTransferCommonDetails = bankTransferCommonDetails with { SenderAccountWithCurrency = ownAccountUSD, BeneficiaryName = "Shenzhen Shinekoo Supply Chain Co.,Ltd" @@ -95,6 +95,6 @@ var toTreasury = await tbcSoapCaller.GetDeserialized( new ImportSinglePaymentOrdersRequestIo( new TreasuryTransferPaymentOrderIo(101001000) - { TransferTypeRecordSpecific = transferTypeRecordSpecific })); + { BankTransferCommonDetails = bankTransferCommonDetails })); Debugger.Break(); \ No newline at end of file diff --git a/AppifySheets.TBC.IntegrationService.Client/ApiConfiguration/TBCApiCredentials.cs b/AppifySheets.TBC.IntegrationService.Client/ApiConfiguration/TBCApiCredentials.cs index 02b31d8..cf6d24e 100644 --- a/AppifySheets.TBC.IntegrationService.Client/ApiConfiguration/TBCApiCredentials.cs +++ b/AppifySheets.TBC.IntegrationService.Client/ApiConfiguration/TBCApiCredentials.cs @@ -1,24 +1,90 @@ using System; +using CSharpFunctionalExtensions; namespace AppifySheets.TBC.IntegrationService.Client.ApiConfiguration; -public record TBCApiCredentials(string Username, string Password); +/// +/// TBC Bank API credentials +/// +public record TBCApiCredentials(string Username, string Password) +{ + /// + /// Creates TBC API credentials with validation + /// + public static Result Create(string username, string password) + { + if (string.IsNullOrWhiteSpace(username)) + return Result.Failure("Username cannot be empty"); + + if (string.IsNullOrWhiteSpace(password)) + return Result.Failure("Password cannot be empty"); + + return Result.Success(new TBCApiCredentials(username.Trim(), password)); + } +} -// ReSharper disable once InconsistentNaming -// ReSharper disable once InconsistentNaming +/// +/// TBC Bank API credentials with certificate for authentication +/// public record TBCApiCredentialsWithCertificate { - public TBCApiCredentialsWithCertificate(TBCApiCredentials TBCApiCredentials, string CertificateFileName, string CertificatePassword) + // Public constructor for backward compatibility + public TBCApiCredentialsWithCertificate(TBCApiCredentials credentials, string certificateFileName, string certificatePassword) { - if (!CertificateFileName.EndsWith(".pfx")) + if (!certificateFileName.EndsWith(".pfx", StringComparison.OrdinalIgnoreCase)) throw new InvalidOperationException("Certificate must have a '.pfx' extension"); - - this.TBCApiCredentials = TBCApiCredentials; - this.CertificateFileName = CertificateFileName; - this.CertificatePassword = CertificatePassword; + + Credentials = credentials ?? throw new ArgumentNullException(nameof(credentials)); + CertificateFileName = certificateFileName; + CertificatePassword = certificatePassword; } - - public TBCApiCredentials TBCApiCredentials { get; } + + public TBCApiCredentials Credentials { get; } public string CertificateFileName { get; } public string CertificatePassword { get; } + + // Backward compatibility + public TBCApiCredentials TBCApiCredentials => Credentials; + + /// + /// Creates TBC API credentials with certificate validation + /// + public static Result Create( + TBCApiCredentials credentials, + string certificateFileName, + string certificatePassword) + { + if (credentials == null) + return Result.Failure("Credentials cannot be null"); + + if (string.IsNullOrWhiteSpace(certificateFileName)) + return Result.Failure("Certificate file name cannot be empty"); + + if (!certificateFileName.EndsWith(".pfx", StringComparison.OrdinalIgnoreCase)) + return Result.Failure("Certificate must have a '.pfx' extension"); + + if (string.IsNullOrWhiteSpace(certificatePassword)) + return Result.Failure("Certificate password cannot be empty"); + + return Result.Success(new TBCApiCredentialsWithCertificate( + credentials, + certificateFileName.Trim(), + certificatePassword)); + } + + /// + /// Creates credentials with all parameters + /// + public static Result Create( + string username, + string password, + string certificateFileName, + string certificatePassword) + { + var credentialsResult = TBCApiCredentials.Create(username, password); + if (credentialsResult.IsFailure) + return Result.Failure(credentialsResult.Error); + + return Create(credentialsResult.Value, certificateFileName, certificatePassword); + } } diff --git a/AppifySheets.TBC.IntegrationService.Client/AppifySheets.TBC.IntegrationService.Client.csproj b/AppifySheets.TBC.IntegrationService.Client/AppifySheets.TBC.IntegrationService.Client.csproj index 244bf62..481775d 100644 --- a/AppifySheets.TBC.IntegrationService.Client/AppifySheets.TBC.IntegrationService.Client.csproj +++ b/AppifySheets.TBC.IntegrationService.Client/AppifySheets.TBC.IntegrationService.Client.csproj @@ -20,7 +20,8 @@ - + + diff --git a/AppifySheets.TBC.IntegrationService.Client/SoapInfrastructure/ImportSinglePaymentOrders/TransferTypeInterfaces.cs b/AppifySheets.TBC.IntegrationService.Client/SoapInfrastructure/ImportSinglePaymentOrders/TransferTypeInterfaces.cs index 938f5a3..1c9f610 100644 --- a/AppifySheets.TBC.IntegrationService.Client/SoapInfrastructure/ImportSinglePaymentOrders/TransferTypeInterfaces.cs +++ b/AppifySheets.TBC.IntegrationService.Client/SoapInfrastructure/ImportSinglePaymentOrders/TransferTypeInterfaces.cs @@ -2,9 +2,9 @@ namespace AppifySheets.TBC.IntegrationService.Client.SoapInfrastructure.ImportSinglePaymentOrders; -public sealed record TransferTypeRecordSpecific +public sealed record BankTransferCommonDetails { - public required BankAccountWithCurrencyV SenderAccountWithCurrency { get; init; } + public required BankAccount SenderAccountWithCurrency { get; init; } public required long DocumentNumber { get; init; } public required decimal Amount { get; init; } public required string BeneficiaryName { get; init; } @@ -15,15 +15,15 @@ public sealed record TransferTypeRecordSpecific public abstract record TransferTypeRecord { - public required TransferTypeRecordSpecific TransferTypeRecordSpecific { get; init; } + public required BankTransferCommonDetails BankTransferCommonDetails { get; init; } - public BankAccountWithCurrencyV SenderAccountWithCurrency => TransferTypeRecordSpecific.SenderAccountWithCurrency; - public long DocumentNumber => TransferTypeRecordSpecific.DocumentNumber; - public decimal Amount => TransferTypeRecordSpecific.Amount; - public string BeneficiaryName => TransferTypeRecordSpecific.BeneficiaryName; - public string? PersonalNumber => TransferTypeRecordSpecific.PersonalNumber; - public string Description => TransferTypeRecordSpecific.Description; - public string? AdditionalDescription => TransferTypeRecordSpecific.AdditionalDescription; + public BankAccount SenderAccountWithCurrency => BankTransferCommonDetails.SenderAccountWithCurrency; + public long DocumentNumber => BankTransferCommonDetails.DocumentNumber; + public decimal Amount => BankTransferCommonDetails.Amount; + public string BeneficiaryName => BankTransferCommonDetails.BeneficiaryName; + public string? PersonalNumber => BankTransferCommonDetails.PersonalNumber; + public string Description => BankTransferCommonDetails.Description; + public string? AdditionalDescription => BankTransferCommonDetails.AdditionalDescription; } public interface IBeneficiaryName @@ -44,7 +44,7 @@ public interface ITreasury public interface IRecipient { - public BankAccountWithCurrencyV RecipientAccountWithCurrency { get; } + public BankAccount RecipientAccountWithCurrency { get; } } public interface IBeneficiaryTaxCode diff --git a/AppifySheets.TBC.IntegrationService.Client/SoapInfrastructure/ImportSinglePaymentOrders/TransferTypeRecords.cs b/AppifySheets.TBC.IntegrationService.Client/SoapInfrastructure/ImportSinglePaymentOrders/TransferTypeRecords.cs index d0dd944..379a65b 100644 --- a/AppifySheets.TBC.IntegrationService.Client/SoapInfrastructure/ImportSinglePaymentOrders/TransferTypeRecords.cs +++ b/AppifySheets.TBC.IntegrationService.Client/SoapInfrastructure/ImportSinglePaymentOrders/TransferTypeRecords.cs @@ -5,7 +5,7 @@ namespace AppifySheets.TBC.IntegrationService.Client.SoapInfrastructure.ImportSi public record TransferWithinBankPaymentOrderIo : TransferTypeRecord, IRecipient, IBeneficiaryName, IDescription { - public required BankAccountWithCurrencyV RecipientAccountWithCurrency { get; init; } + public required BankAccount RecipientAccountWithCurrency { get; init; } } public record TransferToOtherBankForeignCurrencyPaymentOrderIo( @@ -13,13 +13,13 @@ public record TransferToOtherBankForeignCurrencyPaymentOrderIo( string BeneficiaryBankCode, string BeneficiaryBankName, string ChargeDetails, - BankAccountWithCurrencyV RecipientAccountWithCurrency) + BankAccount RecipientAccountWithCurrency) : TransferTypeRecord, IBeneficiaryForCurrencyTransfer, IRecipient, IBeneficiaryName, IDescription; -public record TransferToOtherBankNationalCurrencyPaymentOrderIo(BankAccountWithCurrencyV RecipientAccountWithCurrency, string BeneficiaryTaxCode) +public record TransferToOtherBankNationalCurrencyPaymentOrderIo(BankAccount RecipientAccountWithCurrency, string BeneficiaryTaxCode) : TransferTypeRecord, IBeneficiaryTaxCode, IRecipient, IBeneficiaryName, IDescription; public record TreasuryTransferPaymentOrderIo(long TreasuryCode) : TransferTypeRecord, ITreasury; [UsedImplicitly] -public record TransferToOwnAccountPaymentOrderIo(BankAccountWithCurrencyV RecipientAccountWithCurrency) : TransferTypeRecord, IRecipient, IBeneficiaryName, IDescription; \ No newline at end of file +public record TransferToOwnAccountPaymentOrderIo(BankAccount RecipientAccountWithCurrency) : TransferTypeRecord, IRecipient, IBeneficiaryName, IDescription; \ No newline at end of file diff --git a/AppifySheets.TBC.IntegrationService.Client/SoapInfrastructure/PasswordChangeRelated/ChangePasswordResponseIo.cs b/AppifySheets.TBC.IntegrationService.Client/SoapInfrastructure/PasswordChangeRelated/ChangePasswordResponseIo.cs index 757c4f1..7472b98 100644 --- a/AppifySheets.TBC.IntegrationService.Client/SoapInfrastructure/PasswordChangeRelated/ChangePasswordResponseIo.cs +++ b/AppifySheets.TBC.IntegrationService.Client/SoapInfrastructure/PasswordChangeRelated/ChangePasswordResponseIo.cs @@ -12,8 +12,8 @@ namespace AppifySheets.TBC.IntegrationService.Client.SoapInfrastructure.Password [XmlRoot(ElementName="ChangePasswordResponseIo")] public class ChangePasswordResponseIo : ISoapResponse { - [XmlElement(ElementName="message")] - public string? Message { get; init; } + [XmlElement(ElementName="message")] + public string? Message { get; init; } [XmlAttribute(AttributeName="i")] public string? I { get; init; } diff --git a/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs b/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs index 4dbc81b..74ef1b0 100644 --- a/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs +++ b/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs @@ -14,12 +14,38 @@ namespace AppifySheets.TBC.IntegrationService.Client.TBC_Services; -// ReSharper disable once InconsistentNaming +/// +/// TBC Bank SOAP API client for executing banking operations +/// public sealed class TBCSoapCaller(TBCApiCredentialsWithCertificate tbcApiCredentialsWithCertificate) { - public async Task> GetDeserialized(RequestSoap RequestSoap) where TDeserializeInto : ISoapResponse + readonly TBCApiCredentialsWithCertificate _credentials = tbcApiCredentialsWithCertificate ?? throw new ArgumentNullException(nameof(tbcApiCredentialsWithCertificate)); + + /// + /// Creates a TBC SOAP caller instance with validation + /// + public static TBCSoapCaller Create(TBCApiCredentialsWithCertificate credentials) => new(credentials); + + /// + /// Creates a TBC SOAP caller with all parameters + /// + public static Result Create( + string username, + string password, + string certificateFileName, + string certificatePassword) + { + var credentialsResult = TBCApiCredentialsWithCertificate.Create( + username, password, certificateFileName, certificatePassword); + + if (credentialsResult.IsFailure) + return Result.Failure(credentialsResult.Error); + + return Create(credentialsResult.Value); + } + public async Task> GetDeserialized(RequestSoap requestSoap) where TDeserializeInto : ISoapResponse { - var response = await CallTBCServiceAsync(RequestSoap); + var response = await CallTBCServiceAsync(requestSoap); // ReSharper disable once ConvertIfStatementToReturnStatement if (response.IsFailure) return response.ConvertFailure(); @@ -69,7 +95,7 @@ static PerformedActionSoapEnvelope GetPerformedActionFor(TBCApiCredentials crede public Task> CallTBCServiceAsync(RequestSoap requestSoap) where TDeserializeInto : ISoapResponse { - var template = GetPerformedActionFor(tbcApiCredentialsWithCertificate.TBCApiCredentials, requestSoap.TBCServiceAction, requestSoap.SoapXml(), requestSoap.Nonce); + var template = GetPerformedActionFor(_credentials.Credentials, requestSoap.TBCServiceAction, requestSoap.SoapXml(), requestSoap.Nonce); return CallTBCServiceAsync(template); } @@ -117,7 +143,7 @@ async Task> CallTBCServiceAsync(PerformedActionSoapEnvelope perfo X509Certificate2Collection GetCertificates() { var collection = new X509Certificate2Collection(); - collection.Import(tbcApiCredentialsWithCertificate.CertificateFileName, tbcApiCredentialsWithCertificate.CertificatePassword, X509KeyStorageFlags.PersistKeySet); + collection.Import(_credentials.CertificateFileName, _credentials.CertificatePassword, X509KeyStorageFlags.PersistKeySet); return collection; } } diff --git a/AppifySheets.TBC.IntegrationService.Tests/AppifySheets.TBC.IntegrationService.Tests.csproj b/AppifySheets.TBC.IntegrationService.Tests/AppifySheets.TBC.IntegrationService.Tests.csproj index b302c31..bc3d4fd 100644 --- a/AppifySheets.TBC.IntegrationService.Tests/AppifySheets.TBC.IntegrationService.Tests.csproj +++ b/AppifySheets.TBC.IntegrationService.Tests/AppifySheets.TBC.IntegrationService.Tests.csproj @@ -25,6 +25,7 @@ + diff --git a/AppifySheets.TBC.IntegrationService.Tests/TBCSoapCallerTests.cs b/AppifySheets.TBC.IntegrationService.Tests/TBCSoapCallerTests.cs index 49b783c..1e110a1 100644 --- a/AppifySheets.TBC.IntegrationService.Tests/TBCSoapCallerTests.cs +++ b/AppifySheets.TBC.IntegrationService.Tests/TBCSoapCallerTests.cs @@ -63,10 +63,10 @@ public async Task PostboxMessagesRequestSoapGetPaymentOrderStatus_should_return_ [Fact] public async Task ImportSinglePaymentOrders_returns_values() { - var ownAccountGEL = new BankAccountWithCurrencyV(new BankAccountV("GE31TB7467936080100003"), CurrencyV.GEL); - var ownAccountUSD = new BankAccountWithCurrencyV(new BankAccountV("GE47TB7467936170100001"), CurrencyV.USD); + var ownAccountGEL = BankAccount.Create("GE31TB7467936080100003", "GEL").Value; + var ownAccountUSD = BankAccount.Create("GE47TB7467936170100001", "USD").Value; - var transferTypeRecordSpecific = new TransferTypeRecordSpecific + var bankTransferCommonDetails = new BankTransferCommonDetails { DocumentNumber = 63865984018636, Amount = 0.01m, @@ -79,43 +79,43 @@ public async Task ImportSinglePaymentOrders_returns_values() var withinBankGel2 = await _tbcSoapCaller.GetDeserialized(new ImportSinglePaymentOrdersRequestIo( new TransferWithinBankPaymentOrderIo { - RecipientAccountWithCurrency = new BankAccountWithCurrencyV(new BankAccountV("GE24TB7755145063300001"), CurrencyV.GEL), - TransferTypeRecordSpecific = transferTypeRecordSpecific + RecipientAccountWithCurrency = BankAccount.Create("GE24TB7755145063300001", "GEL").Value, + BankTransferCommonDetails = bankTransferCommonDetails })); var withinBankCurrency = await _tbcSoapCaller.GetDeserialized(new ImportSinglePaymentOrdersRequestIo( new TransferWithinBankPaymentOrderIo { - TransferTypeRecordSpecific = transferTypeRecordSpecific with + BankTransferCommonDetails = bankTransferCommonDetails with { SenderAccountWithCurrency = ownAccountUSD }, - RecipientAccountWithCurrency = new BankAccountWithCurrencyV(new BankAccountV("GE86TB1144836120100002"), CurrencyV.USD), + RecipientAccountWithCurrency = BankAccount.Create("GE86TB1144836120100002", "USD").Value, })); var toAnotherBankGel = await _tbcSoapCaller.GetDeserialized( new ImportSinglePaymentOrdersRequestIo( new TransferToOtherBankNationalCurrencyPaymentOrderIo( - new BankAccountWithCurrencyV(new BankAccountV("GE33BG0000000263255500"), CurrencyV.GEL), "123123123") + BankAccount.Create("GE33BG0000000263255500", "GEL").Value, "123123123") { - TransferTypeRecordSpecific = transferTypeRecordSpecific + BankTransferCommonDetails = bankTransferCommonDetails })); var toAnotherBankCurrencyGood = await _tbcSoapCaller.GetDeserialized( new ImportSinglePaymentOrdersRequestIo( new TransferToOtherBankForeignCurrencyPaymentOrderIo( "test", "test", "SHA", "TEST", - new BankAccountWithCurrencyV(new BankAccountV("GE33BG0000000263255500"), CurrencyV.USD)) + BankAccount.Create("GE33BG0000000263255500", "USD").Value) { - TransferTypeRecordSpecific = transferTypeRecordSpecific with { SenderAccountWithCurrency = ownAccountUSD } + BankTransferCommonDetails = bankTransferCommonDetails with { SenderAccountWithCurrency = ownAccountUSD } })); var toAnotherBankCurrencyBad = await _tbcSoapCaller.GetDeserialized( new ImportSinglePaymentOrdersRequestIo( new TransferToOtherBankForeignCurrencyPaymentOrderIo("test", "test", "SHA", "TEST", - new BankAccountWithCurrencyV(new BankAccountV("GE33BG0000000263255500"), CurrencyV.USD)) + BankAccount.Create("GE33BG0000000263255500", "USD").Value) { - TransferTypeRecordSpecific = transferTypeRecordSpecific with { SenderAccountWithCurrency = ownAccountUSD } + BankTransferCommonDetails = bankTransferCommonDetails with { SenderAccountWithCurrency = ownAccountUSD } })); var toChina = await _tbcSoapCaller.GetDeserialized( @@ -123,9 +123,9 @@ public async Task ImportSinglePaymentOrders_returns_values() new TransferToOtherBankForeignCurrencyPaymentOrderIo("China", // "ICBKCNBJSZN", "INDUSTRIAL AND COMMERCIAL BANK OF CHINA SHENZHEN BRANCH", "SHA", "Invoice(LZSK202311028)", "ICBKCNBJSZN", "INDUSTRIAL AND COMMERCIAL BANK OF CHINA SHENZHEN BRANCH", "SHA", - new BankAccountWithCurrencyV(new BankAccountV("4000109819100186641"), CurrencyV.USD)) + BankAccount.Create("4000109819100186641", "USD").Value) { - TransferTypeRecordSpecific = transferTypeRecordSpecific with + BankTransferCommonDetails = bankTransferCommonDetails with { SenderAccountWithCurrency = ownAccountUSD, BeneficiaryName = "Shenzhen Shinekoo Supply Chain Co.,Ltd" @@ -135,6 +135,6 @@ public async Task ImportSinglePaymentOrders_returns_values() var toTreasury = await _tbcSoapCaller.GetDeserialized( new ImportSinglePaymentOrdersRequestIo( new TreasuryTransferPaymentOrderIo(101001000) - { TransferTypeRecordSpecific = transferTypeRecordSpecific })); + { BankTransferCommonDetails = bankTransferCommonDetails })); } } \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 8587c54..04d48c6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -55,7 +55,7 @@ dotnet run --project AppifySheets.TBC.IntegrationService.Client.DemoConsole ### When Working with Transfer Types - All transfer types implement specific interfaces that define required fields -- Use `TransferTypeRecordSpecific` for common transfer fields +- Use `BankTransferCommonDetails` for common transfer fields - Each transfer type has its own validation rules and required fields ### Testing Approach diff --git a/README.md b/README.md index 134d6ef..05a685a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ #### You will require 4 things from the TBC Bank - 1) `.pfx` certificate, 2) `Username`, 3) `Password` and 4) `certificate_password` -Service Documentation by the TBC Bank is here - https://developers.tbcbank.ge/docs/dbi-overview +Service Documentation by the TBC Bank is here - https://developers.tbcbank.ge/docs/dbi-overview ## Installation @@ -13,23 +13,39 @@ dotnet add package AppifySheets.TBC.IntegrationService.Client ``` ## Following services are implemented: -* [Import Single Payment Orders](https://developers.tbcbank.ge/docs/import-single-payments) - Execute various types of payment transfers -* [Get Account Movements](https://developers.tbcbank.ge/docs/account-movement) - Retrieve account transaction history -* [Get Payment Order Status](https://developers.tbcbank.ge/docs/get-payment-order-status) - Check status of submitted payment orders -* [Change Password](https://developers.tbcbank.ge/docs/change-password) - Change API user password +* Import Single Payment Orders - Execute various types of payment transfers +* Get Account Movements - Retrieve account transaction history +* Get Payment Order Status - Check status of submitted payment orders +* Change Password - Change API user password ## Usage Examples ### Setup ```csharp -var credentials = new TBCApiCredentials("Username", "Password"); // Obtain API Credentials & Certificate with password from the Bank/Banker -var tbcApiCredentialsWithCertificate = new TBCApiCredentialsWithCertificate(credentials, "TBCIntegrationService.pfx", "certificate_password"); - -var tbcSoapCaller = new TBCSoapCaller(tbcApiCredentialsWithCertificate); - -// Example IBAN format: GE00TB0000000000000000 -var ownAccountGEL = BankAccountWithCurrencyV.Create(new BankAccountV("GE00TB0000000000000001"), CurrencyV.GEL).Value; -var ownAccountUSD = BankAccountWithCurrencyV.Create(new BankAccountV("GE00TB0000000000000002"), CurrencyV.USD).Value; +// Create API credentials with validation +var credentialsResult = TBCApiCredentials.Create("Username", "Password"); +if (credentialsResult.IsFailure) + return credentialsResult.Error; + +// Add certificate authentication +var credentialsWithCertResult = TBCApiCredentialsWithCertificate.Create( + credentialsResult.Value, + "TBCIntegrationService.pfx", + "certificate_password"); + +// Create SOAP caller +var soapCallerResult = TBCSoapCaller.Create(credentialsWithCertResult.Value); +var tbcSoapCaller = soapCallerResult.Value; + +// Create bank accounts +var ownAccountGEL = BankAccount.Create("GE00TB0000000000000001", "GEL").Value; +var ownAccountUSD = BankAccount.Create("GE00TB0000000000000002", "USD").Value; + +// Or create components separately +var ibanResult = Iban.Create("GE00TB0000000000000001"); +var accountResult = BankAccount.Create(ibanResult.Value, Currency.GEL); +if (accountResult.IsFailure) + return accountResult.Error; ``` ### Account Operations @@ -84,7 +100,7 @@ var passwordChangeResult = await tbcSoapCaller.GetDeserialized( #### Common Transfer Parameters ```csharp // Common parameters for all transfer types -var transferTypeRecordSpecific = new TransferTypeRecordSpecific +var bankTransferCommonDetails = new BankTransferCommonDetails { DocumentNumber = 123, Amount = 0.01m, @@ -102,9 +118,8 @@ var transferTypeRecordSpecific = new TransferTypeRecordSpecific var withinBankGel = await tbcSoapCaller.GetDeserialized(new ImportSinglePaymentOrdersRequestIo( new TransferWithinBankPaymentOrderIo { - RecipientAccountWithCurrency = BankAccountWithCurrencyV.Create( - new BankAccountV("GE00TB0000000000000003"), CurrencyV.GEL).Value, - TransferTypeRecordSpecific = transferTypeRecordSpecific + RecipientAccountWithCurrency = BankAccount.Create("GE00TB0000000000000003", "GEL").Value, + BankTransferCommonDetails = bankTransferCommonDetails })); ``` @@ -113,12 +128,11 @@ var withinBankGel = await tbcSoapCaller.GetDeserialized(new ImportSinglePaymentO var withinBankCurrency = await tbcSoapCaller.GetDeserialized(new ImportSinglePaymentOrdersRequestIo( new TransferWithinBankPaymentOrderIo { - TransferTypeRecordSpecific = transferTypeRecordSpecific with + BankTransferCommonDetails = bankTransferCommonDetails with { SenderAccountWithCurrency = ownAccountUSD }, - RecipientAccountWithCurrency = BankAccountWithCurrencyV.Create( - new BankAccountV("GE00TB0000000000000004"), CurrencyV.USD).Value, + RecipientAccountWithCurrency = BankAccount.Create("GE00TB0000000000000004", "USD").Value, })); ``` @@ -131,10 +145,10 @@ var withinBankCurrency = await tbcSoapCaller.GetDeserialized(new ImportSinglePay var toAnotherBankGel = await tbcSoapCaller.GetDeserialized( new ImportSinglePaymentOrdersRequestIo( new TransferToOtherBankNationalCurrencyPaymentOrderIo( - BankAccountWithCurrencyV.Create(new BankAccountV("GE00BG0000000000000001"), CurrencyV.GEL).Value, + BankAccount.Create("GE00BG0000000000000001", "GEL").Value, "123456789") // Beneficiary tax code { - TransferTypeRecordSpecific = transferTypeRecordSpecific + BankTransferCommonDetails = bankTransferCommonDetails })); ``` @@ -147,9 +161,9 @@ var toAnotherBankCurrency = await tbcSoapCaller.GetDeserialized( "BANKSWIFT", // Bank SWIFT/BIC code "SHA", // Charge type: SHA (shared), OUR (sender pays), BEN (beneficiary pays) "Payment Reference", - BankAccountWithCurrencyV.Create(new BankAccountV("GE00BG0000000000000002"), CurrencyV.USD).Value) + BankAccount.Create("GE00BG0000000000000002", "USD").Value) { - TransferTypeRecordSpecific = transferTypeRecordSpecific with + BankTransferCommonDetails = bankTransferCommonDetails with { SenderAccountWithCurrency = ownAccountUSD } @@ -165,9 +179,9 @@ var toChina = await tbcSoapCaller.GetDeserialized( "ICBKCNBJSZN", // Bank SWIFT code "INDUSTRIAL AND COMMERCIAL BANK OF CHINA SHENZHEN BRANCH", // Bank name "SHA", // Charge type - BankAccountWithCurrencyV.Create(new BankAccountV("CN0000000000000000001"), CurrencyV.USD).Value) + BankAccount.Create("CN0000000000000000001", "USD").Value) { - TransferTypeRecordSpecific = transferTypeRecordSpecific with + BankTransferCommonDetails = bankTransferCommonDetails with { SenderAccountWithCurrency = ownAccountUSD, BeneficiaryName = "Shenzhen Example Company Ltd" @@ -185,7 +199,7 @@ var toTreasury = await tbcSoapCaller.GetDeserialized( new ImportSinglePaymentOrdersRequestIo( new TreasuryTransferPaymentOrderIo(101001000) // Treasury code { - TransferTypeRecordSpecific = transferTypeRecordSpecific + BankTransferCommonDetails = bankTransferCommonDetails })); ``` diff --git a/VERSION b/VERSION index 8cfbc90..359a5b9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.1 \ No newline at end of file +2.0.0 \ No newline at end of file diff --git a/artifacts/test-results/AppifySheets.TBC.IntegrationService.Tests.trx b/artifacts/test-results/AppifySheets.TBC.IntegrationService.Tests.trx deleted file mode 100644 index 9c87187..0000000 --- a/artifacts/test-results/AppifySheets.TBC.IntegrationService.Tests.trx +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - - - - - - Interop+Crypto+OpenSslCryptographicException : error:10000080:BIO routines::no such file - at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle) - at System.Security.Cryptography.X509Certificates.StorePal.FromFile(String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) - at System.Security.Cryptography.X509Certificates.X509Certificate2Collection.Import(String fileName, String password, X509KeyStorageFlags keyStorageFlags) - at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.<>c__DisplayClass5_0.<CallTBCServiceAsync>g__GetCertificates|1() in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs:line 120 - at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.CallTBCServiceAsync(PerformedActionSoapEnvelope performedActionSoapEnvelope) in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs:line 88 - at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.GetDeserialized[TDeserializeInto](RequestSoap`1 RequestSoap) in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs:line 22 - at AppifySheets.TBC.IntegrationService.Tests.TBCSoapCallerTests.PostboxMessagesRequestSoapGetPaymentOrderStatus_should_return_values() in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Tests/TBCSoapCallerTests.cs:line 59 ---- End of stack trace from previous location --- - - - - - - - Interop+Crypto+OpenSslCryptographicException : error:10000080:BIO routines::no such file - at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle) - at System.Security.Cryptography.X509Certificates.StorePal.FromFile(String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) - at System.Security.Cryptography.X509Certificates.X509Certificate2Collection.Import(String fileName, String password, X509KeyStorageFlags keyStorageFlags) - at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.<>c__DisplayClass5_0.<CallTBCServiceAsync>g__GetCertificates|1() in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs:line 120 - at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.CallTBCServiceAsync(PerformedActionSoapEnvelope performedActionSoapEnvelope) in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs:line 88 - at AppifySheets.TBC.IntegrationService.Client.SoapInfrastructure.GetAccountMovements.GetAccountMovementsHelper.<>c__DisplayClass2_0.<<GetAccountMovementAsync>g__GetDataAsync|0>d.MoveNext() in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/SoapInfrastructure/GetAccountMovements/GetAccountMovementsHelper.cs:line 47 ---- End of stack trace from previous location --- - at AppifySheets.TBC.IntegrationService.Client.SoapInfrastructure.GetAccountMovements.GetAccountMovementsHelper.GetAccountMovementAsync(Period period, TBCSoapCaller tbcSoapCaller) in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/SoapInfrastructure/GetAccountMovements/GetAccountMovementsHelper.cs:line 17 - at AppifySheets.TBC.IntegrationService.Tests.TBCSoapCallerTests.AccountMovements_returns_values() in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Tests/TBCSoapCallerTests.cs:line 42 ---- End of stack trace from previous location --- - - - - - - - Interop+Crypto+OpenSslCryptographicException : error:10000080:BIO routines::no such file - at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle) - at System.Security.Cryptography.X509Certificates.StorePal.FromFile(String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) - at System.Security.Cryptography.X509Certificates.X509Certificate2Collection.Import(String fileName, String password, X509KeyStorageFlags keyStorageFlags) - at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.<>c__DisplayClass5_0.<CallTBCServiceAsync>g__GetCertificates|1() in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs:line 120 - at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.CallTBCServiceAsync(PerformedActionSoapEnvelope performedActionSoapEnvelope) in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs:line 88 - at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.GetDeserialized[TDeserializeInto](RequestSoap`1 RequestSoap) in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs:line 22 - at AppifySheets.TBC.IntegrationService.Tests.TBCSoapCallerTests.ImportSinglePaymentOrders_returns_values() in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Tests/TBCSoapCallerTests.cs:line 79 ---- End of stack trace from previous location --- - - - - - - - Interop+Crypto+OpenSslCryptographicException : error:10000080:BIO routines::no such file - at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle) - at System.Security.Cryptography.X509Certificates.StorePal.FromFile(String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) - at System.Security.Cryptography.X509Certificates.X509Certificate2Collection.Import(String fileName, String password, X509KeyStorageFlags keyStorageFlags) - at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.<>c__DisplayClass5_0.<CallTBCServiceAsync>g__GetCertificates|1() in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs:line 120 - at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.CallTBCServiceAsync(PerformedActionSoapEnvelope performedActionSoapEnvelope) in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs:line 88 - at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.GetDeserialized[TDeserializeInto](RequestSoap`1 RequestSoap) in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs:line 22 - at AppifySheets.TBC.IntegrationService.Tests.TBCSoapCallerTests.PasswordChangeIsSuccessful() in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Tests/TBCSoapCallerTests.cs:line 35 ---- End of stack trace from previous location --- - - - - - - - Interop+Crypto+OpenSslCryptographicException : error:10000080:BIO routines::no such file - at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle) - at System.Security.Cryptography.X509Certificates.StorePal.FromFile(String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) - at System.Security.Cryptography.X509Certificates.X509Certificate2Collection.Import(String fileName, String password, X509KeyStorageFlags keyStorageFlags) - at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.<>c__DisplayClass5_0.<CallTBCServiceAsync>g__GetCertificates|1() in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs:line 120 - at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.CallTBCServiceAsync(PerformedActionSoapEnvelope performedActionSoapEnvelope) in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs:line 88 - at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.GetDeserialized[TDeserializeInto](RequestSoap`1 RequestSoap) in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs:line 22 - at AppifySheets.TBC.IntegrationService.Tests.TBCSoapCallerTests.RequestSoapGetPaymentOrderStatus_should_return_values() in /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Tests/TBCSoapCallerTests.cs:line 52 ---- End of stack trace from previous location --- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - [xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.4.5+1caef2f33e (64-bit .NET 8.0.17) -[xUnit.net 00:00:00.26] Discovering: AppifySheets.TBC.IntegrationService.Tests -[xUnit.net 00:00:00.28] Discovered: AppifySheets.TBC.IntegrationService.Tests -[xUnit.net 00:00:00.28] Starting: AppifySheets.TBC.IntegrationService.Tests -[xUnit.net 00:00:00.31] Interop+Crypto+OpenSslCryptographicException : error:10000080:BIO routines::no such file -[xUnit.net 00:00:00.31] Stack Trace: -[xUnit.net 00:00:00.31] at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle) -[xUnit.net 00:00:00.31] at System.Security.Cryptography.X509Certificates.StorePal.FromFile(String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) -[xUnit.net 00:00:00.31] at System.Security.Cryptography.X509Certificates.X509Certificate2Collection.Import(String fileName, String password, X509KeyStorageFlags keyStorageFlags) -[xUnit.net 00:00:00.31] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs(120,0): at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.<>c__DisplayClass5_0.<CallTBCServiceAsync>g__GetCertificates|1() -[xUnit.net 00:00:00.31] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs(88,0): at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.CallTBCServiceAsync(PerformedActionSoapEnvelope performedActionSoapEnvelope) -[xUnit.net 00:00:00.31] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs(22,0): at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.GetDeserialized[TDeserializeInto](RequestSoap`1 RequestSoap) -[xUnit.net 00:00:00.31] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Tests/TBCSoapCallerTests.cs(59,0): at AppifySheets.TBC.IntegrationService.Tests.TBCSoapCallerTests.PostboxMessagesRequestSoapGetPaymentOrderStatus_should_return_values() -[xUnit.net 00:00:00.31] --- End of stack trace from previous location --- -[xUnit.net 00:00:00.31] Interop+Crypto+OpenSslCryptographicException : error:10000080:BIO routines::no such file -[xUnit.net 00:00:00.31] Stack Trace: -[xUnit.net 00:00:00.31] at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle) -[xUnit.net 00:00:00.31] at System.Security.Cryptography.X509Certificates.StorePal.FromFile(String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) -[xUnit.net 00:00:00.31] at System.Security.Cryptography.X509Certificates.X509Certificate2Collection.Import(String fileName, String password, X509KeyStorageFlags keyStorageFlags) -[xUnit.net 00:00:00.31] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs(120,0): at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.<>c__DisplayClass5_0.<CallTBCServiceAsync>g__GetCertificates|1() -[xUnit.net 00:00:00.31] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs(88,0): at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.CallTBCServiceAsync(PerformedActionSoapEnvelope performedActionSoapEnvelope) -[xUnit.net 00:00:00.31] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs(22,0): at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.GetDeserialized[TDeserializeInto](RequestSoap`1 RequestSoap) -[xUnit.net 00:00:00.31] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Tests/TBCSoapCallerTests.cs(79,0): at AppifySheets.TBC.IntegrationService.Tests.TBCSoapCallerTests.ImportSinglePaymentOrders_returns_values() -[xUnit.net 00:00:00.31] --- End of stack trace from previous location --- -[xUnit.net 00:00:00.32] Interop+Crypto+OpenSslCryptographicException : error:10000080:BIO routines::no such file -[xUnit.net 00:00:00.32] Stack Trace: -[xUnit.net 00:00:00.32] at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle) -[xUnit.net 00:00:00.32] at System.Security.Cryptography.X509Certificates.StorePal.FromFile(String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) -[xUnit.net 00:00:00.32] at System.Security.Cryptography.X509Certificates.X509Certificate2Collection.Import(String fileName, String password, X509KeyStorageFlags keyStorageFlags) -[xUnit.net 00:00:00.32] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs(120,0): at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.<>c__DisplayClass5_0.<CallTBCServiceAsync>g__GetCertificates|1() -[xUnit.net 00:00:00.32] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs(88,0): at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.CallTBCServiceAsync(PerformedActionSoapEnvelope performedActionSoapEnvelope) -[xUnit.net 00:00:00.32] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs(22,0): at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.GetDeserialized[TDeserializeInto](RequestSoap`1 RequestSoap) -[xUnit.net 00:00:00.32] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Tests/TBCSoapCallerTests.cs(52,0): at AppifySheets.TBC.IntegrationService.Tests.TBCSoapCallerTests.RequestSoapGetPaymentOrderStatus_should_return_values() -[xUnit.net 00:00:00.32] --- End of stack trace from previous location --- -[xUnit.net 00:00:00.32] Interop+Crypto+OpenSslCryptographicException : error:10000080:BIO routines::no such file -[xUnit.net 00:00:00.32] Stack Trace: -[xUnit.net 00:00:00.32] at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle) -[xUnit.net 00:00:00.32] at System.Security.Cryptography.X509Certificates.StorePal.FromFile(String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) -[xUnit.net 00:00:00.32] at System.Security.Cryptography.X509Certificates.X509Certificate2Collection.Import(String fileName, String password, X509KeyStorageFlags keyStorageFlags) -[xUnit.net 00:00:00.32] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs(120,0): at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.<>c__DisplayClass5_0.<CallTBCServiceAsync>g__GetCertificates|1() -[xUnit.net 00:00:00.32] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs(88,0): at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.CallTBCServiceAsync(PerformedActionSoapEnvelope performedActionSoapEnvelope) -[xUnit.net 00:00:00.32] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/SoapInfrastructure/GetAccountMovements/GetAccountMovementsHelper.cs(47,0): at AppifySheets.TBC.IntegrationService.Client.SoapInfrastructure.GetAccountMovements.GetAccountMovementsHelper.<>c__DisplayClass2_0.<<GetAccountMovementAsync>g__GetDataAsync|0>d.MoveNext() -[xUnit.net 00:00:00.32] --- End of stack trace from previous location --- -[xUnit.net 00:00:00.32] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/SoapInfrastructure/GetAccountMovements/GetAccountMovementsHelper.cs(17,0): at AppifySheets.TBC.IntegrationService.Client.SoapInfrastructure.GetAccountMovements.GetAccountMovementsHelper.GetAccountMovementAsync(Period period, TBCSoapCaller tbcSoapCaller) -[xUnit.net 00:00:00.32] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Tests/TBCSoapCallerTests.cs(42,0): at AppifySheets.TBC.IntegrationService.Tests.TBCSoapCallerTests.AccountMovements_returns_values() -[xUnit.net 00:00:00.32] --- End of stack trace from previous location --- -[xUnit.net 00:00:00.32] Interop+Crypto+OpenSslCryptographicException : error:10000080:BIO routines::no such file -[xUnit.net 00:00:00.32] Stack Trace: -[xUnit.net 00:00:00.32] at Interop.Crypto.CheckValidOpenSslHandle(SafeHandle handle) -[xUnit.net 00:00:00.32] at System.Security.Cryptography.X509Certificates.StorePal.FromFile(String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags) -[xUnit.net 00:00:00.32] at System.Security.Cryptography.X509Certificates.X509Certificate2Collection.Import(String fileName, String password, X509KeyStorageFlags keyStorageFlags) -[xUnit.net 00:00:00.32] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs(120,0): at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.<>c__DisplayClass5_0.<CallTBCServiceAsync>g__GetCertificates|1() -[xUnit.net 00:00:00.32] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs(88,0): at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.CallTBCServiceAsync(PerformedActionSoapEnvelope performedActionSoapEnvelope) -[xUnit.net 00:00:00.32] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Client/TBC_Services/TBCSoapCaller.cs(22,0): at AppifySheets.TBC.IntegrationService.Client.TBC_Services.TBCSoapCaller.GetDeserialized[TDeserializeInto](RequestSoap`1 RequestSoap) -[xUnit.net 00:00:00.32] /home/petre/repos/EuroCredit.CRM2Alta.API/TBC-IntegrationService-StandardPlus-Client/AppifySheets.TBC.IntegrationService.Tests/TBCSoapCallerTests.cs(35,0): at AppifySheets.TBC.IntegrationService.Tests.TBCSoapCallerTests.PasswordChangeIsSuccessful() -[xUnit.net 00:00:00.32] --- End of stack trace from previous location --- -[xUnit.net 00:00:00.32] Finished: AppifySheets.TBC.IntegrationService.Tests - - - - - [xUnit.net 00:00:00.31] AppifySheets.TBC.IntegrationService.Tests.TBCSoapCallerTests.PostboxMessagesRequestSoapGetPaymentOrderStatus_should_return_values [FAIL] - - - [xUnit.net 00:00:00.31] AppifySheets.TBC.IntegrationService.Tests.TBCSoapCallerTests.ImportSinglePaymentOrders_returns_values [FAIL] - - - [xUnit.net 00:00:00.32] AppifySheets.TBC.IntegrationService.Tests.TBCSoapCallerTests.RequestSoapGetPaymentOrderStatus_should_return_values [FAIL] - - - [xUnit.net 00:00:00.32] AppifySheets.TBC.IntegrationService.Tests.TBCSoapCallerTests.AccountMovements_returns_values [FAIL] - - - [xUnit.net 00:00:00.32] AppifySheets.TBC.IntegrationService.Tests.TBCSoapCallerTests.PasswordChangeIsSuccessful [FAIL] - - - - \ No newline at end of file diff --git a/nupkg/AppifySheets.TBC.IntegrationService.Client.1.1.0.nupkg b/nupkg/AppifySheets.TBC.IntegrationService.Client.1.1.0.nupkg deleted file mode 100644 index 84df156..0000000 Binary files a/nupkg/AppifySheets.TBC.IntegrationService.Client.1.1.0.nupkg and /dev/null differ