diff --git a/Adyen.IntegrationTest/Adyen.IntegrationTest.csproj b/Adyen.IntegrationTest/Adyen.IntegrationTest.csproj index 7662e15c7..bc52439c6 100644 --- a/Adyen.IntegrationTest/Adyen.IntegrationTest.csproj +++ b/Adyen.IntegrationTest/Adyen.IntegrationTest.csproj @@ -1,7 +1,7 @@ - net8.0;net6.0 + net8.0 12 enable disable diff --git a/Adyen.IntegrationTest/BaseTest.cs b/Adyen.IntegrationTest/BaseTest.cs index 1fcb4e960..ecb866f49 100644 --- a/Adyen.IntegrationTest/BaseTest.cs +++ b/Adyen.IntegrationTest/BaseTest.cs @@ -3,12 +3,9 @@ using System.Threading.Tasks; using Adyen.Model; -using Adyen.Model.BinLookup; using Adyen.Model.Checkout; using Adyen.Model.Payment; using Adyen.Service; -using Adyen.Service.Checkout; -using Amount = Adyen.Model.Checkout; using PaymentRequest = Adyen.Model.Payment.PaymentRequest; using PaymentResult = Adyen.Model.Payment.PaymentResult; using Environment = Adyen.Model.Environment; @@ -59,7 +56,7 @@ public PaymentResult CreatePaymentResultWithIdempotency(string idempotency) return paymentResult; } - public PaymentResult CreatePaymentResultWithRecurring(Recurring.ContractEnum contract) + public PaymentResult CreatePaymentResultWithRecurring(Model.Payment.Recurring.ContractEnum contract) { var client = CreateApiKeyTestClient(); var payment = new PaymentService(client); @@ -178,9 +175,9 @@ private PaymentRequest CreateFullPaymentRequest() return paymentRequest; } - private PaymentRequest CreateFullPaymentRequestWithRecurring(Recurring.ContractEnum contract) + private Model.Payment.PaymentRequest CreateFullPaymentRequestWithRecurring(Model.Payment.Recurring.ContractEnum contract) { - var paymentRequest = new PaymentRequest + var paymentRequest = new Model.Payment.PaymentRequest { MerchantAccount = ClientConstants.MerchantAccount, Amount = new Model.Payment.Amount("EUR", 1500), @@ -188,7 +185,7 @@ private PaymentRequest CreateFullPaymentRequestWithRecurring(Recurring.ContractE Reference = "payment - " + DateTime.Now.ToString("yyyyMMdd"), ShopperReference = "test-1234", AdditionalData = CreateAdditionalData(), - Recurring = new Recurring { Contract = contract }, + Recurring = new Model.Payment.Recurring() { Contract = contract }, ApplicationInfo = new Model.Payment.ApplicationInfo() { ExternalPlatform = new Model.Payment.ExternalPlatform() diff --git a/Adyen.IntegrationTest/Checkout/PaymentsServiceIntegrationTest.cs b/Adyen.IntegrationTest/Checkout/PaymentsServiceIntegrationTest.cs new file mode 100644 index 000000000..f63fcd9ec --- /dev/null +++ b/Adyen.IntegrationTest/Checkout/PaymentsServiceIntegrationTest.cs @@ -0,0 +1,246 @@ +using System.Diagnostics.Tracing; +using Adyen.Checkout.Extensions; +using Adyen.Checkout.Models; +using Adyen.Checkout.Services; +using Adyen.Core.Client; +using Adyen.Core.Client.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.Extensions.Hosting; +using Adyen.Core.Options; +using Microsoft.Extensions.Logging; + +namespace Adyen.IntegrationTest.Checkout +{ + [TestClass] + public class PaymentsServiceIntegrationTest + { + private readonly IPaymentsService _paymentsApiService; + + public PaymentsServiceIntegrationTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureCheckout( + (context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.AdyenApiKey = context.Configuration["ADYEN_API_KEY"]; + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _paymentsApiService = host.Services.GetRequiredService(); + + // Example how to do logging for IPaymentsService and the PaymensServiceEvents. + ILogger logger = host.Services.GetRequiredService>(); + + PaymentsServiceEvents events = host.Services.GetRequiredService(); + + // On /payments + events.OnPayments += (sender, eventArgs) => + { + ApiResponse apiResponse = eventArgs.ApiResponse; + logger.LogInformation("{TotalSeconds,-9} | {Path} | {StatusCode} |", (apiResponse.DownloadedAt - apiResponse.RequestedAt).TotalSeconds, apiResponse.StatusCode, apiResponse.Path); + }; + + // OnError /payments. + events.OnErrorPayments += (sender, eventArgs) => + { + logger.LogError(eventArgs.Exception, "An error occurred after sending the request to the server."); + }; + + } + + [TestMethod] + public async Task Given_Payments_When_CardDetails_Provided_Returns_OK() + { + // Arrange + var request = new PaymentRequest( + amount: new Amount("EUR", 1999), + merchantAccount: "HeapUnderflowECOM", + reference: "reference", + returnUrl: "https://adyen.com/", + paymentMethod: new CheckoutPaymentMethod( + new CardDetails( + type: CardDetails.TypeEnum.Scheme, + encryptedCardNumber: "test_4111111111111111", + encryptedExpiryMonth: "test_03", + encryptedExpiryYear: "test_2030", + encryptedSecurityCode: "test_737", + holderName: "John Smith" + ) + ) + ); + IPaymentsApiResponse response = await _paymentsApiService.PaymentsAsync(Guid.NewGuid().ToString(), request); + + response.TryDeserializeOkResponse(out var result); + Assert.AreEqual(result?.MerchantReference, "reference"); + } + + [TestMethod] + public async Task Given_Payments_When_CardDetails_Without_Idempotency_Key_Provided_Returns_DifferentRefs() + { + // Test when no idempotency key is provided + var request = new PaymentRequest( + amount: new Amount("EUR", 1999), + merchantAccount: "HeapUnderflowECOM", + reference: "ref1-original-request-1", + returnUrl: "https://adyen.com/", + paymentMethod: new CheckoutPaymentMethod( + new CardDetails( + type: CardDetails.TypeEnum.Scheme, + encryptedCardNumber: "test_4111111111111111", + encryptedExpiryMonth: "test_03", + encryptedExpiryYear: "test_2030", + encryptedSecurityCode: "test_737", + holderName: "John Smith" + ) + ) + ); + IPaymentsApiResponse response = await _paymentsApiService.PaymentsAsync(paymentRequest: request); + + response.TryDeserializeOkResponse(out PaymentResponse result); + Assert.AreEqual(result?.MerchantReference, "ref1-original-request-1"); + + + // Test when no idempotency key is provided + var request2 = new PaymentRequest( + amount: new Amount("EUR", 1999), + merchantAccount: "HeapUnderflowECOM", + reference: "ref2-should-be-different", + returnUrl: "https://adyen.com/", + paymentMethod: new CheckoutPaymentMethod( + new CardDetails( + type: CardDetails.TypeEnum.Scheme, + encryptedCardNumber: "test_4111111111111111", + encryptedExpiryMonth: "test_03", + encryptedExpiryYear: "test_2030", + encryptedSecurityCode: "test_737", + holderName: "John Smith" + ) + ) + ); + IPaymentsApiResponse response2 = await _paymentsApiService.PaymentsAsync(paymentRequest: request2); + + response2.TryDeserializeOkResponse(out PaymentResponse result2); + Assert.AreEqual(result2?.MerchantReference, "ref2-should-be-different"); + + // Test when null is explicitly provided. + var request3 = new PaymentRequest( + amount: new Amount("EUR", 1999), + merchantAccount: "HeapUnderflowECOM", + reference: "ref3-should-be-very-different", + returnUrl: "https://adyen.com/", + paymentMethod: new CheckoutPaymentMethod( + new CardDetails( + type: CardDetails.TypeEnum.Scheme, + encryptedCardNumber: "test_4111111111111111", + encryptedExpiryMonth: "test_03", + encryptedExpiryYear: "test_2030", + encryptedSecurityCode: "test_737", + holderName: "John Smith" + ) + ) + ); + IPaymentsApiResponse response3 = await _paymentsApiService.PaymentsAsync(null, request3); + response3.TryDeserializeOkResponse(out PaymentResponse result3); + Assert.AreEqual(result3?.MerchantReference, "ref3-should-be-very-different"); + } + + [TestMethod] + public void HttpClientBuilderExtensions_Polly_Retry_CircuitBreaker_Timeout_Example() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureCheckout( + (context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.AdyenApiKey = context.Configuration["ADYEN_API_KEY"]; + options.Environment = AdyenEnvironment.Test; + }); + }, + httpClientBuilderOptions: (IHttpClientBuilder builder) => + { + builder.AddRetryPolicy(5); + builder.AddCircuitBreakerPolicy(3, TimeSpan.FromSeconds(30)); + builder.AddTimeoutPolicy(TimeSpan.FromMinutes(5)); + }) + .Build(); + } + + [TestMethod] + public void HttpClientBuilderExtensions_Regular_Timeout_Modify_HttpClient_Example() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureCheckout( + (context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.AdyenApiKey = context.Configuration["ADYEN_API_KEY"]; + options.Environment = AdyenEnvironment.Test; + }); + }, client => + { + client.Timeout = TimeSpan.FromMinutes(1); + }) + .Build(); + } + + [TestMethod] + public async Task PaymentsServiceEvents_Override_Delegates_Example() + { + // Arrange + IHost host = Host.CreateDefaultBuilder() + .ConfigureCheckout( + (context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.AdyenApiKey = context.Configuration["ADYEN_API_KEY"]; + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + var request = new PaymentRequest( + amount: new Amount("EUR", 1999), + merchantAccount: "HeapUnderflowECOM", + reference: "reference", + returnUrl: "https://adyen.com/", + paymentMethod: new CheckoutPaymentMethod( + new CardDetails( + type: CardDetails.TypeEnum.Scheme, + encryptedCardNumber: "test_4111111111111111", + encryptedExpiryMonth: "test_03", + encryptedExpiryYear: "test_2030", + encryptedSecurityCode: "test_737", + holderName: "John Smith" + ) + ) + ); + + + PaymentsServiceEvents paymentsServiceEvents = host.Services.GetRequiredService(); + IPaymentsService paymentsApiService = host.Services.GetRequiredService(); + + int isCalledOnce = 0; + + // Example override using a delegate: + paymentsServiceEvents.OnPayments += (sender, args) => + { + Console.WriteLine("OnPayments event received - IsSuccessStateCode: " + args.ApiResponse.IsSuccessStatusCode); + isCalledOnce++; + }; + + // Act + await paymentsApiService.PaymentsAsync(Guid.NewGuid().ToString(), request); + + // Assert + Assert.IsTrue(isCalledOnce == 1); + } + } +} \ No newline at end of file diff --git a/Adyen.IntegrationTest/ErrorTest.cs b/Adyen.IntegrationTest/ErrorTest.cs index f803087cf..33eb905db 100644 --- a/Adyen.IntegrationTest/ErrorTest.cs +++ b/Adyen.IntegrationTest/ErrorTest.cs @@ -49,9 +49,9 @@ public void TestClassicPaymentErrorHandling() ShopperEmail = "s.hopper@test.com", ShopperIP = "61.294.12.12", ShopperReference = "test-1234", - Recurring = new Recurring() + Recurring = new Model.Payment.Recurring() { - Contract = Recurring.ContractEnum.RECURRING + Contract = Model.Payment.Recurring.ContractEnum.RECURRING }, ShopperInteraction = PaymentRequest.ShopperInteractionEnum.Ecommerce, MerchantAccount = MerchantAccount diff --git a/Adyen.IntegrationTest/RecurringTest.cs b/Adyen.IntegrationTest/RecurringTest.cs index 42726c666..1eb082ef4 100644 --- a/Adyen.IntegrationTest/RecurringTest.cs +++ b/Adyen.IntegrationTest/RecurringTest.cs @@ -40,7 +40,7 @@ private RecurringDetailsRequest CreateRecurringDetailsRequest() { ShopperReference = "test-1234", MerchantAccount = ClientConstants.MerchantAccount, - Recurring = new Recurring { Contract = ContractEnum.RECURRING } + Recurring = new Model.Recurring.Recurring { Contract = ContractEnum.RECURRING } }; return request; } diff --git a/Adyen.Test/AcsWebhooks/AcsWebhooksTest.cs b/Adyen.Test/AcsWebhooks/AcsWebhooksTest.cs new file mode 100644 index 000000000..3fe638a98 --- /dev/null +++ b/Adyen.Test/AcsWebhooks/AcsWebhooksTest.cs @@ -0,0 +1,112 @@ +using Adyen.AcsWebhooks.Extensions; +using Adyen.AcsWebhooks.Models; +using Adyen.AcsWebhooks.Client; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.Extensions.Hosting; +using System.Text.Json; + +namespace Adyen.Test.AcsWebhooks +{ + [TestClass] + public class AcsWebhooksTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public AcsWebhooksTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureAcsWebhooks((context, services, config) => + { + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_Deserialize_Authentication_Webhook_WHen_OOB_TRIGGER_FL_Returns_Correct_Challenge_Flow() + { + // Arrange + string json = @" +{ + ""data"": { + ""authentication"": { + ""acsTransId"": ""6a4c1709-a42e-4c7f-96c7-1043adacfc97"", + ""challenge"": { + ""flow"": ""OOB_TRIGGER_FL"", + ""lastInteraction"": ""2022-12-22T15:49:03+01:00"" + }, + ""challengeIndicator"": ""01"", + ""createdAt"": ""2022-12-22T15:45:03+01:00"", + ""deviceChannel"": ""app"", + ""dsTransID"": ""a3b86754-444d-46ca-95a2-ada351d3f42c"", + ""exemptionIndicator"": ""lowValue"", + ""inPSD2Scope"": true, + ""messageCategory"": ""payment"", + ""messageVersion"": ""2.2.0"", + ""riskScore"": 0, + ""threeDSServerTransID"": ""6edcc246-23ee-4e94-ac5d-8ae620bea7d9"", + ""transStatus"": ""Y"", + ""type"": ""challenge"" + }, + ""balancePlatform"": ""YOUR_BALANCE_PLATFORM"", + ""id"": ""497f6eca-6276-4993-bfeb-53cbbbba6f08"", + ""paymentInstrumentId"": ""PI3227C223222B5BPCMFXD2XG"", + ""purchase"": { + ""date"": ""2022-12-22T15:49:03+01:00"", + ""merchantName"": ""MyShop"", + ""originalAmount"": { + ""currency"": ""EUR"", + ""value"": 1000 + } + }, + ""status"": ""authenticated"" + }, + ""environment"": ""test"", + ""timestamp"": ""2022-12-22T15:42:03+01:00"", + ""type"": ""balancePlatform.authentication.created"" +} +"; + // Act + AuthenticationNotificationRequest r = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.IsNotNull(r); + Assert.AreEqual(AuthenticationNotificationRequest.TypeEnum.BalancePlatformAuthenticationCreated, r.Type); + Assert.AreEqual("test", r.Environment); + Assert.AreEqual(DateTime.Parse("2022-12-22T15:42:03+01:00"), r.Timestamp); + + Assert.IsNotNull(r.Data); + Assert.AreEqual("497f6eca-6276-4993-bfeb-53cbbbba6f08", r.Data.Id); + Assert.AreEqual("YOUR_BALANCE_PLATFORM", r.Data.BalancePlatform); + Assert.AreEqual("PI3227C223222B5BPCMFXD2XG", r.Data.PaymentInstrumentId); + Assert.AreEqual(AuthenticationNotificationData.StatusEnum.Authenticated, r.Data.Status); + + Assert.IsNotNull(r.Data.Purchase); + Assert.AreEqual("2022-12-22T15:49:03+01:00", r.Data.Purchase.Date); + Assert.AreEqual("MyShop", r.Data.Purchase.MerchantName); + Assert.AreEqual("EUR", r.Data.Purchase.OriginalAmount.Currency); + Assert.AreEqual(1000, r.Data.Purchase.OriginalAmount.Value); + + Assert.IsNotNull(r.Data.Authentication); + Assert.AreEqual("6a4c1709-a42e-4c7f-96c7-1043adacfc97", r.Data.Authentication.AcsTransId); + Assert.AreEqual(AuthenticationInfo.ChallengeIndicatorEnum._01, r.Data.Authentication.ChallengeIndicator); + Assert.AreEqual(DateTime.Parse("2022-12-22T15:45:03+01:00"), r.Data.Authentication.CreatedAt); + Assert.AreEqual(AuthenticationInfo.DeviceChannelEnum.App, r.Data.Authentication.DeviceChannel); + Assert.AreEqual("a3b86754-444d-46ca-95a2-ada351d3f42c", r.Data.Authentication.DsTransID); + Assert.AreEqual(AuthenticationInfo.ExemptionIndicatorEnum.LowValue, r.Data.Authentication.ExemptionIndicator); + Assert.IsTrue(r.Data.Authentication.InPSD2Scope); + Assert.AreEqual(AuthenticationInfo.MessageCategoryEnum.Payment, r.Data.Authentication.MessageCategory); + Assert.AreEqual("2.2.0", r.Data.Authentication.MessageVersion); + Assert.AreEqual(0, r.Data.Authentication.RiskScore); + Assert.AreEqual("6edcc246-23ee-4e94-ac5d-8ae620bea7d9", r.Data.Authentication.ThreeDSServerTransID); + Assert.AreEqual(AuthenticationInfo.TransStatusEnum.Y, r.Data.Authentication.TransStatus); + Assert.AreEqual(AuthenticationInfo.TypeEnum.Challenge, r.Data.Authentication.Type); + + Assert.IsNotNull(r.Data.Authentication.Challenge); + Assert.AreEqual(ChallengeInfo.FlowEnum.OOBTRIGGERFL, r.Data.Authentication.Challenge.Flow); + Assert.AreEqual(DateTime.Parse("2022-12-22T15:49:03+01:00"), r.Data.Authentication.Challenge.LastInteraction); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/Adyen.Test.csproj b/Adyen.Test/Adyen.Test.csproj index ac0cc226d..0f160fee1 100644 --- a/Adyen.Test/Adyen.Test.csproj +++ b/Adyen.Test/Adyen.Test.csproj @@ -1,7 +1,7 @@  - net8.0;net6.0 + net8.0 12 enable disable diff --git a/Adyen.Test/BalanceControl/BalanceControlTest.cs b/Adyen.Test/BalanceControl/BalanceControlTest.cs new file mode 100644 index 000000000..a71c543fa --- /dev/null +++ b/Adyen.Test/BalanceControl/BalanceControlTest.cs @@ -0,0 +1,72 @@ +using Adyen.BalanceControl.Client; +using Adyen.BalanceControl.Extensions; +using Adyen.BalanceControl.Models; +using Adyen.BalanceControl.Services; +using Adyen.Core.Options; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Text.Json; + +namespace Adyen.Test.BalanceControl +{ + [TestClass] + public class BalanceControlTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public BalanceControlTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureBalanceControl((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_Deserialize_When_BalanceControlTransfer_Returns_Correct_Status() + { + // Arrange + var json = TestUtilities.GetTestFileContent("mocks/balance-control-transfer.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.CreatedAt, new DateTime(2022,01, 24)); + Assert.AreEqual(response.Status, BalanceTransferResponse.StatusEnum.Transferred); + } + + [TestMethod] + public async Task Given_IBalanceControlService_When_Live_Url_And_Prefix_Are_Set_Returns_Correct_Live_Url_Endpoint() + { + // Arrange + IHost liveHost = Host.CreateDefaultBuilder() + .ConfigureBalanceControl((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.AdyenApiKey = "your-live-api-key"; + options.Environment = AdyenEnvironment.Live; + options.LiveEndpointUrlPrefix = "prefix"; + }); + }) + .Build(); + + // Act + var balanceControlService = liveHost.Services.GetRequiredService(); + + // Assert + Assert.AreEqual("https://prefix-pal-live.adyenpayments.com/pal/servlet/BalanceControl/v1", balanceControlService.HttpClient.BaseAddress.ToString()); + } + + } +} \ No newline at end of file diff --git a/Adyen.Test/BalanceControlTest.cs b/Adyen.Test/BalanceControlTest.cs deleted file mode 100644 index 64d3e92dd..000000000 --- a/Adyen.Test/BalanceControlTest.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using Adyen.Model.BalanceControl; -using Adyen.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Adyen.Test -{ - [TestClass] - public class BalanceControlTest : BaseTest - { - [TestMethod] - public void BalanceTransferTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balance-control-transfer.json"); - var service = new BalanceControlService(client); - var response = service.BalanceTransfer(new BalanceTransferRequest()); - Assert.AreEqual(response.CreatedAt, new DateTime(2022,01, 24)); - Assert.AreEqual(response.Status, BalanceTransferResponse.StatusEnum.Transferred); - } - } -} \ No newline at end of file diff --git a/Adyen.Test/BalancePlatform/AccountHolders/AccountHolderTest.cs b/Adyen.Test/BalancePlatform/AccountHolders/AccountHolderTest.cs new file mode 100644 index 000000000..3e31dd17f --- /dev/null +++ b/Adyen.Test/BalancePlatform/AccountHolders/AccountHolderTest.cs @@ -0,0 +1,100 @@ +using System.Text.Json; +using Adyen.BalancePlatform.Client; +using Adyen.BalancePlatform.Extensions; +using Adyen.BalancePlatform.Models; +using Adyen.BalancePlatform.Services; +using Adyen.Core.Options; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Adyen.Test.BalancePlatform.AccountHolders +{ + [TestClass] + public class AccountHolderTest : BaseTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public AccountHolderTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureBalancePlatform((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.AdyenApiKey = context.Configuration["ADYEN_API_KEY"]; + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + + [TestMethod] + public async Task Given_AccountHolder_When_Unknown_Enum_Then_Result_Deserialize_To_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/balanceplatform/AccountHolderWithUnknownEnum.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.IsNotNull(response); + Assert.IsNull(response.Status); + } + + [TestMethod] + public async Task Given_AccountHolder_Deserialize_Correctly() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/balanceplatform/AccountHolder.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.Status, AccountHolder.StatusEnum.Active); + Assert.AreEqual(response.Id, "AH32272223222B5CM4MWJ892H"); + } + + [TestMethod] + public async Task Given_PaginatedBalanceAccountsResponse_Deserialize_Correctly() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/balanceplatform/PaginatedAccountHoldersResponse.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Arrange + Assert.AreEqual(response.AccountHolders[0].Id, "AH32272223222B5GFSNSXFFL9"); + Assert.AreEqual(response.AccountHolders[0].Status, AccountHolder.StatusEnum.Active); + } + + [TestMethod] + public async Task Given_IAccountHolderService_When_Live_Url_And_Prefix_Are_Set_Returns_Correct_Live_Url_Endpoint_And_No_Prefix() + { + // Arrange + IHost liveHost = Host.CreateDefaultBuilder() + .ConfigureBalancePlatform((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.AdyenApiKey = "your-live-api-key"; + options.Environment = AdyenEnvironment.Live; + options.LiveEndpointUrlPrefix = "prefix"; + }); + }) + .Build(); + + // Act + var accountHoldersService = liveHost.Services.GetRequiredService(); + + // Assert + Assert.AreEqual("https://balanceplatform-api-live.adyen.com/bcl/v2", accountHoldersService.HttpClient.BaseAddress.ToString()); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/BalancePlatform/BalanceAccounts/BalanceAccountTest.cs b/Adyen.Test/BalancePlatform/BalanceAccounts/BalanceAccountTest.cs new file mode 100644 index 000000000..2c24c10b5 --- /dev/null +++ b/Adyen.Test/BalancePlatform/BalanceAccounts/BalanceAccountTest.cs @@ -0,0 +1,108 @@ +using System.Text.Json; +using Adyen.BalancePlatform.Client; +using Adyen.BalancePlatform.Extensions; +using Adyen.BalancePlatform.Models; +using Adyen.BalancePlatform.Services; +using Adyen.Core.Options; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NSubstitute; + +namespace Adyen.Test.BalancePlatform.BalanceAccounts +{ + [TestClass] + public class BalanceAccountTest : BaseTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public BalanceAccountTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureBalancePlatform((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.AdyenApiKey = context.Configuration["ADYEN_API_KEY"]; + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_BalanceAccounts_Deserialize_Correctly() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/balanceplatform/BalanceAccount.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.Status, BalanceAccount.StatusEnum.Active); + Assert.AreEqual(response.Id, "BA3227C223222H5J4DCGQ9V9L"); + } + + [TestMethod] + public async Task Given_SweepConfigurationV2_Deserialize_Correctly() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/balanceplatform/SweepConfiguration.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.Status, SweepConfigurationV2.StatusEnum.Active); + Assert.AreEqual(response.Type, SweepConfigurationV2.TypeEnum.Pull); + Assert.AreEqual(response.Id, "SWPC4227C224555B5FTD2NT2JV4WN5"); + } + + [TestMethod] + public async Task Given_BalanceSweepConfigurationsResponse_Deserialize_Correctly() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/balanceplatform/BalanceSweepConfigurationsResponse.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.Sweeps[0].Status, SweepConfigurationV2.StatusEnum.Active); + Assert.AreEqual(response.Sweeps[0].Id, "SWPC4227C224555B5FTD2NT2JV4WN5"); + Assert.AreEqual(response.Sweeps[0].Schedule.Type, SweepSchedule.TypeEnum.Daily); + } + + [TestMethod] + public async Task Given_UpdateSweepConfigurationV2_Deserialize_Correctly() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/balanceplatform/SweepConfiguration.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.Status, SweepConfigurationV2.StatusEnum.Active); + Assert.AreEqual(response.Type, SweepConfigurationV2.TypeEnum.Pull); + Assert.AreEqual(response.Id, "SWPC4227C224555B5FTD2NT2JV4WN5"); + } + + [TestMethod] + public async Task Given_Deserialize_When_PaginatedPaymentInstrumentsResponse_Returns_Correct_Object() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/balanceplatform/PaginatedPaymentInstrumentsResponse.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.PaymentInstruments[0].Status, PaymentInstrument.StatusEnum.Active); + Assert.AreEqual(response.PaymentInstruments[0].Id, "PI32272223222B59M5TM658DT"); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/BalancePlatform/BalancePlatformTest.cs b/Adyen.Test/BalancePlatform/BalancePlatformTest.cs new file mode 100644 index 000000000..e3056971a --- /dev/null +++ b/Adyen.Test/BalancePlatform/BalancePlatformTest.cs @@ -0,0 +1,61 @@ +using System.Text.Json; +using Adyen.BalancePlatform.Client; +using Adyen.BalancePlatform.Extensions; +using Adyen.BalancePlatform.Models; +using Adyen.Core.Options; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Adyen.Test.BalancePlatform +{ + [TestClass] + public class BalancePlatformTest : BaseTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public BalancePlatformTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureBalancePlatform((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.AdyenApiKey = context.Configuration["ADYEN_API_KEY"]; + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_BalancePlatform_Deserialize_Correctly() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/balanceplatform/BalancePlatform.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.Status, "Active"); + Assert.AreEqual(response.Id, "YOUR_BALANCE_PLATFORM"); + } + + [TestMethod] + public async Task Given_PaginatedAccountHoldersResponse_Deserialize_Correctly() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/balanceplatform/PaginatedBalanceAccountsResponse.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("BA32272223222B5CTDNB66W2Z", response.BalanceAccounts[0].Id); + Assert.AreEqual(BalanceAccountBase.StatusEnum.Active, response.BalanceAccounts[1].Status); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/BalancePlatform/PaymentInstrumentGroups/PaymentInstrumentGroupTest.cs b/Adyen.Test/BalancePlatform/PaymentInstrumentGroups/PaymentInstrumentGroupTest.cs new file mode 100644 index 000000000..f7cc1f109 --- /dev/null +++ b/Adyen.Test/BalancePlatform/PaymentInstrumentGroups/PaymentInstrumentGroupTest.cs @@ -0,0 +1,63 @@ +using System.Text.Json; +using Adyen.BalancePlatform.Client; +using Adyen.BalancePlatform.Extensions; +using Adyen.BalancePlatform.Models; +using Adyen.BalancePlatform.Services; +using Adyen.Core.Options; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NSubstitute; + +namespace Adyen.Test.BalancePlatform.PaymentInstrumentGroups +{ + [TestClass] + public class PaymentInstrumentGroupTest : BaseTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public PaymentInstrumentGroupTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureBalancePlatform((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.AdyenApiKey = context.Configuration["ADYEN_API_KEY"]; + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_PaymentInstrumentGroup_Deserialize_Correctly() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/balanceplatform/PaymentInstrumentGroup.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.Id, "PG3227C223222B5CMD3FJFKGZ"); + Assert.AreEqual(response.BalancePlatform, "YOUR_BALANCE_PLATFORM"); + } + + [TestMethod] + public async Task Given_TransactionRulesResponse_Deserialize_Correctly() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/balanceplatform/TransactionRulesResponse.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Arrange + Assert.AreEqual(response.TransactionRules[0].Type, TransactionRule.TypeEnum.Velocity); + Assert.AreEqual(response.TransactionRules[0].Id, "TR3227C223222C5GXR3XP596N"); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/BalancePlatform/PaymentInstruments/PaymentInstrumentTest.cs b/Adyen.Test/BalancePlatform/PaymentInstruments/PaymentInstrumentTest.cs new file mode 100644 index 000000000..2e8ccd257 --- /dev/null +++ b/Adyen.Test/BalancePlatform/PaymentInstruments/PaymentInstrumentTest.cs @@ -0,0 +1,49 @@ +using System.Text.Json; +using Adyen.BalancePlatform.Client; +using Adyen.BalancePlatform.Extensions; +using Adyen.BalancePlatform.Models; +using Adyen.BalancePlatform.Services; +using Adyen.Core.Options; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NSubstitute; + +namespace Adyen.Test.BalancePlatform.PaymentInstruments +{ + [TestClass] + public class PaymentInstrumentTest : BaseTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public PaymentInstrumentTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureBalancePlatform((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.AdyenApiKey = context.Configuration["ADYEN_API_KEY"]; + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_PaymentInstrument_Deserialize_Correctly() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/balanceplatform/PaymentInstrument.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.BalanceAccountId, "BA3227C223222B5CTBLR8BWJB"); + Assert.AreEqual(response.Type, PaymentInstrument.TypeEnum.BankAccount); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/BalancePlatform/TransactionRules/TransactionRuleTest.cs b/Adyen.Test/BalancePlatform/TransactionRules/TransactionRuleTest.cs new file mode 100644 index 000000000..82b13cf91 --- /dev/null +++ b/Adyen.Test/BalancePlatform/TransactionRules/TransactionRuleTest.cs @@ -0,0 +1,112 @@ +using System.Text.Json; +using Adyen.BalancePlatform.Client; +using Adyen.BalancePlatform.Extensions; +using Adyen.BalancePlatform.Models; +using Adyen.Core.Options; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Adyen.Test.BalancePlatform.TransactionRules +{ + [TestClass] + public class TransactionRuleTest : BaseTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public TransactionRuleTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureBalancePlatform((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.AdyenApiKey = context.Configuration["ADYEN_API_KEY"]; + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_TransactionRule_Serialize_Correctly() + { + // Arrange + var target = new TransactionRule( + description: "Allow only point-of-sale transactions", + reference: "YOUR_REFERENCE_4F7346", + entityKey: new TransactionRuleEntityKey(entityType: "paymentInstrument", entityReference: "PI3227C223222B5BPCMFXD2XG"), + status: TransactionRule.StatusEnum.Active, + interval: new TransactionRuleInterval("perTransaction"), + ruleRestrictions: new TransactionRuleRestrictions( + processingTypes: new ProcessingTypesRestriction( + operation: "noneMatch", + value: new List + { + ProcessingTypesRestriction.ValueEnum.Pos, + ProcessingTypesRestriction.ValueEnum.Ecommerce + }) + ), + type: "blockList" + ); + + // Act + string result = JsonSerializer.Serialize(target, _jsonSerializerOptionsProvider.Options); + + // Assert + JsonDocument jsonDoc = JsonDocument.Parse(result); + JsonElement root = jsonDoc.RootElement; + + Assert.AreEqual("Allow only point-of-sale transactions", root.GetProperty("description").GetString()); + Assert.AreEqual("YOUR_REFERENCE_4F7346", root.GetProperty("reference").GetString()); + + JsonElement entityKey = root.GetProperty("entityKey"); + Assert.AreEqual("paymentInstrument", entityKey.GetProperty("entityType").GetString()); + Assert.AreEqual("PI3227C223222B5BPCMFXD2XG", entityKey.GetProperty("entityReference").GetString()); + Assert.AreEqual("active", root.GetProperty("status").GetString()); + + JsonElement interval = root.GetProperty("interval"); + Assert.AreEqual("perTransaction", interval.GetProperty("type").GetString()); + + JsonElement processingTypes = root.GetProperty("ruleRestrictions").GetProperty("processingTypes"); + Assert.AreEqual("noneMatch", processingTypes.GetProperty("operation").GetString()); + + JsonElement.ArrayEnumerator values = processingTypes.GetProperty("value").EnumerateArray(); + Assert.IsTrue(values.Any(v => v.GetString() == "pos") && values.Any(v => v.GetString() == "ecommerce")); + Assert.AreEqual("blockList", root.GetProperty("type").GetString()); + } + + [TestMethod] + public async Task Given_TransactionRule_Deserialize_Correctly() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/balanceplatform/TransactionRule.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.EntityKey.EntityReference, "PI3227C223222B5BPCMFXD2XG"); + Assert.AreEqual(response.EntityKey.EntityType, "paymentInstrument"); + Assert.AreEqual(response.Interval.Type, TransactionRuleInterval.TypeEnum.PerTransaction); + Assert.AreEqual(response.RuleRestrictions.ProcessingTypes.Operation, "noneMatch"); + } + + [TestMethod] + public async Task Given_TransactionRuleResponse_Deserialize_Correctly() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/balanceplatform/TransactionRuleResponse.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.TransactionRule.Id, "TR32272223222B5GFSGFLFCHM"); + Assert.AreEqual(response.TransactionRule.Interval.Type, TransactionRuleInterval.TypeEnum.PerTransaction); + Assert.AreEqual(response.TransactionRule.RuleRestrictions.ProcessingTypes.Operation, "noneMatch"); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/BalancePlatformTest.cs b/Adyen.Test/BalancePlatformTest.cs deleted file mode 100644 index 81e489e9f..000000000 --- a/Adyen.Test/BalancePlatformTest.cs +++ /dev/null @@ -1,428 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading; -using Adyen.Model.BalancePlatform; -using Adyen.Service.BalancePlatform; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using NSubstitute; - -namespace Adyen.Test -{ - [TestClass] - public class BalancePlatformTest : BaseTest - { - #region AccountHolders - - /// - /// Test GetAccountHoldersId - /// - [TestMethod] - public void GetAccountHoldersIdTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/AccountHolder.json"); - var service = new AccountHoldersService(client); - - var response = service.GetAccountHolder("AH32272223222B5CM4MWJ892H"); - Assert.AreEqual(response.Status, AccountHolder.StatusEnum.Active); - Assert.AreEqual(response.Id, "AH32272223222B5CM4MWJ892H"); - } - - /// - /// Test PostAccountHolders - /// - [TestMethod] - public void PostAccountHoldersTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/AccountHolder.json"); - var service = new AccountHoldersService(client); - var accountHolder = new AccountHolderInfo() - { - BalancePlatform = "balance" - }; - var response = service.CreateAccountHolder(accountHolder); - Assert.AreEqual(response.Status, AccountHolder.StatusEnum.Active); - Assert.AreEqual(response.Id, "AH32272223222B5CM4MWJ892H"); - } - - /// - /// Test PatchAccountHoldersId - /// - [TestMethod] - public void PatchAccountHoldersIdTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/AccountHolder.json"); - var service = new AccountHoldersService(client); - var accountHolder = new AccountHolderUpdateRequest() - { - BalancePlatform = "balance" - }; - var response = service.UpdateAccountHolder("AH32272223222B5CM4MWJ892H", accountHolder); - Assert.AreEqual(response.Status, AccountHolder.StatusEnum.Active); - Assert.AreEqual(response.Id, "AH32272223222B5CM4MWJ892H"); - } - - /// - /// Test GetAccountHoldersIdBalanceAccountsAsync - /// - [TestMethod] - public void GetAccountHoldersIdBalanceAccountsAsyncTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/PaginatedBalanceAccountsResponse.json"); - var service = new AccountHoldersService(client); - - var response = service.GetAllBalanceAccountsOfAccountHolderAsync("id", offset: 1, limit: 3).Result; - Assert.AreEqual("BA32272223222B59K6ZXHBFN6", response.BalanceAccounts[0].Id); - Assert.AreEqual(BalanceAccountBase.StatusEnum.Closed, response.BalanceAccounts[1].Status); - ClientInterfaceSubstitute.Received() - .RequestAsync( - "https://balanceplatform-api-test.adyen.com/bcl/v2/accountHolders/id/balanceAccounts?offset=1&limit=3", - null, null, HttpMethod.Get, default); - } - - #endregion - - #region BalanceAccounts - - /// - /// Test GetBalanceAccountsId - /// - [TestMethod] - public void GetBalanceAccountsIdTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/BalanceAccount.json"); - var service = new BalanceAccountsService(client); - - var response = service.GetBalanceAccount("AH32272223222B5CM4MWJ892H"); - Assert.AreEqual(response.Status, BalanceAccount.StatusEnum.Active); - Assert.AreEqual(response.Id, "BA3227C223222B5BLP6JQC3FD"); - } - - /// - /// Test GetBalanceAccountsId - /// - [TestMethod] - public void PostBalanceAccountsTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/BalanceAccount.json"); - var service = new BalanceAccountsService(client); - var balanceAccountInfo = new BalanceAccountInfo() - { - AccountHolderId = "accountHolderId" - }; - var response = service.CreateBalanceAccount(balanceAccountInfo); - Assert.AreEqual(response.Status, BalanceAccount.StatusEnum.Active); - Assert.AreEqual(response.Id, "BA3227C223222B5BLP6JQC3FD"); - } - - /// - /// Test PatchBalanceAccountsIdAsync - /// - [TestMethod] - public void PatchBalanceAccountsIdAsyncTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/BalanceAccount.json"); - var service = new BalanceAccountsService(client); - var response = service.UpdateBalanceAccountAsync("BA3227C223222B5BLP6JQC3FD", new BalanceAccountUpdateRequest()).Result; - Assert.AreEqual(response.Status, BalanceAccount.StatusEnum.Active); - Assert.AreEqual(response.Id, "BA3227C223222B5BLP6JQC3FD"); - } - - /// - /// Test PostBalanceAccountsBalanceAccountIdSweeps - /// - [TestMethod] - public void PostBalanceAccountsBalanceAccountIdSweepsTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/SweepConfiguration.json"); - var service = new BalanceAccountsService(client); - - var response = service.CreateSweep("1245yhgeswkrw", new CreateSweepConfigurationV2()); - Assert.AreEqual(response.Status, SweepConfigurationV2.StatusEnum.Active); - Assert.AreEqual(response.Type, SweepConfigurationV2.TypeEnum.Pull); - } - - /// - /// Test GetBalanceAccountsBalanceAccountIdSweeps - /// - [TestMethod] - public void GetBalanceAccountsBalanceAccountIdSweepsTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/BalanceSweepConfigurationsResponse.json"); - var service = new BalanceAccountsService(client); - - var response = service.GetAllSweepsForBalanceAccount("balanceAccountId"); - Assert.AreEqual(response.Sweeps[0].Status, SweepConfigurationV2.StatusEnum.Active); - Assert.AreEqual(response.Sweeps[0].Id, "SWPC4227C224555B5FTD2NT2JV4WN5"); - var schedule = response.Sweeps[0].Schedule.Type; - Assert.AreEqual(schedule, SweepSchedule.TypeEnum.Daily); - ClientInterfaceSubstitute.Received().RequestAsync( - "https://balanceplatform-api-test.adyen.com/bcl/v2/balanceAccounts/balanceAccountId/sweeps", - null, - null, HttpMethod.Get, new CancellationToken()); - } - - /// - /// Test PatchBalanceAccountsBalanceAccountIdSweepsSweepId - /// - [TestMethod] - public void PatchBalanceAccountsBalanceAccountIdSweepsSweepIdTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/SweepConfiguration.json"); - var service = new BalanceAccountsService(client); - - var response = service.UpdateSweep("balanceID", "sweepId",new UpdateSweepConfigurationV2()); - Assert.AreEqual(response.Status, SweepConfigurationV2.StatusEnum.Active); - Assert.AreEqual(response.Type, SweepConfigurationV2.TypeEnum.Pull); - } - - /// - /// Test DeleteBalanceAccountsBalanceAccountIdSweepsSweepId - /// - [TestMethod] - public void DeleteBalanceAccountsBalanceAccountIdSweepsSweepIdTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/SweepConfiguration.json"); - var service = new BalanceAccountsService(client); - service.DeleteSweep("balanceID", "sweepId"); - } - - /// - /// Test PatchBalanceAccountsBalanceAccountIdSweepsSweepId - /// - [TestMethod] - public void GetBalanceAccountsIdPaymentInstrumentsTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/PaginatedPaymentInstrumentsResponse.json"); - var service = new BalanceAccountsService(client); - - var response = service.GetPaymentInstrumentsLinkedToBalanceAccount("balanceID"); - Assert.AreEqual(response.PaymentInstruments[0].Status, PaymentInstrument.StatusEnum.Active); - Assert.AreEqual(response.PaymentInstruments[0].Id, "PI32272223222B59M5TM658DT"); - } - #endregion - - #region General - - /// - /// Test GetBalancePlatformsId - /// - [TestMethod] - public void GetBalancePlatformsIdTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/BalancePlatform.json"); - var service = new PlatformService(client); - - var response = service.GetBalancePlatform("uniqueIdentifier"); - Assert.AreEqual(response.Status, "Active"); - Assert.AreEqual(response.Id, "YOUR_BALANCE_PLATFORM"); - } - - /// - /// Test GetBalancePlatformsIdAccountHolders - /// - [TestMethod] - public void GetBalancePlatformsIdAccountHoldersTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/PaginatedAccountHoldersResponse.json"); - var service = new PlatformService(client); - - var response = service.GetAllAccountHoldersUnderBalancePlatform("uniqueIdentifier"); - Assert.AreEqual(response.AccountHolders[0].Id, "AH32272223222B59DDWSCCMP7"); - Assert.AreEqual(response.AccountHolders[0].Status, AccountHolder.StatusEnum.Active); - } - - #endregion - - #region PaymentInstrumentGroups - - /// - /// Test GetPaymentInstrumentGroupsId - /// - [TestMethod] - public void GetPaymentInstrumentGroupsIdTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/PaymentInstrumentGroup.json"); - var service = new PaymentInstrumentGroupsService(client); - - var response = service.GetPaymentInstrumentGroup("uniqueIdentifier"); - Assert.AreEqual(response.Id, "PG3227C223222B5CMD3FJFKGZ"); - Assert.AreEqual(response.BalancePlatform, "YOUR_BALANCE_PLATFORM"); - } - - /// - /// Test PostPaymentInstrumentGroups - /// - [TestMethod] - public void PostPaymentInstrumentGroupsTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/PaymentInstrumentGroup.json"); - var service = new PaymentInstrumentGroupsService(client); - - var response = service.CreatePaymentInstrumentGroup(new PaymentInstrumentGroupInfo()); - Assert.AreEqual(response.Id, "PG3227C223222B5CMD3FJFKGZ"); - Assert.AreEqual(response.BalancePlatform, "YOUR_BALANCE_PLATFORM"); - } - - /// - /// Test GetPaymentInstrumentGroupsIdTransactionRules - /// - [TestMethod] - public void GetPaymentInstrumentGroupsIdTransactionRulesTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/TransactionRulesResponse.json"); - var service = new PaymentInstrumentGroupsService(client); - - var response = service.GetAllTransactionRulesForPaymentInstrumentGroup("id"); - Assert.AreEqual(response.TransactionRules[0].Type, TransactionRule.TypeEnum.Velocity); - Assert.AreEqual(response.TransactionRules[0].Id, "TR32272223222B5CMDGMC9F4F"); - } - - #endregion - - #region PaymenInstruments - - /// - /// Test GetPaymentInstrumentGroupsId - /// - [TestMethod] - public void PostPaymentInstrumentsTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/PaymentInstrument.json"); - var service = new PaymentInstrumentsService(client); - - var response = service.CreatePaymentInstrument(new PaymentInstrumentInfo()); - Assert.AreEqual(response.BalanceAccountId, "BA3227C223222B5CTBLR8BWJB"); - Assert.AreEqual(response.Type, PaymentInstrument.TypeEnum.BankAccount); - } - - /// - /// Test PatchPaymentInstrumentsId - /// - [TestMethod] - public void PatchPaymentInstrumentsIdTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/PaymentInstrument.json"); - var service = new PaymentInstrumentsService(client); - - var response = service.UpdatePaymentInstrument("id", new PaymentInstrumentUpdateRequest()); - Assert.AreEqual(response.BalanceAccountId, "BA3227C223222B5CTBLR8BWJB"); - Assert.AreEqual(response.Type, UpdatePaymentInstrument.TypeEnum.BankAccount); - } - - /// - /// Test GetPaymentInstrumentsId - /// - [TestMethod] - public void GetPaymentInstrumentsIdTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/PaymentInstrument.json"); - var service = new PaymentInstrumentsService(client); - - var response = service.GetPaymentInstrument("id"); - Assert.AreEqual(response.BalanceAccountId, "BA3227C223222B5CTBLR8BWJB"); - Assert.AreEqual(response.Type, PaymentInstrument.TypeEnum.BankAccount); - } - - /// - /// Test GetPaymentInstrumentsIdTransactionRules - /// - [TestMethod] - public void GetPaymentInstrumentsIdTransactionRulesTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/TransactionRulesResponse.json"); - var service = new PaymentInstrumentsService(client); - - var response = service.GetAllTransactionRulesForPaymentInstrument("id"); - Assert.AreEqual(response.TransactionRules[0].Id, "TR32272223222B5CMDGMC9F4F"); - Assert.AreEqual(response.TransactionRules[0].Type, TransactionRule.TypeEnum.Velocity); - } - - #endregion - - #region TransactionRules - - /// - /// Test PostTransactionRules - /// - [TestMethod] - public void PostTransactionRulesTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/TransactionRule.json"); - var service = new TransactionRulesService(client); - - var response = service.CreateTransactionRule(new TransactionRuleInfo()); - Assert.AreEqual(response.EntityKey.EntityReference, "PI3227C223222B5BPCMFXD2XG"); - Assert.AreEqual(response.EntityKey.EntityType, "paymentInstrument"); - Assert.AreEqual(response.Interval.Type, TransactionRuleInterval.TypeEnum.PerTransaction); - } - - /// - /// Test PatchTransactionRulesTransactionRuleId - /// - [TestMethod] - public void PatchTransactionRulesTransactionRuleIdTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/TransactionRule.json"); - var service = new TransactionRulesService(client); - - var response = service.UpdateTransactionRule("transactionRuleId", new TransactionRuleInfo()); - Assert.AreEqual(response.EntityKey.EntityReference, "PI3227C223222B5BPCMFXD2XG"); - Assert.AreEqual(response.EntityKey.EntityType, "paymentInstrument"); - Assert.AreEqual(response.Interval.Type, TransactionRuleInterval.TypeEnum.PerTransaction); - } - - /// - /// Test GetTransactionRulesTransactionRuleId - /// - [TestMethod] - public void GetTransactionRulesTransactionRuleIdTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/TransactionRuleResponse.json"); - var service = new TransactionRulesService(client); - - var response = service.GetTransactionRule("transactionRuleId"); - Assert.AreEqual(response.TransactionRule.Id, "TR32272223222B5CMD3V73HXG"); - Assert.AreEqual(response.TransactionRule.Interval.Type, TransactionRuleInterval.TypeEnum.Monthly); - } - - /// - /// Test DeleteTransactionRulesTransactionRuleId - /// - [TestMethod] - public void DeleteTransactionRulesTransactionRuleIdTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/TransactionRuleResponse.json"); - var service = new TransactionRulesService(client); - var response = service.DeleteTransactionRule("transactionRuleId"); - - } - #endregion - - #region BankAccountValidation - /// - /// Test /validateBankAccountIdentification - /// - [TestMethod] - public void ValidateBankAccountTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/balanceplatform/TransactionRuleResponse.json"); - var service = new BankAccountValidationService(client); - var bankAccountIdentificationValidationRequest = new BankAccountIdentificationValidationRequest - { - AccountIdentification = new BankAccountIdentificationValidationRequestAccountIdentification( - new CZLocalAccountIdentification - { - AccountNumber = "123456789", - BankCode = "bankCode", - Type = CZLocalAccountIdentification.TypeEnum.CzLocal - }) - }; - service.ValidateBankAccountIdentification(bankAccountIdentificationValidationRequest); - ClientInterfaceSubstitute.Received().RequestAsync( - "https://balanceplatform-api-test.adyen.com/bcl/v2/validateBankAccountIdentification", - Arg.Any(), - null, HttpMethod.Post, new CancellationToken()); - } - #endregion - } -} \ No newline at end of file diff --git a/Adyen.Test/BinLookup/BinLookupTest.cs b/Adyen.Test/BinLookup/BinLookupTest.cs new file mode 100644 index 000000000..335b68430 --- /dev/null +++ b/Adyen.Test/BinLookup/BinLookupTest.cs @@ -0,0 +1,82 @@ +using System.Text.Json; +using Adyen.BinLookup.Client; +using Adyen.BinLookup.Extensions; +using Adyen.BinLookup.Models; +using Adyen.Core.Options; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Adyen.Test.BinLookup +{ + [TestClass] + public class BinLookupTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public BinLookupTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureBinLookup((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.AdyenApiKey = context.Configuration["ADYEN_API_KEY"]; + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_Deserialize_When_ThreeDSAvailabilityResponse_Result_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/binlookup/get3dsavailability-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("F013371337", response.DsPublicKeys[0].DirectoryServerId); + Assert.AreEqual("visa", response.DsPublicKeys[0].Brand); + Assert.AreEqual("411111111111", response.ThreeDS2CardRangeDetails[0].StartRange); + Assert.AreEqual("411111111111", response.ThreeDS2CardRangeDetails[0].EndRange); + Assert.AreEqual("2.1.0", response.ThreeDS2CardRangeDetails[0].ThreeDS2Versions.FirstOrDefault()); + Assert.AreEqual("https://pal-test.adyen.com/threeds2simulator/acs/startMethod.shtml", response.ThreeDS2CardRangeDetails[0].ThreeDSMethodURL); + Assert.IsTrue(response.ThreeDS1Supported); + Assert.IsTrue(response.ThreeDS2supported); + } + + [TestMethod] + public async Task Given_Deserialize_When_CostEstimateResponse_Result_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/binlookup/getcostestimate-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("1111", response.CardBin.Summary); + Assert.AreEqual("Unsupported", response.ResultCode); + } + + [TestMethod] + public async Task Given_Serialize_When_CostEstimateRequest_ShopperInteractionEnums_Result_Should_Return_Correct_String() + { + // Arrange + // Act + string ecommerce = JsonSerializer.Serialize(CostEstimateRequest.ShopperInteractionEnum.Ecommerce, _jsonSerializerOptionsProvider.Options); + string contAuth = JsonSerializer.Serialize(CostEstimateRequest.ShopperInteractionEnum.ContAuth, _jsonSerializerOptionsProvider.Options); + string moto = JsonSerializer.Serialize(CostEstimateRequest.ShopperInteractionEnum.Moto, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(@"""Ecommerce""", ecommerce); + Assert.AreEqual(@"""ContAuth""", contAuth); + Assert.AreEqual(@"""Moto""", moto); + } + } +} diff --git a/Adyen.Test/BinLookupTest.cs b/Adyen.Test/BinLookupTest.cs deleted file mode 100644 index 59d8e4bc8..000000000 --- a/Adyen.Test/BinLookupTest.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Linq; -using Adyen.Model.BinLookup; -using Adyen.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Adyen.Test -{ - [TestClass] - public class BinLookupTest : BaseTest - { - [TestMethod] - public void Get3dsAvailabilitySuccessMockedTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/binlookup/get3dsavailability-success.json"); - var binLookup = new BinLookupService(client); - var threeDsAvailabilityRequest = new ThreeDSAvailabilityRequest - { - MerchantAccount = "merchantAccount", - CardNumber = "4111111111111111" - }; - var threeDsAvailabilityResponse = binLookup.Get3dsAvailability(threeDsAvailabilityRequest); - Assert.AreEqual("F013371337", threeDsAvailabilityResponse.DsPublicKeys[0].DirectoryServerId); - Assert.AreEqual("visa", threeDsAvailabilityResponse.DsPublicKeys[0].Brand); - Assert.AreEqual("411111111111", threeDsAvailabilityResponse.ThreeDS2CardRangeDetails[0].StartRange); - Assert.AreEqual("411111111111", threeDsAvailabilityResponse.ThreeDS2CardRangeDetails[0].EndRange); - Assert.AreEqual("2.1.0", threeDsAvailabilityResponse.ThreeDS2CardRangeDetails[0].ThreeDS2Versions.FirstOrDefault()); - Assert.AreEqual("https://pal-test.adyen.com/threeds2simulator/acs/startMethod.shtml", threeDsAvailabilityResponse.ThreeDS2CardRangeDetails[0].ThreeDSMethodURL); - Assert.AreEqual(true, threeDsAvailabilityResponse.ThreeDS1Supported); - Assert.AreEqual(true, threeDsAvailabilityResponse.ThreeDS2supported); - } - - [TestMethod] - public void GetCostEstimateSuccessMockedTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/binlookup/getcostestimate-success.json"); - var binLookup = new BinLookupService(client); - var costEstimateRequest = new CostEstimateRequest(); - var amount = new Amount - { - Currency = "EUR", - Value = 1000 - }; - costEstimateRequest.Amount = amount; - var costEstimateAssumptions = new CostEstimateAssumptions - { - AssumeLevel3Data = true, - Assume3DSecureAuthenticated = true - }; - costEstimateRequest.Assumptions = costEstimateAssumptions; - costEstimateRequest.CardNumber = "4111111111111111"; - costEstimateRequest.MerchantAccount = "merchantAccount"; - var merchantDetails = new MerchantDetails - { - CountryCode = "NL", - Mcc = "7411", - EnrolledIn3DSecure = true - }; - costEstimateRequest.MerchantDetails = (merchantDetails); - costEstimateRequest.ShopperInteraction = CostEstimateRequest.ShopperInteractionEnum.Ecommerce; - var costEstimateResponse = binLookup.GetCostEstimate(costEstimateRequest); - Assert.AreEqual("1111", costEstimateResponse.CardBin.Summary); - Assert.AreEqual("Unsupported", costEstimateResponse.ResultCode); - } - - [TestMethod] - public void GetCostEstimateSuccessGenerateShopperInteractionFromEnum() - { - var ecommerce = Util.JsonOperation.SerializeRequest(CostEstimateRequest.ShopperInteractionEnum.Ecommerce); - var contAuth = Util.JsonOperation.SerializeRequest(CostEstimateRequest.ShopperInteractionEnum.ContAuth); - var moto = Util.JsonOperation.SerializeRequest(CostEstimateRequest.ShopperInteractionEnum.Moto); - Assert.AreEqual("\"Ecommerce\"", ecommerce); - Assert.AreEqual("\"ContAuth\"", contAuth); - Assert.AreEqual("\"Moto\"", moto); - } - } -} diff --git a/Adyen.Test/Checkout/CheckoutTest.cs b/Adyen.Test/Checkout/CheckoutTest.cs new file mode 100644 index 000000000..2a7a97694 --- /dev/null +++ b/Adyen.Test/Checkout/CheckoutTest.cs @@ -0,0 +1,425 @@ +using Adyen.Core.Options; +using Adyen.Checkout.Extensions; +using Adyen.Checkout.Models; +using Adyen.Checkout.Client; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.Extensions.Hosting; +using System.Text.Json; + +namespace Adyen.Test.Checkout +{ + [TestClass] + public class CheckoutTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public CheckoutTest() + { + IHost testHost = Host.CreateDefaultBuilder() + .ConfigureCheckout((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = testHost.Services.GetRequiredService(); + } + + [TestMethod] + public void Given_Deserialize_When_Payment_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/payments-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("8535296650153317", response.PspReference); + Assert.AreEqual(PaymentResponse.ResultCodeEnum.Authorised, response.ResultCode); + Assert.IsNotNull(response.AdditionalData); + Assert.AreEqual(9, response.AdditionalData.Count); + Assert.AreEqual("8/2018", response.AdditionalData["expiryDate"]); + Assert.AreEqual("GREEN", response.AdditionalData["fraudResultType"]); + Assert.AreEqual("411111", response.AdditionalData["cardBin"]); + Assert.AreEqual("1111", response.AdditionalData["cardSummary"]); + Assert.AreEqual("false", response.AdditionalData["fraudManualReview"]); + Assert.AreEqual("Default", response.AdditionalData["aliasType"]); + Assert.AreEqual("H167852639363479", response.AdditionalData["alias"]); + Assert.AreEqual("visa", response.AdditionalData["cardPaymentMethod"]); + Assert.AreEqual("NL", response.AdditionalData["cardIssuingCountry"]); + } + + [TestMethod] + public void Given_Deserialize_When_Payment3DS2_CheckoutThreeDS2Action_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/payments-3DS2-IdentifyShopper.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(PaymentResponse.ResultCodeEnum.IdentifyShopper, response.ResultCode); + Assert.AreEqual("threeDS2", response.Action.CheckoutThreeDS2Action.Type.ToString()); + Assert.IsNotNull(response.Action.CheckoutThreeDS2Action.PaymentData); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentError_Result_ServiceError_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/payments-error-invalid-data-422.json"); + + // Act + var serviceErrorResponse = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.IsNull(serviceErrorResponse.PspReference); + Assert.AreEqual("Reference Missing", serviceErrorResponse.Message); + Assert.AreEqual("validation", serviceErrorResponse.ErrorType); + Assert.AreEqual("130", serviceErrorResponse.ErrorCode); + Assert.AreEqual(422, serviceErrorResponse.Status); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentDetailsResponse_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/paymentsdetails-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("8515232733321252", response.PspReference); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentDetailsActionResponse_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/paymentsdetails-action-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("8515232733321252", response.PspReference); + Assert.AreEqual(PaymentDetailsResponse.ResultCodeEnum.Authorised, response.ResultCode); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentMethods_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/paymentmethods-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.PaymentMethods.Count, 32); + Assert.IsNotNull(response.PaymentMethods[12].Issuers); + Assert.AreEqual(response.PaymentMethods[12].Issuers[0].Id, "66"); + Assert.AreEqual(response.PaymentMethods[12].Issuers[0].Name, "Bank Nowy BFG S.A."); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentMethods__Result_Brands_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/paymentmethods-brands-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(7, response.PaymentMethods.Count); + Assert.AreEqual(5, response.PaymentMethods[0].Brands.Count); + Assert.AreEqual("visa", response.PaymentMethods[0].Brands[0]); + Assert.AreEqual("mc", response.PaymentMethods[0].Brands[1]); + Assert.AreEqual("amex", response.PaymentMethods[0].Brands[2]); + Assert.AreEqual("bcmc", response.PaymentMethods[0].Brands[3]); + Assert.AreEqual("maestro", response.PaymentMethods[0].Brands[4]); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentMethods_Result_Without_Brands_Is_Exactly_50() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/paymentmethods-without-brands-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.PaymentMethods.Count, 50); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentLinks_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/payment-links-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("your-id", response.Id); + Assert.AreEqual(PaymentLinkResponse.StatusEnum.Active, response.Status); + Assert.AreEqual("https://checkoutshopper-test.adyen.com/checkoutshopper/payByLink.shtml?d=YW1vdW50TWlub3JW...JRA", response.Url); + Assert.AreEqual(new DateTimeOffset(2019, 12, 17, 10, 05, 29, TimeSpan.Zero), response.ExpiresAt); + Assert.AreEqual("YOUR_ORDER_NUMBER", response.Reference); + Assert.AreEqual(1250, response.Amount.Value); + Assert.AreEqual("BRL", response.Amount.Currency); + } + + [TestMethod] + public void Given_Deserialize_When_Payments_PayPal_Result_CheckoutSDKAction_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/payments-success-paypal.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.IsNotNull(response.Action.CheckoutSDKAction); + Assert.AreEqual("EC-42N19135GM6949000", response.Action.CheckoutSDKAction.SdkData["orderID"]); + Assert.AreEqual("Ab02b4c0!BQABAgARb1TvUJa4nwS0Z1nOmxoYfD9+z...", response.Action.CheckoutSDKAction.PaymentData); + Assert.AreEqual("paypal", response.Action.CheckoutSDKAction.PaymentMethodType); + } + + [TestMethod] + public void Given_Deserialize_When_ApplePayDetails_Result_Is_Not_Null() + { + // Arrange + string json = "{\"type\": \"applepay\",\"applePayToken\": \"VNRWtuNlNEWkRCSm1xWndjMDFFbktkQU...\"}"; + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual( ApplePayDetails.TypeEnum.Applepay, response.Type); + Assert.AreEqual("VNRWtuNlNEWkRCSm1xWndjMDFFbktkQU...", response.ApplePayToken); + } + + [TestMethod] + public void Given_Deserialize_When_ApplePayDetails_With_Unknown_Value_Result_Is_Not_Null() + { + // Arrange + string json = "{\"type\": \"applepay\",\"someValue\": \"notInSpec\",\"applePayToken\": \"VNRWtuNlNEWkRCSm1xWndjMDFFbktkQU...\"}"; + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(ApplePayDetails.TypeEnum.Applepay, response.Type); + Assert.AreEqual("VNRWtuNlNEWkRCSm1xWndjMDFFbktkQU...", response.ApplePayToken); + } + + [TestMethod] + public void Given_Deserialize_When_PayWithGoogleDetails_Returns_Not_Null() + { + // Arrange + string json = "{\"type\": \"paywithgoogle\",\"someValue\": \"notInSpec\",\"googlePayToken\": \"==Payload as retrieved from Google Pay response==\"}"; + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(PayWithGoogleDetails.TypeEnum.Paywithgoogle, response.Type); + Assert.AreEqual(response.GooglePayToken, "==Payload as retrieved from Google Pay response=="); + } + + [TestMethod] + public void Given_Deserialize_When_BlikCode_Result_Is_Not_Null() + { + // Arrange + string json = "{\"type\":\"blik\",\"blikCode\":\"blikCode\"}"; + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(BlikDetails.TypeEnum.Blik, response.Type); + Assert.AreEqual("blikCode", response.BlikCode); + } + + [TestMethod] + public void Given_Deserialize_When_DragonpayDetails_Result_Is_Not_Null() + { + // Arrange + string json = "{\"issuer\":\"issuer\",\"shopperEmail\":\"test@adyen.com\",\"type\":\"dragonpay_ebanking\"}"; + + // Act + var result = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(DragonpayDetails.TypeEnum.DragonpayEbanking, result.Type); + Assert.AreEqual("issuer", result.Issuer); + Assert.AreEqual("test@adyen.com", result.ShopperEmail); + } + + [TestMethod] + public void Given_Deserialize_When_AfterPayDetails_Result_Is_Not_Null() + { + // Arrange + string json = @" +{ + ""resultCode"":""RedirectShopper"", + ""action"":{ + ""paymentMethodType"":""afterpaytouch"", + ""method"":""GET"", + ""url"":""https://checkoutshopper-test.adyen.com/checkoutshopper/checkoutPaymentRedirect?redirectData=..."", + ""type"":""redirect"" + } +}"; + // Act + var result = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(PaymentResponse.ResultCodeEnum.RedirectShopper, result.ResultCode); + Assert.AreEqual("afterpaytouch", result.Action.CheckoutRedirectAction.PaymentMethodType); + Assert.AreEqual(CheckoutRedirectAction.TypeEnum.Redirect, result.Action.CheckoutRedirectAction.Type); + Assert.AreEqual("https://checkoutshopper-test.adyen.com/checkoutshopper/checkoutPaymentRedirect?redirectData=...", result.Action.CheckoutRedirectAction.Url); + Assert.AreEqual("GET", result.Action.CheckoutRedirectAction.Method); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentResponse_3DS_ChallengeShopper_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/paymentResponse-3DS-ChallengeShopper.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(CheckoutThreeDS2Action.TypeEnum.ThreeDS2, response.Action.CheckoutThreeDS2Action.Type); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentMethodsResponse_StoredPaymentMethods_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/paymentmethods-storedpaymentmethods.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(4, response.StoredPaymentMethods.Count); + Assert.AreEqual("NL32ABNA0515071439", response.StoredPaymentMethods[0].Iban); + Assert.AreEqual("Adyen", response.StoredPaymentMethods[0].OwnerName); + Assert.AreEqual("sepadirectdebit", response.StoredPaymentMethods[0].Type); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentResponse_3DS2_IdentifyShopper_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/paymentResponse-3DS2-Action.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(PaymentResponse.ResultCodeEnum.IdentifyShopper, response.ResultCode); + Assert.AreEqual(CheckoutThreeDS2Action.TypeEnum.ThreeDS2, response.Action.CheckoutThreeDS2Action.Type); + } + + [TestMethod] + public void Given_Serialize_When_CheckoutSessionRequest_Result_Contains_DateOnly_And_DateTimeOffset() + { + // Arrange + CreateCheckoutSessionRequest checkoutSessionRequest = new CreateCheckoutSessionRequest( + merchantAccount: "TestMerchant", + reference: "TestReference", + returnUrl: "http://test-url.com", + amount: new Amount("EUR", 10000L), + dateOfBirth: new DateOnly(1998, 1, 1), + expiresAt: new DateTimeOffset(2023, 4, 1, 1, 1, 1, TimeSpan.Zero) + ); + + // Act + string target = JsonSerializer.Serialize(checkoutSessionRequest); + + // Assert + Assert.IsTrue(target.Contains("1998-01-01")); + Assert.IsTrue(target.Contains("2023-04-01T01:01:01+00:00")); + + Assert.IsTrue(target.Contains("TestMerchant")); + Assert.IsTrue(target.Contains("TestReference")); + Assert.IsTrue(target.Contains("http://test-url.com")); + Assert.IsTrue(target.Contains("EUR")); + Assert.IsTrue(target.Contains("10000")); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentMethodsBalance_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/paymentmethods-balance-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(BalanceCheckResponse.ResultCodeEnum.Success, response.ResultCode); + Assert.AreEqual("EUR", response.Balance.Currency); + Assert.AreEqual("2500", response.Balance.Value.ToString()); + } + + [TestMethod] + public void Given_Deserialize_When_CreateOrderResponse_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/orders-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(CreateOrderResponse.ResultCodeEnum.Success, response.ResultCode); + Assert.AreEqual("8515930288670953", response.PspReference); + Assert.AreEqual("Ab02b4c0!BQABAgBqxSuFhuXUF7IvIRvSw5bDPHN...", response.OrderData); + Assert.AreEqual("EUR", response.RemainingAmount.Currency); + Assert.AreEqual("2500", response.RemainingAmount.Value.ToString()); + } + + [TestMethod] + public void Given_Deserialize_When_CancelOrderResponse_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/orders-cancel-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("Received", response.ResultCode.ToString()); + Assert.AreEqual("8515931182066678", response.PspReference); + } + + [TestMethod] + public void Given_Deserialize_When_GetStoredPaymentMethods_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/get-storedPaymentMethod-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("string", response.StoredPaymentMethods[0].Type); + Assert.AreEqual("merchantAccount", response.MerchantAccount); + } + } +} diff --git a/Adyen.Test/Checkout/DonationsTest.cs b/Adyen.Test/Checkout/DonationsTest.cs new file mode 100644 index 000000000..41cc3398f --- /dev/null +++ b/Adyen.Test/Checkout/DonationsTest.cs @@ -0,0 +1,46 @@ +using Adyen.Core.Options; +using Adyen.Checkout.Extensions; +using Adyen.Checkout.Models; +using Adyen.Checkout.Client; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.Extensions.Hosting; +using System.Text.Json; + +namespace Adyen.Test.Checkout +{ + [TestClass] + public class DonationsTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public DonationsTest() + { + IHost testHost = Host.CreateDefaultBuilder() + .ConfigureCheckout((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = testHost.Services.GetRequiredService(); + } + + [TestMethod] + public void Given_Deserialize_When_Donations_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/donations-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(DonationPaymentResponse.StatusEnum.Completed, response.Status); + Assert.AreEqual("10720de4-7c5d-4a17-9161-fa4abdcaa5c4", response.Reference); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/Checkout/ModificationTest.cs b/Adyen.Test/Checkout/ModificationTest.cs new file mode 100644 index 000000000..15b30d7de --- /dev/null +++ b/Adyen.Test/Checkout/ModificationTest.cs @@ -0,0 +1,116 @@ +using Adyen.Core.Options; +using Adyen.Checkout.Extensions; +using Adyen.Checkout.Models; +using Adyen.Checkout.Client; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.Extensions.Hosting; +using System.Text.Json; + +namespace Adyen.Test.Checkout +{ + [TestClass] + public class ModificationTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public ModificationTest() + { + IHost testHost = Host.CreateDefaultBuilder() + .ConfigureCheckout((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = testHost.Services.GetRequiredService(); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentCaptureResponse_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/captures-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(PaymentCaptureResponse.StatusEnum.Received, response.Status); + Assert.AreEqual("my_reference", response.Reference); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentCancelResponse_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/cancels-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(PaymentCancelResponse.StatusEnum.Received, response.Status); + Assert.AreEqual("my_reference", response.Reference); + } + + [TestMethod] + public void Given_Deserialize_When_StandalonePaymentCancelResponse_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/standalone-cancels-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(StandalonePaymentCancelResponse.StatusEnum.Received, response.Status); + Assert.AreEqual("861633338418518C", response.PspReference); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentRefundResponse_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/refunds-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(PaymentRefundResponse.StatusEnum.Received, response.Status); + Assert.AreEqual("my_reference", response.Reference); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentReversalResponse_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/reversals-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(PaymentReversalResponse.StatusEnum.Received, response.Status); + Assert.AreEqual("my_reference", response.Reference); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentAmountUpdateResponse_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/reversals-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(PaymentAmountUpdateResponse.StatusEnum.Received, response.Status); + Assert.AreEqual("my_reference", response.Reference); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/Checkout/PaymentsTest.cs b/Adyen.Test/Checkout/PaymentsTest.cs new file mode 100644 index 000000000..1ce3d327b --- /dev/null +++ b/Adyen.Test/Checkout/PaymentsTest.cs @@ -0,0 +1,510 @@ +using Adyen.Core.Options; +using Adyen.Checkout.Extensions; +using Adyen.Checkout.Models; +using Adyen.Checkout.Services; +using Adyen.Checkout.Client; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.Extensions.Hosting; +using System.Text; +using System.Text.Json; + +namespace Adyen.Test.Checkout +{ + [TestClass] + public class PaymentsTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public PaymentsTest() + { + IHost testHost = Host.CreateDefaultBuilder() + .ConfigureCheckout((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = testHost.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_PaymentMethodsResponse_When_Deserialized_Then_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/payment-methods-response.json"); + // Act + PaymentMethodsResponse result = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.IsNotNull(result.PaymentMethods); + Assert.IsNull(result.StoredPaymentMethods); + Assert.AreEqual(29, result.PaymentMethods.Count); + } + + [TestMethod] + public async Task Given_CreateCheckoutSessionRequest_When_Serialize_Long__Result_Contains_Zeros() + { + // Arrange + CreateCheckoutSessionRequest checkoutSessionRequest = new CreateCheckoutSessionRequest( + amount: new Amount("EUR", 10000L), + merchantAccount: "merchantAccount", + reference: "TestReference", + returnUrl: "http://test-url.com", + channel: CreateCheckoutSessionRequest.ChannelEnum.Web, + countryCode: "NL", + lineItems: new List() { + new LineItem(quantity: 1, amountIncludingTax: 5000, description: "description1", amountExcludingTax: 0), + new LineItem(quantity: 1, amountIncludingTax: 5000, description: "description2", taxAmount: 0) + } + ); + + // Act + string result = JsonSerializer.Serialize(checkoutSessionRequest, _jsonSerializerOptionsProvider.Options); + + // Assert + using JsonDocument json = JsonDocument.Parse(result); + JsonElement lineItems = json.RootElement.GetProperty("lineItems"); + + lineItems[0].TryGetProperty("amountExcludingTax", out JsonElement amountExcludingTax); + lineItems[1].TryGetProperty("taxAmount", out JsonElement taxAmount); + + Assert.AreEqual(0, amountExcludingTax.GetInt32()); + Assert.AreEqual(0, taxAmount.GetInt32()); + } + + [TestMethod] + public async Task Given_CreateCheckoutSessionResponse_When_Deserialize_Returns_Not_Null() + { + // Arrange + string json = @"{""mode"": ""embedded"",""amount"": {""currency"": ""EUR"",""value"": 10000},""expiresAt"": ""2023-04-06T17:11:15+02:00"",""id"": ""CS0068299CB8DA273A"",""merchantAccount"": ""TestMerchantAccount"",""reference"": ""TestReference"",""returnUrl"": ""http://test-url.com"",""sessionData"": ""Ab02b4c0!BQABAgBoacRJuRbNM/typUKATopkZ3V+cINm0WTAvwl9uQJ2e8I00P2KFemlwp4nb1bOqqYh1zx48gDAHt0QDs2JTiQIqDQRizqopLFRk/wAJHFoCuam/GvOHflg9vwS3caHbkBToIolxcYcJoJheIN9o1fGmNIHZb9VrWfiKsXMgmYsSUifenayS2tkKCTquF7vguaAwk7q5ES1GDwzP/J2mChJGH04jGrVL4szPHGmznr897wIcFQyBSZe4Uifqoe0fpiIxZLNWadLMya6SnvQYNAQL1V6ti+7F4wHHyLwHWTCua993sttue70TE4918EV80HcAWx0LE1+uW6J5KBHCKdYNi9ESlGSZreRwLCpdNbmumB6MlyHZlz2umLiw0ZZJAEpdrwXA2NiyHzJDKDKfbAhd8uoTSACrbgwbrAXI1cvb1WjiOQjLn9MVW++fuJCq6vIeX+rUKMeltBAHMeBZyC54ndAucw9nS03uyckjphunE4tl4WTT475VkfUiyJCTT3S2IOVYS9V9M8pMQ1/GlDVNCLBJJfrYlVXoC8VX68QW1HERkhNYQbTgLQ9kI3cDeMP5d4TuUL3XR2Q6sNiOMdIPGDYQ9QFKFdOE3OQ5X9wxUYbX6j88wR2gRGJcS5agqFHOR1ZTNrjumOYrEkjL8ehFXEs/KluhURllfi0POUPGAwlUWBjDCZcKaeeR0kASnsia2V5IjoiQUYwQUFBMTAzQ0E1MzdFQUVEODdDMjRERDUzOTA5QjgwQTc4QTkyM0UzODIzRDY4REFDQzk0QjlGRjgzMDVEQyJ9E0Gs1T0RaO7NluuXP59fegcW6SQKq4BhC3ZzEKPm1vJuwAJ2gZUaicaXbRPW3IyadavKRlfGdFeAYt2B3ik8fGiK3ZkKU0CrZ0qL0IO5rrWrOg74HMnpxRAn1RhAHRHfGEk67FFPNjr0aLBJXSia7xZWiyKA+i4QU63roY2sxMI/q41mvJjRUz0rPKT3SbVDt1Of7K7BP8cmiboBkWexFBD/mkJyMOpYAOoFp/tKOUHTWZYcc1GpbyV1jInXVfEUhHROYCtS4p/xwF6DdT3bI0+HRU35/xNBTZH2yJN55u9i42Vb0ddCyhzOLZYQ3JVgglty6hLgVeQzuN4b2MoalhfTl11HsElJT1kB0mznVX8CL7UMTUp/2uSgL8DN6kB4owEZ45nWRxIR/2sCidMJ1tnSI1uSqkeqXRf1vat5qPq+BzpYE0Urn2ZZEmgJyb2u0ZLn2x1tfJyPtv+hqBoT7iqJ224dSbuB4HMT49YtcheUtR0jnrImJXm+M1TeIjWB3XWOScrNV8sWEJMAiIU="",""shopperLocale"": ""en-US""}"; + + // Act + CreateCheckoutSessionResponse response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.IsNotNull(response.Id); + } + + [TestMethod] + public async Task Given_Serialize_When_DeviceRenderOptions_Returns_MultiSelect_And_OtherHtml() + { + // Arrange + DeviceRenderOptions deviceRenderOptions = new DeviceRenderOptions + { + SdkInterface = DeviceRenderOptions.SdkInterfaceEnum.Native, + SdkUiType = new List() + { + DeviceRenderOptions.SdkUiTypeEnum.MultiSelect, + DeviceRenderOptions.SdkUiTypeEnum.OtherHtml + } + }; + + // Act + string result = JsonSerializer.Serialize(deviceRenderOptions, _jsonSerializerOptionsProvider.Options); + + // Assert + using JsonDocument json = JsonDocument.Parse(result); + JsonElement root = json.RootElement; + + JsonElement sdkInterface = root.GetProperty("sdkInterface"); + Assert.AreEqual("native", sdkInterface.GetString()); + + JsonElement sdkUiTypes = root.GetProperty("sdkUiType"); + Assert.AreEqual("multiSelect", sdkUiTypes[0].GetString()); + Assert.AreEqual("otherHtml", sdkUiTypes[1].GetString()); + } + + [TestMethod] + public async Task Given_Byte_Array_When_Serialize_Returns_Not_Null() + { + // Arrange + byte[] plainTextBytes = Encoding.ASCII.GetBytes("Bytes-To-Be-Encoded"); + string base64String = System.Convert.ToBase64String(plainTextBytes); + byte[] base64Bytes = Encoding.ASCII.GetBytes(base64String); + var threeDSecure = new ThreeDSecureData + { + Cavv = base64Bytes, + Xid = base64Bytes + }; + + // Act + string jsonRequest = JsonSerializer.Serialize(threeDSecure, _jsonSerializerOptionsProvider.Options); + + // Assert + string json = "{\"cavv\":\"Qnl0ZXMtVG8tQmUtRW5jb2RlZA==\",\"xid\":\"Qnl0ZXMtVG8tQmUtRW5jb2RlZA==\"}"; + Assert.AreEqual(json, jsonRequest); + } + + [TestMethod] + public async Task Given_Byte_Array_When_Deserialize_Result_ThreeDSecureData_Should_Deserialize_Correctly() + { + // Arrange + string json = "{\"cavv\":\"Qnl0ZXMtVG8tQmUtRW5jb2RlZA==\",\"xid\":\"Qnl0ZXMtVG8tQmUtRW5jb2RlZA==\"}"; + + // Act + ThreeDSecureData jsonRequest = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + string xid = Encoding.ASCII.GetString(jsonRequest.Xid); + string cavv = Encoding.ASCII.GetString(jsonRequest.Cavv); + string jsonElementBase64 = "Qnl0ZXMtVG8tQmUtRW5jb2RlZA=="; + Assert.AreEqual(jsonElementBase64, xid); + Assert.AreEqual(jsonElementBase64, cavv); + } + + [TestMethod] + public async Task Given_ConfigureCheckout_When_Timeout_Is_Provided_Then_Timeout_Is_Set() + { + // Arrange + IHost testHost = Host.CreateDefaultBuilder() + .ConfigureCheckout((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }, client => + { + client.Timeout = TimeSpan.FromSeconds(42); + }) + .Build(); + + // Act + var httpClient = testHost.Services.GetService().HttpClient; + + // Assert + Assert.AreEqual(httpClient.Timeout, TimeSpan.FromSeconds(42)); + } + + [TestMethod] + public async Task Given_Empty_ConfigureCheckout_When_No_AdyenOptions_Provided_Then_IPaymentService_Should_Throw_InvalidOperationException() + { + // Arrange + IHost testHost = Host.CreateDefaultBuilder() + .ConfigureCheckout((context, services, config) => + { + // Empty + }) + .Build(); + + // Act + // Assert + Assert.Throws(() => + { + // No ApiKey provided, cannot instantiate the ApiKeyToken object + testHost.Services.GetRequiredService(); + }); + } + + [TestMethod] + public async Task Given_IPaymentsService_When_Live_Url_And_Prefix_Are_Set_Returns_Correct_Live_Url_Endpoint_And_No_Prefix() + { + // Arrange + IHost liveHost = Host.CreateDefaultBuilder() + .ConfigureCheckout((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.AdyenApiKey = "your-live-api-key"; + options.Environment = AdyenEnvironment.Live; + options.LiveEndpointUrlPrefix = "prefix"; + }); + }) + .Build(); + + // Act + var paymentsService = liveHost.Services.GetRequiredService(); + + // Assert + Assert.AreEqual("https://prefix-checkout-live.adyenpayments.com/checkout/v71", paymentsService.HttpClient.BaseAddress.ToString()); + } + + [TestMethod] + public async Task Given_IPaymentsService_When_Test_Url_Returns_Correct_Test_Url_Endpoint_And_No_Prefix() + { + // Arrange + IHost liveHost = Host.CreateDefaultBuilder() + .ConfigureCheckout((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + // Act + var paymentsService = liveHost.Services.GetRequiredService(); + + // Assert + Assert.AreEqual("https://checkout-test.adyen.com/v71", paymentsService.HttpClient.BaseAddress.ToString()); + } + + [TestMethod] + public async Task Given_IPaymentsService_When_Live_Url_And_Prefix_Not_Set_Throws_InvalidOperationException() + { + // Arrange + IHost liveHost = Host.CreateDefaultBuilder() + .ConfigureCheckout((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.AdyenApiKey = "your-live-api-key"; + options.Environment = AdyenEnvironment.Live; + }); + }) + .Build(); + + // Act + // Assert + Assert.ThrowsException(() => + { + liveHost.Services.GetRequiredService(); + }); + } + + [TestMethod] + public async Task Given_ConfigureCheckout_When_Live_Url_And_Prefix_Not_Set_Throws_InvalidOperationException() + { + // Arrange + IHost liveHost = Host.CreateDefaultBuilder() + .ConfigureCheckout((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Live; + }); + }) + .Build(); + + // Act + // Assert + Assert.Throws(() => + liveHost.Services.GetRequiredService() + ); + + Assert.Throws(() => + liveHost.Services.GetRequiredService() + ); + + Assert.Throws(() => + liveHost.Services.GetRequiredService() + ); + + Assert.Throws(() => + liveHost.Services.GetRequiredService() + ); + + Assert.Throws(() => + liveHost.Services.GetRequiredService() + ); + + Assert.Throws(() => + liveHost.Services.GetRequiredService() + ); + + Assert.Throws(() => + liveHost.Services.GetRequiredService() + ); + } + + [TestMethod] + public void Given_PaymentRequest_When_PaymentMethod_Is_AchDetails_Result_Is_Not_Null() + { + // Arrange + // Act + var achDetails = new AchDetails + { + Type = AchDetails.TypeEnum.Ach, + BankAccountNumber = "1234567", + BankLocationId = "1234567", + EncryptedBankAccountNumber = "1234asdfg", + OwnerName = "John Smith" + }; + + var paymentRequest = new PaymentRequest( + merchantAccount: "YOUR_MERCHANT_ACCOUNT", + amount: new Amount("EUR", 1000), reference: "ACH test", + paymentMethod: new CheckoutPaymentMethod(achDetails), + shopperIP: "192.0.2.1", + channel: PaymentRequest.ChannelEnum.Web, origin: "https://your-company.com", + returnUrl: "https://your-company.com/checkout?shopperOrder=12xy.." + ); + + // Assert + AchDetails paymentMethodDetails = paymentRequest.PaymentMethod.AchDetails; + Assert.AreEqual(paymentMethodDetails.Type, AchDetails.TypeEnum.Ach); + Assert.AreEqual(paymentMethodDetails.BankAccountNumber, "1234567"); + Assert.AreEqual(paymentMethodDetails.BankLocationId, "1234567"); + Assert.AreEqual(paymentMethodDetails.EncryptedBankAccountNumber, "1234asdfg"); + Assert.AreEqual(paymentMethodDetails.OwnerName, "John Smith"); + Assert.AreEqual(paymentRequest.MerchantAccount, "YOUR_MERCHANT_ACCOUNT"); + Assert.AreEqual(paymentRequest.Reference, "ACH test"); + Assert.AreEqual(paymentRequest.ReturnUrl, "https://your-company.com/checkout?shopperOrder=12xy.."); + } + + [TestMethod] + public void Given_PaymentRequest_When_PaymentMethod_Is_ApplePayDetails_Result_Is_Not_Null() + { + // Arrange + // Act + var applePay = new ApplePayDetails( + type: ApplePayDetails.TypeEnum.Applepay, + applePayToken: "VNRWtuNlNEWkRCSm1xWndjMDFFbktkQU..." + ); + + var paymentRequest = new PaymentRequest( + merchantAccount: "YOUR_MERCHANT_ACCOUNT", + amount: new Amount("EUR", 1000), + reference: "apple pay test", + paymentMethod: new CheckoutPaymentMethod(applePay), + returnUrl: "https://your-company.com/checkout?shopperOrder=12xy.." + ); + + // Assert + ApplePayDetails paymentMethodDetails = paymentRequest.PaymentMethod.ApplePayDetails; + Assert.AreEqual(paymentMethodDetails.Type, ApplePayDetails.TypeEnum.Applepay); + Assert.AreEqual(paymentMethodDetails.ApplePayToken, "VNRWtuNlNEWkRCSm1xWndjMDFFbktkQU..."); + Assert.AreEqual(paymentRequest.MerchantAccount, "YOUR_MERCHANT_ACCOUNT"); + Assert.AreEqual(paymentRequest.Reference, "apple pay test"); + Assert.AreEqual(paymentRequest.ReturnUrl, "https://your-company.com/checkout?shopperOrder=12xy.."); + } + + [TestMethod] + public void Given_PaymentRequest_When_PaymentMethod_Is_GooglePayDetails_Result_Is_Not_Null() + { + // Arrange + // Act + var paymentRequest = new PaymentRequest( + merchantAccount: "YOUR_MERCHANT_ACCOUNT", + amount: new Amount("EUR", 1000), + reference: "google pay test", + paymentMethod: new CheckoutPaymentMethod( + new GooglePayDetails( + type: GooglePayDetails.TypeEnum.Googlepay, + googlePayToken: "==Payload as retrieved from Google Pay response==", + fundingSource: GooglePayDetails.FundingSourceEnum.Debit + ) + ), + returnUrl: "https://your-company.com/checkout?shopperOrder=12xy.."); + + // Assert + GooglePayDetails paymentMethodDetails = paymentRequest.PaymentMethod.GooglePayDetails; + Assert.AreEqual(paymentMethodDetails.Type, GooglePayDetails.TypeEnum.Googlepay); + Assert.AreEqual(paymentMethodDetails.GooglePayToken, "==Payload as retrieved from Google Pay response=="); + Assert.AreEqual(paymentMethodDetails.FundingSource, GooglePayDetails.FundingSourceEnum.Debit); + Assert.AreEqual(paymentRequest.MerchantAccount, "YOUR_MERCHANT_ACCOUNT"); + Assert.AreEqual(paymentRequest.Reference, "google pay test"); + Assert.AreEqual(paymentRequest.ReturnUrl, "https://your-company.com/checkout?shopperOrder=12xy.."); + } + + [TestMethod] + public void Given_PaymentRequest_When_PaymentMethod_Is_iDEAL_Result_Is_Not_Null() + { + // Arrange + // Act + var paymentRequest = new PaymentRequest( + merchantAccount: "YOUR_MERCHANT_ACCOUNT", + amount: new Amount("EUR", 1000), + reference: "ideal test", + paymentMethod: new CheckoutPaymentMethod(new IdealDetails( + type: IdealDetails.TypeEnum.Ideal, + issuer: "1121") + ), + returnUrl: "https://your-company.com/checkout?shopperOrder=12xy.." ); + + // Assert + IdealDetails paymentMethodDetails = paymentRequest.PaymentMethod.IdealDetails; + Assert.AreEqual(paymentMethodDetails.Type, IdealDetails.TypeEnum.Ideal); + Assert.AreEqual(paymentRequest.MerchantAccount, "YOUR_MERCHANT_ACCOUNT"); + Assert.AreEqual(paymentRequest.Reference, "ideal test"); + Assert.AreEqual(paymentRequest.ReturnUrl, "https://your-company.com/checkout?shopperOrder=12xy.."); + } + + [TestMethod] + public void Given_PaymentRequest_When_PaymentMethod_Is_BacsDirectDebitDetails_Result_Is_Not_Null() + { + // Arrange + // Act + var paymentRequest = new PaymentRequest + { + MerchantAccount = "YOUR_MERCHANT_ACCOUNT", + Amount = new Amount("GBP", 1000), + Reference = "bacs direct debit test", + PaymentMethod = new CheckoutPaymentMethod(new BacsDirectDebitDetails + { + Type = BacsDirectDebitDetails.TypeEnum.DirectdebitGB, + BankAccountNumber = "NL0123456789", + BankLocationId = "121000358", + HolderName = "John Smith" + }), + ReturnUrl = "https://your-company.com/checkout?shopperOrder=12xy.." + }; + + // Assert + BacsDirectDebitDetails paymentMethodDetails = paymentRequest.PaymentMethod.BacsDirectDebitDetails; + Assert.AreEqual(paymentMethodDetails.Type, BacsDirectDebitDetails.TypeEnum.DirectdebitGB); + Assert.AreEqual(paymentMethodDetails.BankAccountNumber, "NL0123456789"); + Assert.AreEqual(paymentMethodDetails.BankLocationId, "121000358"); + Assert.AreEqual(paymentMethodDetails.HolderName, "John Smith"); + Assert.AreEqual(paymentRequest.MerchantAccount, "YOUR_MERCHANT_ACCOUNT"); + Assert.AreEqual(paymentRequest.Reference, "bacs direct debit test"); + Assert.AreEqual(paymentRequest.ReturnUrl, "https://your-company.com/checkout?shopperOrder=12xy.."); + } + + + [TestMethod] + public void Given_PaymentRequest_When_PaymentMethod_Is_PayPalDetails_Result_Is_Not_Null() + { + // Arrange + // Act + var paymentRequest = new PaymentRequest + { + MerchantAccount = "YOUR_MERCHANT_ACCOUNT", + Amount = new Amount("USD", 1000), + Reference = "paypal test", + PaymentMethod = new CheckoutPaymentMethod(new PayPalDetails + { + Type = PayPalDetails.TypeEnum.Paypal, + Subtype = PayPalDetails.SubtypeEnum.Sdk, + StoredPaymentMethodId = "2345654212345432345" + }), + ReturnUrl = "https://your-company.com/checkout?shopperOrder=12xy.." + }; + + // Assert + PayPalDetails paymentMethodDetails = paymentRequest.PaymentMethod.PayPalDetails; + Assert.AreEqual(paymentMethodDetails.Type, PayPalDetails.TypeEnum.Paypal); + Assert.AreEqual(paymentMethodDetails.Subtype, PayPalDetails.SubtypeEnum.Sdk); + } + + [TestMethod] + public void Given_PaymentRequest_When_PaymentMethod_Is_ZipDetails_Result_Is_Not_Null() + { + // Arrange + // Act + var paymentRequest = new PaymentRequest + { + MerchantAccount = "YOUR_MERCHANT_ACCOUNT", + Amount = new Amount("USD", 1000), + Reference = "zip test", + PaymentMethod = new CheckoutPaymentMethod(new ZipDetails + { + Type = ZipDetails.TypeEnum.Zip + }), + ReturnUrl = "https://your-company.com/checkout?shopperOrder=12xy..", + }; + + // Assert + ZipDetails paymentMethodDetails = paymentRequest.PaymentMethod.ZipDetails; + Assert.AreEqual(paymentMethodDetails.Type, ZipDetails.TypeEnum.Zip); + Assert.AreEqual(paymentRequest.MerchantAccount, "YOUR_MERCHANT_ACCOUNT"); + Assert.AreEqual(paymentRequest.Reference, "zip test"); + Assert.AreEqual(paymentRequest.ReturnUrl, "https://your-company.com/checkout?shopperOrder=12xy.."); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/Checkout/UtilityServiceTest.cs b/Adyen.Test/Checkout/UtilityServiceTest.cs new file mode 100644 index 000000000..b4aca2758 --- /dev/null +++ b/Adyen.Test/Checkout/UtilityServiceTest.cs @@ -0,0 +1,79 @@ +using Adyen.Checkout.Extensions; +using Adyen.Checkout.Models; +using Adyen.Checkout.Client; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.Extensions.Hosting; +using Adyen.Core.Options; +using System.Text.Json; + +namespace Adyen.Test.Checkout +{ + [TestClass] + public class UtilityServiceTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public UtilityServiceTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureCheckout((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_Deserialize_When_UtilityResponse_For_OriginKeys_Returns_Success() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkoututility/originkeys-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("pub.v2.7814286629520534.aHR0cHM6Ly93d3cueW91ci1kb21haW4xLmNvbQ.UEwIBmW9-c_uXo5wSEr2w8Hz8hVIpujXPHjpcEse3xI", response.OriginKeys["https://www.your-domain1.com"]); + } + + [TestMethod] + public void Given_Deserialize_When_CardDetailsResponse_Returns_Not_Null() + { + // Arrange + var json = TestUtilities.GetTestFileContent("mocks/checkout/card-details-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.IsFalse(response.IsCardCommercial); + Assert.AreEqual("CREDIT", response.FundingSource); + Assert.AreEqual("FR", response.IssuingCountryCode); + + Assert.AreEqual("visa", response.Brands[0].Type); + Assert.IsTrue(response.Brands[0].Supported); + + Assert.AreEqual("cartebancaire", response.Brands[1].Type); + Assert.IsFalse(response.Brands[1].Supported); + } + + [TestMethod] + public void Given_Deserialize_When_ApplePaySessionResponse_Result_Is_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/checkout/apple-pay-sessions-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("eyJ2Z...340278gdflkaswer", response.Data); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/CheckoutTest.cs b/Adyen.Test/CheckoutTest.cs deleted file mode 100644 index ccdb78961..000000000 --- a/Adyen.Test/CheckoutTest.cs +++ /dev/null @@ -1,1090 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Adyen.HttpClient.Interfaces; -using Adyen.Model; -using Adyen.Model.Checkout; -using Adyen.Service.Checkout; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using NSubstitute; -using static Adyen.Model.Checkout.PaymentResponse; -using ApplicationInfo = Adyen.Model.ApplicationInformation.ApplicationInfo; -using Environment = Adyen.Model.Environment; -using IRecurringService = Adyen.Service.Checkout.IRecurringService; -using RecurringService = Adyen.Service.Checkout.RecurringService; - -namespace Adyen.Test -{ - [TestClass] - public class CheckoutTest : BaseTest - { - /// - /// Tests successful checkout client Test URL generation. - /// - [TestMethod] - public void CheckoutEndpointTestEnvironmentSuccessTest() - { - var client = CreateMockForAdyenClientTest(new Config()); - client.SetEnvironment(Environment.Test, "companyUrl"); - var checkout = new PaymentsService(client); - checkout.PaymentsAsync(new PaymentRequest()).GetAwaiter(); - - ClientInterfaceSubstitute.Received().RequestAsync("https://checkout-test.adyen.com/v71/payments", - Arg.Any(), null, new HttpMethod("POST"), default); - } - - /// - /// Tests successful checkout client Live URL generation. - /// - [TestMethod] - public void CheckoutEndpointLiveEnvironmentSuccessTest() - { - var client = CreateMockForAdyenClientTest(new Config()); - client.SetEnvironment(Environment.Live, "companyUrl"); - var checkout = new PaymentsService(client); - checkout.PaymentsAsync(new PaymentRequest()).GetAwaiter(); - - ClientInterfaceSubstitute.Received().RequestAsync( - "https://companyUrl-checkout-live.adyenpayments.com/checkout/v71/payments", - Arg.Any(), null, new HttpMethod("POST"), Arg.Any()); - } - - /// - /// Tests unsuccessful checkout client Live URL generation. - /// - [TestMethod] - public void CheckoutEndpointLiveErrorTest() - { - var config = new Config(); - var client = new Client(config); - client.SetEnvironment(Environment.Live, null); - Assert.ThrowsException(() => new PaymentsService(client)); - } - - /// - /// Tests unsuccessful checkout client Live URL generation. - /// - [TestMethod] - public void CheckoutEndpointLiveWithBasicAuthErrorTest() - { - var client = new Client(new Config() - { - Username = "ws_*******", - Password = "*******", - Environment = Environment.Live - - }); - Assert.ThrowsException( - () => new PaymentsService(client)); - } - - /// - /// Tests successful checkout client Live URL generation with basic auth. - /// - [TestMethod] - public void CheckoutEndpointLiveWithBasicAuthTest() - { - var client = CreateMockForAdyenClientTest( - new Config - { - Username = "ws_*******", - Password = "******", - Environment = Environment.Live, - LiveEndpointUrlPrefix = "live-url" - }); - var checkout = new PaymentsService(client); - checkout.PaymentsAsync(new PaymentRequest()).GetAwaiter(); - ClientInterfaceSubstitute.Received().RequestAsync("https://live-url-checkout-live.adyenpayments.com/checkout/v71/payments", - Arg.Any(), null, new HttpMethod("POST"), default); - } - - /// - /// Tests successful checkout client Live URL generation with API key. - /// - [TestMethod] - public void CheckoutEndpointLiveWithAPIKeyTest() - { - var client = CreateMockForAdyenClientTest( - new Config - { - XApiKey = "xapikey", - Environment = Environment.Live, - LiveEndpointUrlPrefix = "live-url" - }); - var checkout = new PaymentsService(client); - checkout.PaymentsAsync(new PaymentRequest()).GetAwaiter(); - ClientInterfaceSubstitute.Received().RequestAsync("https://live-url-checkout-live.adyenpayments.com/checkout/v71/payments", - Arg.Any(), null, new HttpMethod("POST"), Arg.Any()); - } - - /// - /// Test success flow for - /// POST /payments - /// - [TestMethod] - public void PaymentsAdditionalDataParsingTest() - { - var paymentRequest = CreatePaymentRequestCheckout(); - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/payments-success.json"); - var checkout = new PaymentsService(client); - var paymentResponse = checkout.Payments(paymentRequest); - Assert.AreEqual("8535296650153317", paymentResponse.PspReference); - Assert.AreEqual(ResultCodeEnum.Authorised, paymentResponse.ResultCode); - Assert.IsNotNull(paymentResponse.AdditionalData); - Assert.AreEqual(9, paymentResponse.AdditionalData.Count); - Assert.AreEqual("8/2018", paymentResponse.AdditionalData["expiryDate"]); - Assert.AreEqual("GREEN", paymentResponse.AdditionalData["fraudResultType"]); - Assert.AreEqual("411111", paymentResponse.AdditionalData["cardBin"]); - Assert.AreEqual("1111", paymentResponse.AdditionalData["cardSummary"]); - Assert.AreEqual("false", paymentResponse.AdditionalData["fraudManualReview"]); - Assert.AreEqual("Default", paymentResponse.AdditionalData["aliasType"]); - Assert.AreEqual("H167852639363479", paymentResponse.AdditionalData["alias"]); - Assert.AreEqual("visa", paymentResponse.AdditionalData["cardPaymentMethod"]); - Assert.AreEqual("NL", paymentResponse.AdditionalData["cardIssuingCountry"]); - } - - /// - /// Test success flow for async - /// POST /payments - /// - [TestMethod] - public async Task PaymentsAsyncAdditionalDataParsingTest() - { - var paymentRequest = CreatePaymentRequestCheckout(); - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/payments-success.json"); - var checkout = new PaymentsService(client); - var paymentResponse = await checkout.PaymentsAsync(paymentRequest); - Assert.AreEqual("8535296650153317", paymentResponse.PspReference); - Assert.AreEqual(ResultCodeEnum.Authorised, paymentResponse.ResultCode); - Assert.IsNotNull(paymentResponse.AdditionalData); - Assert.AreEqual(9, paymentResponse.AdditionalData.Count); - Assert.AreEqual("8/2018", paymentResponse.AdditionalData["expiryDate"]); - Assert.AreEqual("GREEN", paymentResponse.AdditionalData["fraudResultType"]); - Assert.AreEqual("411111", paymentResponse.AdditionalData["cardBin"]); - Assert.AreEqual("1111", paymentResponse.AdditionalData["cardSummary"]); - Assert.AreEqual("false", paymentResponse.AdditionalData["fraudManualReview"]); - Assert.AreEqual("Default", paymentResponse.AdditionalData["aliasType"]); - Assert.AreEqual("H167852639363479", paymentResponse.AdditionalData["alias"]); - Assert.AreEqual("visa", paymentResponse.AdditionalData["cardPaymentMethod"]); - Assert.AreEqual("NL", paymentResponse.AdditionalData["cardIssuingCountry"]); - } - - /// - /// Test success flow for 3DS2 - /// POST /payments - /// - [TestMethod] - public void Payments3DS2Test() - { - var payment3DS2Request = CreatePaymentRequest3DS2(); - var client = - CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/payments-3DS2-IdentifyShopper.json"); - var checkout = new PaymentsService(client); - var paymentResponse = checkout.Payments(payment3DS2Request); - Assert.AreEqual(paymentResponse.ResultCode, ResultCodeEnum.IdentifyShopper); - Assert.AreEqual(paymentResponse.Action.GetCheckoutThreeDS2Action().Type.ToString(), "ThreeDS2"); - Assert.IsNotNull(paymentResponse.Action.GetCheckoutThreeDS2Action().PaymentData); - } - - /// - /// Test error flow for - /// POST /payments - /// - [TestMethod] - public void PaymentsErrorTest() - { - var paymentMethodsRequest = CreatePaymentRequestCheckout(); - var client = - CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/payments-error-invalid-data-422.json"); - var checkout = new PaymentsService(client); - var paymentResponse = checkout.Payments(paymentMethodsRequest); - Assert.IsNull(paymentResponse.PspReference); - } - - /// - /// Test success flow for - /// POST /payments/details - /// - [TestMethod] - public void PaymentDetailsTest() - { - var detailsRequest = CreateDetailsRequest(); - detailsRequest.Details = - new PaymentCompletionDetails( - payload: "Ab02b4c0!BQABAgBQn96RxfJHpp2RXhqQBuhQFWgE...gfGHb4IZSP4IpoCC2==RXhqQBuhQ"); - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/paymentsdetails-success.json"); - var checkout = new PaymentsService(client); - var paymentResponse = checkout.PaymentsDetails(detailsRequest); - Assert.AreEqual("8515232733321252", paymentResponse.PspReference); - } - - /// - /// Test success flow for async - /// POST /payments/details - /// - [TestMethod] - public async Task PaymentDetailsAsyncTest() - { - var detailsRequest = CreateDetailsRequest(); - detailsRequest.Details = - new PaymentCompletionDetails( - payload: "Ab02b4c0!BQABAgBQn96RxfJHpp2RXhqQBuhQFWgE...gfGHb4IZSP4IpoCC2==RXhqQBuhQ"); - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/paymentsdetails-success.json"); - var checkout = new PaymentsService(client); - var paymentResponse = await checkout.PaymentsDetailsAsync(detailsRequest); - Assert.AreEqual("8515232733321252", paymentResponse.PspReference); - } - - /// - /// Test error flow for - /// POST /payments/details - /// - [TestMethod] - public void PaymentDetailsErrorTest() - { - var detailsRequest = CreateDetailsRequest(); - detailsRequest.Details = - new PaymentCompletionDetails( - payload: "Ab02b4c0!BQABAgBQn96RxfJHpp2RXhqQBuhQFWgE...gfGHb4IZSP4IpoCC2==RXhqQBuhQ"); - var client = - CreateMockTestClientApiKeyBasedRequestAsync( - "mocks/checkout/paymentsdetails-error-invalid-data-422.json"); - var checkout = new PaymentsService(client); - var paymentResponse = checkout.PaymentsDetails(detailsRequest); - Assert.IsNull(paymentResponse.ResultCode); - } - - /// - /// Test success deserialization for - /// POST /payments/details - /// - [TestMethod] - public void PaymentDetailsResponseDeserializeTest() - { - var detailsRequest = CreateDetailsRequest(); - detailsRequest.Details = - new PaymentCompletionDetails( - payload: "Ab02b4c0!BQABAgBQn96RxfJHpp2RXhqQBuhQFWgE...gfGHb4IZSP4IpoCC2==RXhqQBuhQ"); - var client = - CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/paymentsdetails-action-success.json"); - var checkout = new PaymentsService(client); - var paymentResponse = checkout.PaymentsDetails(detailsRequest); - Assert.AreEqual(paymentResponse.PspReference, "8515232733321252"); - Assert.AreEqual(paymentResponse.ResultCode, PaymentDetailsResponse.ResultCodeEnum.Authorised); - } - - /// - /// Test success flow for - /// POST /paymentMethods - /// - [TestMethod] - public void PaymentMethodsTest() - { - var paymentMethodsRequest = CreatePaymentMethodRequest("YourMerchantAccount"); - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/paymentmethods-success.json"); - var checkout = new PaymentsService(client); - var paymentMethodsResponse = checkout.PaymentMethods(paymentMethodsRequest); - Assert.AreEqual(paymentMethodsResponse.PaymentMethods.Count, 32); - } - - /// - /// Test success flow for - /// POST /paymentMethods - /// - [TestMethod] - public void PaymentMethodsIssuersTest() - { - var paymentMethodsRequest = CreatePaymentMethodRequest("YourMerchantAccount"); - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/paymentmethods-success.json"); - var checkout = new PaymentsService(client); - var paymentMethodsResponse = checkout.PaymentMethods(paymentMethodsRequest); - Assert.IsNotNull(paymentMethodsResponse.PaymentMethods[12].Issuers); - Assert.AreEqual(paymentMethodsResponse.PaymentMethods[12].Issuers[0].Id, "66"); - Assert.AreEqual(paymentMethodsResponse.PaymentMethods[12].Issuers[0].Name, "Bank Nowy BFG S.A."); - } - - /// - /// Test success flow for - /// POST /paymentMethods - /// - [TestMethod] - public void PaymentMethodsStoreValueTest() - { - var paymentMethodsRequest = CreatePaymentMethodRequest("YourMerchantAccount"); - paymentMethodsRequest.Store = "MerchantStore"; - Assert.AreEqual(paymentMethodsRequest.Store, "MerchantStore"); - } - - /// - /// Test success flow for async - /// POST /paymentMethods - /// - [TestMethod] - public async Task PaymentMethodsAsyncTest() - { - var paymentMethodsRequest = CreatePaymentMethodRequest("YourMerchantAccount"); - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/paymentmethods-success.json"); - var checkout = new PaymentsService(client); - var paymentMethodsResponse = await checkout.PaymentMethodsAsync(paymentMethodsRequest); - Assert.AreEqual(paymentMethodsResponse.PaymentMethods.Count, 32); - } - - /// - /// Test error flow for - /// POST /paymentMethods - /// - [TestMethod] - public void PaymentMethodsErrorTest() - { - var paymentMethodsRequest = CreatePaymentMethodRequest("YourMerchantAccount"); - var client = - CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/paymentmethods-error-forbidden-403.json"); - var checkout = new PaymentsService(client); - var paymentMethodsResponse = checkout.PaymentMethods(paymentMethodsRequest); - Assert.IsNull(paymentMethodsResponse.PaymentMethods); - } - - /// - /// Test success flow including brands check for - /// POST /paymentMethods - /// - [TestMethod] - public void PaymentMethodsWithBrandsTest() - { - var paymentMethodsRequest = CreatePaymentMethodRequest("YourMerchantAccount"); - var client = - CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/paymentmethods-brands-success.json"); - var checkout = new PaymentsService(client); - var paymentMethodsResponse = checkout.PaymentMethods(paymentMethodsRequest); - Assert.AreEqual(paymentMethodsResponse.PaymentMethods.Count, 7); - Assert.AreEqual(paymentMethodsResponse.PaymentMethods[0].Brands.Count, 5); - Assert.AreEqual(paymentMethodsResponse.PaymentMethods[0].Brands[0], "visa"); - Assert.AreEqual(paymentMethodsResponse.PaymentMethods[0].Brands[1], "mc"); - Assert.AreEqual(paymentMethodsResponse.PaymentMethods[0].Brands[2], "amex"); - Assert.AreEqual(paymentMethodsResponse.PaymentMethods[0].Brands[3], "bcmc"); - Assert.AreEqual(paymentMethodsResponse.PaymentMethods[0].Brands[4], "maestro"); - } - - /// - /// Test flow without including brands check for - /// POST /paymentMethods - /// - [TestMethod] - public void PaymentMethodsWithoutBrandsTest() - { - var paymentMethodsRequest = CreatePaymentMethodRequest("YourMerchantAccount"); - var client = - CreateMockTestClientApiKeyBasedRequestAsync( - "mocks/checkout/paymentmethods-without-brands-success.json"); - var checkout = new PaymentsService(client); - var paymentMethodsResponse = checkout.PaymentMethods(paymentMethodsRequest); - Assert.AreEqual(paymentMethodsResponse.PaymentMethods.Count, 50); - } - - [TestMethod] - public void ApplicationInfoTest() - { - ApplicationInfo applicationInfo = new ApplicationInfo(); - Assert.AreEqual(applicationInfo.AdyenLibrary.Name, Constants.ClientConfig.LibName); - Assert.AreEqual(applicationInfo.AdyenLibrary.Version, Constants.ClientConfig.LibVersion); - } - - [Ignore] // The adyen library info will not be added anymore by default, let's investigate if we should. - [TestMethod] - public void PaymentRequestApplicationInfoTest() - { - var paymentRequest = CreatePaymentRequestCheckout(); - var name = paymentRequest.ApplicationInfo.AdyenLibrary.Name; - var version = paymentRequest.ApplicationInfo.AdyenLibrary.Version; - Assert.AreEqual(version, Constants.ClientConfig.LibVersion); - Assert.AreEqual(name, Constants.ClientConfig.LibName); - } - - [TestMethod] - public void PaymentRequestAppInfoExternalTest() - { - var externalPlatform = new ExternalPlatform(); - var merchantApplication = new CommonField(); - externalPlatform.Integrator = "TestExternalPlatformIntegration"; - externalPlatform.Name = "TestExternalPlatformName"; - externalPlatform.Version = "TestExternalPlatformVersion"; - merchantApplication.Name = "MerchantApplicationName"; - merchantApplication.Version = "MerchantApplicationVersion"; - var paymentRequest = CreatePaymentRequestCheckout(); - paymentRequest.ApplicationInfo = new Model.Checkout.ApplicationInfo - { - ExternalPlatform = externalPlatform, - MerchantApplication = merchantApplication - }; - Assert.AreEqual(paymentRequest.ApplicationInfo.ExternalPlatform.Integrator, - "TestExternalPlatformIntegration"); - Assert.AreEqual(paymentRequest.ApplicationInfo.ExternalPlatform.Name, "TestExternalPlatformName"); - Assert.AreEqual(paymentRequest.ApplicationInfo.ExternalPlatform.Version, "TestExternalPlatformVersion"); - Assert.AreEqual(paymentRequest.ApplicationInfo.MerchantApplication.Name, "MerchantApplicationName"); - Assert.AreEqual(paymentRequest.ApplicationInfo.MerchantApplication.Version, "MerchantApplicationVersion"); - } - - - [TestMethod] - public void PaymentsOriginTest() - { - var paymentMethodsRequest = CreatePaymentRequestCheckout(); - paymentMethodsRequest.Origin = "https://localhost:8080"; - Assert.AreEqual(paymentMethodsRequest.Origin, "https://localhost:8080"); - } - - /// - /// Test CreatePaymentLinkRequest - /// POST /payments/result - /// - [TestMethod] - public void CreatePaymentLinkSuccess() - { - var createPaymentLinkRequest = new PaymentLinkRequest(store: "TheDemoStore", - amount: new Amount(currency: "EUR", 1000), merchantAccount: "MerchantAccount", reference: "reference"); - Assert.AreEqual(createPaymentLinkRequest.Store, "TheDemoStore"); - } - - /// - /// Test success flow for - /// POST /payments/result - /// - [TestMethod] - public void PaymentLinksSuccess() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/payment-links-success.json"); - var checkout = new PaymentLinksService(client); - var createPaymentLinkRequest = new PaymentLinkRequest(amount: new Amount(currency: "EUR", 1000), - merchantAccount: "MerchantAccount", reference: "YOUR_ORDER_NUMBER"); - var paymentLinksResponse = checkout.PaymentLinks(createPaymentLinkRequest); - Assert.AreEqual(paymentLinksResponse.Url, - "https://checkoutshopper-test.adyen.com/checkoutshopper/payByLink.shtml?d=YW1vdW50TWlub3JW...JRA"); - Assert.AreEqual(paymentLinksResponse.ExpiresAt, new DateTime(2019,12,17,10,05,29)); - Assert.AreEqual(paymentLinksResponse.Reference, "YOUR_ORDER_NUMBER"); - Assert.IsNotNull(paymentLinksResponse.Amount); - } - - /// - /// Test success flow for creation of a payment link with recurring payment - /// POST /paymentLinks - /// - [TestMethod] - public void CreateRecurringPaymentLinkSuccessTest() - { - var client = - CreateMockTestClientApiKeyBasedRequestAsync( - "mocks/checkout/paymentlinks-recurring-payment-success.json"); - var checkout = new PaymentLinksService(client); - - var createPaymentLinkRequest = new PaymentLinkRequest(amount: new Amount(currency: "EUR", 100), - merchantAccount: "MerchantAccount", reference: "REFERENCE_NUMBER") - { - CountryCode = "GR", - ShopperLocale = "GR", - ShopperReference = "ShopperReference", - StorePaymentMethodMode = PaymentLinkRequest.StorePaymentMethodModeEnum.Enabled, - RecurringProcessingModel = PaymentLinkRequest.RecurringProcessingModelEnum.Subscription - }; - - var paymentLinksResponse = checkout.PaymentLinks(createPaymentLinkRequest); - - Assert.AreEqual(createPaymentLinkRequest.Reference, paymentLinksResponse.Reference); - Assert.AreEqual( - "https://checkoutshopper-test.adyen.com/checkoutshopper/payByLink.shtml?d=YW1vdW50TWlub3JW...JRA", - paymentLinksResponse.Url); - Assert.AreEqual(createPaymentLinkRequest.Amount.Currency, paymentLinksResponse.Amount.Currency); - Assert.AreEqual(createPaymentLinkRequest.Amount.Value, paymentLinksResponse.Amount.Value); - } - - /// - /// Test success flow for multibanco - /// Post /payments - /// - [Ignore] - [TestMethod] - public void MultibancoPaymentSuccessMockedTest() - { - var client = - CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/paymentsresult-multibanco-success.json"); - var checkout = new PaymentsService(client); - var paymentRequest = CreatePaymentRequestCheckout(); - var paymentResponse = checkout.Payments(paymentRequest); - var paymentResponseAction = paymentResponse.Action.GetCheckoutVoucherAction(); - Assert.AreEqual(paymentResponseAction.PaymentMethodType, "multibanco"); - Assert.AreEqual(paymentResponseAction.ExpiresAt, "01/12/2020 09:37:49"); - Assert.AreEqual(paymentResponseAction.Reference, "501 422 944"); - Assert.AreEqual(paymentResponseAction.Entity, "12101"); - } - - /// - /// Test RiskData - Clientdata flow for - /// POST /payments - /// - [TestMethod] - public void PaymentClientdataParsingTest() - { - var paymentRequest = CreatePaymentRequestCheckout(); - var riskdata = new RiskData - { - ClientData = "IOfW3k9G2PvXFu2j" - }; - paymentRequest.RiskData = riskdata; - Assert.AreEqual(paymentRequest.RiskData.ClientData, "IOfW3k9G2PvXFu2j"); - } - - /// - /// Test success flow for paypal - /// Post /payments - /// - [TestMethod] - public void PaypalPaymentSuccessTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/payments-success-paypal.json"); - var checkout = new PaymentsService(client); - var paymentRequest = CreatePaymentRequestCheckout(); - var paymentResponse = checkout.Payments(paymentRequest); - var result = paymentResponse.Action.GetCheckoutSDKAction(); - Assert.IsNotNull(result); - Assert.AreEqual("EC-42N19135GM6949000", result.SdkData["orderID"]); - Assert.AreEqual("Ab02b4c0!BQABAgARb1TvUJa4nwS0Z1nOmxoYfD9+z...", result.PaymentData); - Assert.AreEqual("paypal", result.PaymentMethodType); - } - - [TestMethod] - public void ApplePayDetailsDeserializationTest() - { - var json = "{\"type\": \"applepay\",\"applePayToken\": \"VNRWtuNlNEWkRCSm1xWndjMDFFbktkQU...\"}"; - var result = CheckoutPaymentMethod.FromJson(json); - Assert.IsInstanceOfType(result.ActualInstance); - Assert.AreEqual(result.GetApplePayDetails().Type, ApplePayDetails.TypeEnum.Applepay); - } - - [TestMethod] - public void CheckoutPaymentMethodDeserializationWithUnknownValuesTest() - { - var json = "{\"type\": \"applepay\",\"someValue\": \"notInSpec\",\"applePayToken\": \"VNRWtuNlNEWkRCSm1xWndjMDFFbktkQU...\"}"; - var result = CheckoutPaymentMethod.FromJson(json); - var json2 = "{\"type\": \"paywithgoogle\",\"someValue\": \"notInSpec\",\"googlePayToken\": \"==Payload as retrieved from Google Pay response==\"}"; - var result2 = CheckoutPaymentMethod.FromJson(json2); - Assert.IsInstanceOfType(result.ActualInstance); - Assert.IsInstanceOfType(result2.ActualInstance); - Assert.AreEqual(result2.GetPayWithGoogleDetails().GooglePayToken, "==Payload as retrieved from Google Pay response=="); - } - - [TestMethod] - public void BlikDetailsDeserializationTest() - { - var json = "{\"type\":\"blik\",\"blikCode\":\"blikCode\"}"; - var result = JsonConvert.DeserializeObject(json); - Assert.IsInstanceOfType(result); - Assert.AreEqual(result.Type, BlikDetails.TypeEnum.Blik); - } - - [TestMethod] - public void DragonpayDetailsDeserializationTest() - { - var json = - "{\"issuer\":\"issuer\",\"shopperEmail\":\"test@test.com\",\"type\":\"dragonpay_ebanking\"}"; - var result = Util.JsonOperation.Deserialize(json); - Assert.IsInstanceOfType(result); - Assert.AreEqual(result.Type, DragonpayDetails.TypeEnum.Ebanking); - } - - [TestMethod] - public void AfterPayDetailsDeserializationTest() - { - var json = @"{ - 'resultCode':'RedirectShopper', - 'action':{ - 'paymentMethodType':'afterpaytouch', - 'method':'GET', - 'url':'https://checkoutshopper-test.adyen.com/checkoutshopper/checkoutPaymentRedirect?redirectData=...', - 'type':'redirect' - } - }"; - var result = JsonConvert.DeserializeObject(json); - Assert.IsInstanceOfType(result.Action.GetCheckoutRedirectAction()); - Assert.AreEqual(result.Action.GetCheckoutRedirectAction().PaymentMethodType, "afterpaytouch"); - } - - [TestMethod] - public void AfterPayDetailsSerializationTest() - { - var json = @"{ - 'paymentMethod':{ - 'type':'afterpaytouch' - }, - 'amount':{ - 'value':1000, - 'currency':'AUD' - }, - 'shopperName':{ - 'firstName':'Simon', - 'lastName':'Hopper' - }, - 'shopperEmail':'s.hopper@example.com', - 'shopperReference':'YOUR_UNIQUE_SHOPPER_ID', - 'reference':'YOUR_ORDER_REFERENCE', - 'merchantAccount':'YOUR_MERCHANT_ACCOUNT', - 'returnUrl':'https://your-company.com/checkout?shopperOrder=12xy..', - 'countryCode':'AU', - 'telephoneNumber':'+61 2 8520 3890', - 'billingAddress':{ - 'city':'Sydney', - 'country':'AU', - 'houseNumberOrName':'123', - 'postalCode':'2000', - 'stateOrProvince':'NSW', - 'street':'Happy Street' - }, - 'deliveryAddress':{ - 'city':'Sydney', - 'country':'AU', - 'houseNumberOrName':'123', - 'postalCode':'2000', - 'stateOrProvince':'NSW', - 'street':'Happy Street' - }, - 'lineItems':[ - { - 'description':'Shoes', - 'quantity':'1', - 'amountIncludingTax':'400', - 'amountExcludingTax': '331', - 'taxAmount': '69', - 'id':'Item #1' - }, - { - 'description':'Socks', - 'quantity':'2', - 'amountIncludingTax':'300', - 'amountExcludingTax': '248', - 'taxAmount': '52', - 'id':'Item #2' - } - ] - }"; - - var result = JsonConvert.DeserializeObject(json); - Assert.IsInstanceOfType(result.PaymentMethod.GetAfterpayDetails()); - Assert.AreEqual(result.PaymentMethod.GetAfterpayDetails().Type, AfterpayDetails.TypeEnum.Afterpaytouch); - } - - /// - /// Test toJson() that includes the type in the action - /// - [TestMethod] - public void PaymentsResponseToJsonTest() - { - var paymentRequest = CreatePaymentRequestCheckout(); - var client = - CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/paymentResponse-3DS-ChallengeShopper.json"); - var checkout = new PaymentsService(client); - var paymentResponse = checkout.Payments(paymentRequest); - var paymentResponseToJson = paymentResponse.ToJson(); - var jObject = JObject.Parse(paymentResponseToJson); - Assert.AreEqual(jObject["action"]["type"], "threeDS2"); - } - - [TestMethod] - public void StoredPaymentMethodsTest() - { - var client = - CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/paymentmethods-storedpaymentmethods.json"); - var checkout = new PaymentsService(client); - var paymentMethodsRequest = new PaymentMethodsRequest(merchantAccount: "TestMerchant"); - var paymentMethodsResponse = checkout.PaymentMethods(paymentMethodsRequest); - Assert.AreEqual(4, paymentMethodsResponse.StoredPaymentMethods.Count); - Assert.AreEqual("NL32ABNA0515071439", paymentMethodsResponse.StoredPaymentMethods[0].Iban); - Assert.AreEqual("Adyen", paymentMethodsResponse.StoredPaymentMethods[0].OwnerName); - Assert.AreEqual("sepadirectdebit", paymentMethodsResponse.StoredPaymentMethods[0].Type); - } - - /// - /// Test if the fraud result are properly deseriazed - /// POST /payments - /// - [TestMethod] - public void ThreeDS2Test() - { - var paymentRequest = CreatePaymentRequestCheckout(); - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/paymentResponse-3DS2-Action.json"); - var checkout = new PaymentsService(client); - var paymentResponse = checkout.Payments(paymentRequest); - var paymentResponseThreeDs2Action = paymentResponse.Action.GetCheckoutThreeDS2Action(); - Assert.AreEqual(ResultCodeEnum.IdentifyShopper, paymentResponse.ResultCode); - Assert.AreEqual(CheckoutThreeDS2Action.TypeEnum.ThreeDS2, paymentResponseThreeDs2Action.Type); - } - - [TestMethod] - public void CheckoutLocalDateSerializationTest() - { - var checkoutSessionRequest = new CreateCheckoutSessionRequest - { - MerchantAccount = "merchant", - Reference = "TestReference", - ReturnUrl = "http://test-url.com", - Amount = new Amount("EUR", 10000L), - DateOfBirth = new DateTime(1998, 1, 1, 1, 1, 1), - ExpiresAt = new DateTime(2023, 4, 1, 1, 1, 1) - }; - // Create a DateTime object with minutes and seconds and verify it gets omitted - Assert.IsTrue(checkoutSessionRequest.ToJson().Contains("1998-01-01")); - // Opposite; check that it keeps full ISO string for other Date parameters - Assert.IsTrue(checkoutSessionRequest.ToJson().Contains("2023-04-01T01:01:01")); - } - - /// - /// Test success sessions - /// POST /sessions - /// - [TestMethod] - public void CheckoutSessionSuccessTest() - { - var checkoutSessionRequest = new CreateCheckoutSessionRequest - { - MerchantAccount = "TestMerchant", - Reference = "TestReference", - ReturnUrl = "http://test-url.com", - Amount = new Amount("EUR", 10000L), - Store = "My Store" - }; - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/sessions-success.json"); - var checkout = new PaymentsService(client); - var checkoutSessionResponse = checkout.Sessions(checkoutSessionRequest); - Assert.AreEqual("TestMerchant", checkoutSessionResponse.MerchantAccount); - Assert.AreEqual("TestReference", checkoutSessionResponse.Reference); - Assert.AreEqual("http://test-url.com", checkoutSessionResponse.ReturnUrl); - Assert.AreEqual("EUR", checkoutSessionResponse.Amount.Currency); - Assert.AreEqual("1000", checkoutSessionResponse.Amount.Value.ToString()); - Assert.AreEqual("My Store", checkoutSessionResponse.Store); - - } - - /// - /// Test success orders - /// POST /paymentMethods/balance - /// - [TestMethod] - public void CheckoutPaymentMethodsBalanceSuccessTest() - { - var checkoutBalanceCheckRequest = new BalanceCheckRequest - (amount: new Amount("EUR", 10000L), - merchantAccount: "TestMerchant", - reference: "TestReference", - paymentMethod: new Dictionary - { - { "type", "givex" }, - { "number", "4126491073027401" }, - { "cvc", "737" } - }); - var client = - CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/paymentmethods-balance-success.json"); - var checkout = new OrdersService(client); - var checkoutBalanceCheckResponse = checkout.GetBalanceOfGiftCard(checkoutBalanceCheckRequest); - Assert.AreEqual(BalanceCheckResponse.ResultCodeEnum.Success, - checkoutBalanceCheckResponse.ResultCode); - Assert.AreEqual("EUR", checkoutBalanceCheckResponse.Balance.Currency); - Assert.AreEqual("2500", checkoutBalanceCheckResponse.Balance.Value.ToString()); - } - - /// - /// Test success orders - /// POST /orders - /// - [TestMethod] - public void CheckoutOrderSuccessTest() - { - var checkoutCreateOrderRequest = new CreateOrderRequest - (amount: new Amount("EUR", 10000L), - merchantAccount: "TestMerchant", - reference: "TestReference"); - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/orders-success.json"); - var checkout = new OrdersService(client); - var checkoutOrdersResponse = checkout.Orders(checkoutCreateOrderRequest); - Assert.AreEqual(CreateOrderResponse.ResultCodeEnum.Success, checkoutOrdersResponse.ResultCode); - Assert.AreEqual("8515930288670953", checkoutOrdersResponse.PspReference); - Assert.AreEqual("Ab02b4c0!BQABAgBqxSuFhuXUF7IvIRvSw5bDPHN...", checkoutOrdersResponse.OrderData); - Assert.AreEqual("EUR", checkoutOrdersResponse.RemainingAmount.Currency); - Assert.AreEqual("2500", checkoutOrdersResponse.RemainingAmount.Value.ToString()); - } - - /// - /// Test success orders cancel - /// POST /orders/cancel - /// - [TestMethod] - public void CheckoutOrdersCancelSuccessTest() - { - var checkoutCancelOrderRequest = new CancelOrderRequest - (merchantAccount: "TestMerchant", - order: new EncryptedOrderData(orderData: "823fh892f8f18f4...148f13f9f3f", pspReference: "8815517812932012")); - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/orders-cancel-success.json"); - var checkout = new OrdersService(client); - var checkoutOrdersCancelResponse = checkout.CancelOrder(checkoutCancelOrderRequest); - Assert.AreEqual("Received", checkoutOrdersCancelResponse.ResultCode.ToString()); - Assert.AreEqual("8515931182066678", checkoutOrdersCancelResponse.PspReference); - } - - /// - /// Test success orders cancel - /// GET /storedPaymentMethods - /// - [TestMethod] - public void GetStoredPaymentMethodsTest() - { - var client = - CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/get-storedPaymentMethod-success.json"); - var checkout = new RecurringService(client); - var listStoredPaymentMethodsResponse = - checkout.GetTokensForStoredPaymentDetails("shopperRef", "merchantAccount"); - Assert.AreEqual("string", listStoredPaymentMethodsResponse.StoredPaymentMethods[0].Type); - Assert.AreEqual("merchantAccount", listStoredPaymentMethodsResponse.MerchantAccount); - } - - /// - /// Test success orders cancel - /// GET /storedPaymentMethods - /// - [TestMethod] - public void DeleteStoredPaymentMethodsTest() - { - var client = - CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/get-storedPaymentMethod-success.json"); - var checkout = new RecurringService(client); - checkout.DeleteTokenForStoredPaymentDetails("recurringId","shopperRef", "merchantAccount"); - } - - #region Modification endpoints tests - - /// - /// Test success capture - /// POST /payments/{paymentPspReference}/captures - /// - [TestMethod] - public void PaymentsCapturesTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/captures-success.json"); - var checkout = new ModificationsService(client); - var createPaymentCaptureRequest = new PaymentCaptureRequest(amount: new Amount("EUR", 1000L), - merchantAccount: "test_merchant_account"); - var paymentCaptureResource = checkout.CaptureAuthorisedPayment("12321A", createPaymentCaptureRequest); - Assert.AreEqual(PaymentCaptureResponse.StatusEnum.Received, paymentCaptureResource.Status); - Assert.AreEqual("my_reference", paymentCaptureResource.Reference); - } - - /// - /// Test success payments cancels - /// POST /payments/{paymentPspReference}/cancels - /// - [TestMethod] - public void PaymentsCancelsTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/cancels-success.json"); - var checkout = new ModificationsService(client); - var createPaymentCancelRequest = new PaymentCancelRequest(merchantAccount: "test_merchant_account"); - var paymentCancelResource = - checkout.CancelAuthorisedPaymentByPspReference("12321A", createPaymentCancelRequest); - Assert.AreEqual(PaymentCancelResponse.StatusEnum.Received, paymentCancelResource.Status); - Assert.AreEqual("my_reference", paymentCancelResource.Reference); - } - - /// - /// Test success cancels - /// POST /cancels - /// - [TestMethod] - public void CancelsTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/standalone-cancels-success.json"); - var checkout = new ModificationsService(client); - var createStandalonePaymentCancelRequest = - new StandalonePaymentCancelRequest(merchantAccount: "test_merchant_account"); - var standalonePaymentCancelResource = - checkout.CancelAuthorisedPayment(createStandalonePaymentCancelRequest); - Assert.AreEqual(StandalonePaymentCancelResponse.StatusEnum.Received, - standalonePaymentCancelResource.Status); - Assert.AreEqual("861633338418518C", standalonePaymentCancelResource.PspReference); - } - - /// - /// Test success payments refunds - /// POST /payments/{paymentPspReference}/refunds - /// - [TestMethod] - public void TestPaymentsRefunds() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/refunds-success.json"); - var checkout = new ModificationsService(client); - var createPaymentRefundRequest = new PaymentRefundRequest(amount: new Amount("EUR", 1000L), - merchantAccount: "test_merchant_account"); - var paymentRefundResource = checkout.RefundCapturedPayment("12321A", createPaymentRefundRequest); - Assert.AreEqual(PaymentRefundResponse.StatusEnum.Received, paymentRefundResource.Status); - Assert.AreEqual("my_reference", paymentRefundResource.Reference); - } - - /// - /// Test success payments reversals - /// POST /payments/{paymentPspReference}/reversals - /// - [TestMethod] - public void PaymentsReversalsTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/reversals-success.json"); - var checkout = new ModificationsService(client); - var createPaymentReversalRequest = - new PaymentReversalRequest(merchantAccount: "test_merchant_account"); - var paymentReversalResource = checkout.RefundOrCancelPayment("12321A", createPaymentReversalRequest); - Assert.AreEqual(PaymentReversalResponse.StatusEnum.Received, paymentReversalResource.Status); - Assert.AreEqual("my_reference", paymentReversalResource.Reference); - } - - /// - /// Test success payments cancels - /// POST /payments/{paymentPspReference}/amountUpdates - /// - [TestMethod] - public void PaymentsAmountUpdatesTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/amount-updates-success.json"); - var checkout = new ModificationsService(client); - var createPaymentAmountUpdateRequest = new PaymentAmountUpdateRequest( - amount: new Amount("EUR", 1000L), - merchantAccount: "test_merchant_account"); - var paymentAmountUpdateResource = - checkout.UpdateAuthorisedAmount("12321A", createPaymentAmountUpdateRequest); - Assert.AreEqual(PaymentAmountUpdateResponse.StatusEnum.Received, paymentAmountUpdateResource.Status); - Assert.AreEqual("my_reference", paymentAmountUpdateResource.Reference); - } - - /// - /// Test success donations - /// POST /donations - /// - [TestMethod] - public void DonationsTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/donations-success.json"); - var checkout = new DonationsService(client); - var paymentDonationRequest = - new DonationPaymentRequest( - merchantAccount: "test_merchant_account", - amount: new Amount("USD", 5), - donationAccount: "Charity_TEST", - paymentMethod: new DonationPaymentMethod(new CardDonations()), - reference: "179761FE-1913-4226-9F43-E475DE634BBA", - returnUrl: "https://your-company.com/..."); - var donationResponse = checkout.Donations(paymentDonationRequest); - Assert.AreEqual(DonationPaymentResponse.StatusEnum.Completed, - donationResponse.Status); - Assert.AreEqual("10720de4-7c5d-4a17-9161-fa4abdcaa5c4", donationResponse.Reference); - } - - /// - /// Test success donations - /// POST /donations - /// - [TestMethod] - public void CardDetailsTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/card-details-success.json"); - var checkout = new PaymentsService(client); - var cardDetailRequest = - new CardDetailsRequest - { - MerchantAccount = "TestMerchant", - CardNumber = "1234567890", - CountryCode = "NL" - }; - var cardDetailResponse = checkout.CardDetails(cardDetailRequest); - Assert.AreEqual("visa", cardDetailResponse.Brands[0].Type); - Assert.AreEqual("cartebancaire", cardDetailResponse.Brands[1].Type); - } - - /// - /// Test success donations - /// POST /donations - /// - [TestMethod] - public void ApplePaySessionsTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/apple-pay-sessions-success.json"); - var checkout = new UtilityService(client); - var applePaySessionRequest = new ApplePaySessionRequest() - { - DisplayName = "YOUR_MERCHANT_NAME", - DomainName = "domainName", - MerchantIdentifier = "234tvsadh34fsghlker3..w35sgfs" - - }; - var applePayResponse = checkout.GetApplePaySessionAsync(applePaySessionRequest).Result; - Assert.AreEqual("eyJ2Z...340278gdflkaswer", applePayResponse.Data); - } - - #endregion - - /// - /// Test success orders cancel - /// GET /storedPaymentMethods - /// - [TestMethod] - public void CheckoutServiceInterfaceTest() - { - var client = - CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkout/get-storedPaymentMethod-success.json"); - var checkout = new MyRecurringService(client); - checkout.DeleteTokenForStoredPaymentDetails("shopperRef", "merchantAccount"); - } - } - - // Implementation to test the Recurring Service Interface - public class MyRecurringService : IRecurringService - { - private readonly IClient _client; - public MyRecurringService(Client client) - { - _client = client.HttpClient; - } - - public void DeleteTokenForStoredPaymentDetails(string recurringId, string shopperReference = default, - string merchantAccount = default, RequestOptions requestOptions = default) - { - var response = _client.Request("", "json", requestOptions, HttpMethod.Delete); - } - - public Task DeleteTokenForStoredPaymentDetailsAsync(string recurringId, string shopperReference = default, - string merchantAccount = default, RequestOptions requestOptions = default, - CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public ListStoredPaymentMethodsResponse GetTokensForStoredPaymentDetails(string shopperReference = default, - string merchantAccount = default, RequestOptions requestOptions = default) - { - throw new NotImplementedException(); - } - - public Task GetTokensForStoredPaymentDetailsAsync(string shopperReference = default, string merchantAccount = default, - RequestOptions requestOptions = default, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - public StoredPaymentMethodResource StoredPaymentMethods(StoredPaymentMethodRequest storedPaymentMethodRequest = default, - RequestOptions requestOptions = default) - { - throw new NotImplementedException(); - } - - public Task StoredPaymentMethodsAsync(StoredPaymentMethodRequest storedPaymentMethodRequest = default, - RequestOptions requestOptions = default, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - } -} diff --git a/Adyen.Test/CheckoutUtilityTest.cs b/Adyen.Test/CheckoutUtilityTest.cs deleted file mode 100644 index 70515889c..000000000 --- a/Adyen.Test/CheckoutUtilityTest.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; -using Adyen.Model.Checkout; -using Adyen.Service.Checkout; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Adyen.Test -{ - [TestClass] - public class CheckoutUtilityTest : BaseTest - { - /// - /// Test success flow for - ///POST /originKeys - /// - [TestMethod] - public void OriginKeysSuccessTest() - { - var checkoutUtilityRequest = new UtilityRequest(originDomains: new List { "www.test.com", "https://www.your-domain2.com" }); - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/checkoututility/originkeys-success.json"); - var _checkout = new UtilityService(client); - var originKeysResponse = _checkout.OriginKeys(checkoutUtilityRequest); - Assert.AreEqual("pub.v2.7814286629520534.aHR0cHM6Ly93d3cueW91ci1kb21haW4xLmNvbQ.UEwIBmW9-c_uXo5wSEr2w8Hz8hVIpujXPHjpcEse3xI", originKeysResponse.OriginKeys["https://www.your-domain1.com"]); - } - } -} diff --git a/Adyen.Test/Core/Auth/ApiTokenTest.cs b/Adyen.Test/Core/Auth/ApiTokenTest.cs new file mode 100644 index 000000000..62651ad7d --- /dev/null +++ b/Adyen.Test/Core/Auth/ApiTokenTest.cs @@ -0,0 +1,53 @@ +using Adyen.Checkout.Extensions; +using Adyen.BalancePlatform.Extensions; +using Adyen.Core.Auth; +using Adyen.Core.Options; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Adyen.Test.Core.Auth +{ + [TestClass] + public class ApiKeyTokenTest + { + [TestMethod] + public async Task Given_Multiple_Api_Tokens_When_Injected_Then_Correct_TokenProvider_Is_Used_To_Provide_Correct_ApiKeyToken() + { + // Arrange + IHost testHost = Host.CreateDefaultBuilder() + .ConfigureCheckout((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.AdyenApiKey = "adyen-api-key"; + options.Environment = AdyenEnvironment.Test; + }); + }) + .ConfigureBalancePlatform((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.AdyenApiKey = "balanceplatform-adyen-api-key"; + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + // Act + ITokenProvider balancePlatformApiKey = testHost.Services.GetRequiredService>(); + ITokenProvider checkoutApiKey = testHost.Services.GetRequiredService>(); + + using var balancePlatformMessage = new HttpRequestMessage(HttpMethod.Get, "/dummy/endpoint"); + balancePlatformApiKey.Get().AddTokenToHttpRequestMessageHeader(balancePlatformMessage); + + using var checkoutMessage = new HttpRequestMessage(HttpMethod.Get, "/dummy/endpoint"); + checkoutApiKey.Get().AddTokenToHttpRequestMessageHeader(checkoutMessage); + + // Assert + Assert.AreEqual(balancePlatformMessage.Headers.First().Value.First(), "balanceplatform-adyen-api-key"); + Assert.AreEqual(checkoutMessage.Headers.First().Value.First(), "adyen-api-key"); + } + + } +} \ No newline at end of file diff --git a/Adyen.Test/Core/Auth/TokenProviderTest.cs b/Adyen.Test/Core/Auth/TokenProviderTest.cs new file mode 100644 index 000000000..db8862722 --- /dev/null +++ b/Adyen.Test/Core/Auth/TokenProviderTest.cs @@ -0,0 +1,49 @@ +using Adyen.Core.Auth; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Adyen.Test.Core.Auth +{ + [TestClass] + public class TokenProviderTest + { + internal sealed class TestToken : TokenBase + { + public string Value { get; } + + internal TestToken(string value) + { + Value = value; + } + } + + [TestMethod] + public async Task Given_TokenProviderWithToken_When_GetCalled_Then_SameInstanceAndValueAreReturned() + { + // Arrange + var token = new TestToken("apikey"); + var provider = new TokenProvider(token); + + // Act + var result = provider.Get(); + + // Assert + Assert.AreSame(token, result); + Assert.AreEqual("apikey", result.Value); + } + + [TestMethod] + public async Task Given_TokenProvider_When_GetCalledMultipleTimes_Then_SameInstanceReturnedEachTime() + { + // Arrange + var token = new TestToken("apikey"); + var provider = new TokenProvider(token); + + // Act + var first = provider.Get(); + var second = provider.Get(); + + // Assert + Assert.AreSame(first, second); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/Core/Client/ApiResponseTest.cs b/Adyen.Test/Core/Client/ApiResponseTest.cs new file mode 100644 index 000000000..d89d88f50 --- /dev/null +++ b/Adyen.Test/Core/Client/ApiResponseTest.cs @@ -0,0 +1,173 @@ +using System; +using System.IO; +using System.Net; +using System.Net.Http; +using System.Text.Json; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Adyen.Core.Client; + +namespace Adyen.Test.Core.Client +{ + [TestClass] + public class ApiResponseTests + { + private HttpRequestMessage CreateRequest(string? uri = "https://adyen.com") + => new HttpRequestMessage(HttpMethod.Get, uri); + + private HttpResponseMessage CreateResponse(HttpStatusCode statusCode, string reasonPhrase = "") + => new HttpResponseMessage(statusCode) + { + ReasonPhrase = reasonPhrase + }; + + [TestMethod] + [DataRow(HttpStatusCode.OK, true)] + [DataRow(HttpStatusCode.Created, true)] + [DataRow(HttpStatusCode.Accepted, true)] + [DataRow(HttpStatusCode.BadRequest, false)] + [DataRow(HttpStatusCode.Unauthorized, false)] + [DataRow(HttpStatusCode.Forbidden, false)] + [DataRow(HttpStatusCode.NotFound, false)] + [DataRow(HttpStatusCode.TooManyRequests, false)] + [DataRow(HttpStatusCode.UnprocessableEntity, false)] + [DataRow(HttpStatusCode.InternalServerError, false)] + public async Task Given_ApiResponse_When_SuccessStatusCode_Then_Result_ShouldMatchHttpResponseMessage(HttpStatusCode code, bool expected) + { + // Arrange + // Act + var response = new ApiResponse(CreateRequest(), CreateResponse(code), "", "/", DateTime.UtcNow, new JsonSerializerOptions()); + // Assert + Assert.AreEqual(expected, response.IsSuccessStatusCode); + } + + [TestMethod] + public async Task Given_ApiResponse_When_Properties_Are_Set_Then_Object_Should_Return_Correct_Values() + { + // Arrange + var request = CreateRequest("https://adyen.com/"); + + // Act + var responseMessage = CreateResponse(HttpStatusCode.Accepted, "Accepted"); + var apiResponse = new ApiResponse(request, responseMessage, "{\"key\":\"value\"}", "/path", DateTime.UtcNow, new JsonSerializerOptions()); + + // Assert + Assert.AreEqual(responseMessage.StatusCode, apiResponse.StatusCode); + Assert.AreEqual(responseMessage.ReasonPhrase, apiResponse.ReasonPhrase); + Assert.AreEqual(request.RequestUri, apiResponse.RequestUri); + Assert.AreEqual("/path", apiResponse.Path); + Assert.IsNotNull(apiResponse.Headers); + Assert.AreEqual("{\"key\":\"value\"}", apiResponse.RawContent); + Assert.IsNull(apiResponse.ContentStream); + } + + [TestMethod] + public async Task Given_ApiResponse_When_ContentStream_Is_Set_Then_Return_ContentStream_And_Empty_RawContent() + { + // Arrange + var request = CreateRequest(); + var responseMessage = CreateResponse(HttpStatusCode.OK); + + // Act + var stream = new MemoryStream(new byte[] { 1, 2, 3 }); + var apiResponse = new ApiResponse(request, responseMessage, stream, "/stream", DateTime.UtcNow, new JsonSerializerOptions()); + + // Assert + Assert.AreEqual(stream, apiResponse.ContentStream); + Assert.AreEqual(string.Empty, apiResponse.RawContent); + } + + private class TestApiResponse : ApiResponse, + IOk, ICreated, IAccepted, + IBadRequest, IUnauthorized, IForbidden, ITooManyRequests, INotFound, IUnprocessableContent, IInternalServerError + { + public TestApiResponse(HttpRequestMessage message, HttpResponseMessage response, string raw, string path, DateTime requested, JsonSerializerOptions opts) + : base(message, response, raw, path, requested, opts) { } + + private T DeserializeRaw() => JsonSerializer.Deserialize(RawContent, _jsonSerializerOptions)!; + + public T Ok() => DeserializeRaw(); + public bool TryDeserializeOkResponse(out T? result) { result = string.IsNullOrEmpty(RawContent) ? default : DeserializeRaw(); return result != null; } + + public T Created() => DeserializeRaw(); + public bool TryDeserializeCreatedResponse(out T? result) { result = string.IsNullOrEmpty(RawContent) ? default : DeserializeRaw(); return result != null; } + + public T Accepted() => DeserializeRaw(); + public bool TryDeserializeAcceptedResponse(out T? result) { result = string.IsNullOrEmpty(RawContent) ? default : DeserializeRaw(); return result != null; } + + public T BadRequest() => DeserializeRaw(); + public bool TryDeserializeBadRequestResponse(out T? result) { result = string.IsNullOrEmpty(RawContent) ? default : DeserializeRaw(); return result != null; } + + public T Unauthorized() => DeserializeRaw(); + public bool TryDeserializeUnauthorizedResponse(out T? result) { result = string.IsNullOrEmpty(RawContent) ? default : DeserializeRaw(); return result != null; } + + public T Forbidden() => DeserializeRaw(); + public bool TryDeserializeForbiddenResponse(out T? result) { result = string.IsNullOrEmpty(RawContent) ? default : DeserializeRaw(); return result != null; } + + public T TooManyRequests() => DeserializeRaw(); + public bool TryDeserializeTooManyRequestsResponse(out T? result) { result = string.IsNullOrEmpty(RawContent) ? default : DeserializeRaw(); return result != null; } + + public T NotFound() => DeserializeRaw(); + public bool TryDeserializeNotFoundResponse(out T? result) { result = string.IsNullOrEmpty(RawContent) ? default : DeserializeRaw(); return result != null; } + + public T UnprocessableContent() => DeserializeRaw(); + public bool TryDeserializeUnprocessableContentResponse(out T? result) { result = string.IsNullOrEmpty(RawContent) ? default : DeserializeRaw(); return result != null; } + + public T InternalServerError() => DeserializeRaw(); + public bool TryDeserializeInternalServerErrorResponse(out T? result) { result = string.IsNullOrEmpty(RawContent) ? default : DeserializeRaw(); return result != null; } + } + + private record TestModel(string Foo); + + [TestMethod] + public async Task Given_ApiResponse_When_TypedResponses_Then_Deserialize_Correctly() + { + // Arrange + var model = new TestModel("adyen"); + + // Act + string json = JsonSerializer.Serialize(model, new JsonSerializerOptions()); + var response = new TestApiResponse(CreateRequest(), CreateResponse(HttpStatusCode.OK), json, "/path", DateTime.UtcNow, new JsonSerializerOptions()); + + // Assert + Assert.AreEqual(model, response.Ok()); + Assert.IsTrue(response.TryDeserializeOkResponse(out var okResult)); + Assert.AreEqual(model, okResult); + + Assert.AreEqual(model, response.Created()); + Assert.IsTrue(response.TryDeserializeCreatedResponse(out var createdResult)); + Assert.AreEqual(model, createdResult); + + Assert.AreEqual(model, response.Accepted()); + Assert.IsTrue(response.TryDeserializeAcceptedResponse(out var acceptedResult)); + Assert.AreEqual(model, acceptedResult); + + Assert.AreEqual(model, response.BadRequest()); + Assert.IsTrue(response.TryDeserializeBadRequestResponse(out var badResult)); + Assert.AreEqual(model, badResult); + + Assert.AreEqual(model, response.Unauthorized()); + Assert.IsTrue(response.TryDeserializeUnauthorizedResponse(out var unAuthResult)); + Assert.AreEqual(model, unAuthResult); + + Assert.AreEqual(model, response.Forbidden()); + Assert.IsTrue(response.TryDeserializeForbiddenResponse(out var forbiddenResult)); + Assert.AreEqual(model, forbiddenResult); + + Assert.AreEqual(model, response.TooManyRequests()); + Assert.IsTrue(response.TryDeserializeTooManyRequestsResponse(out var tooManyResult)); + Assert.AreEqual(model, tooManyResult); + + Assert.AreEqual(model, response.NotFound()); + Assert.IsTrue(response.TryDeserializeNotFoundResponse(out var notFoundResult)); + Assert.AreEqual(model, notFoundResult); + + Assert.AreEqual(model, response.UnprocessableContent()); + Assert.IsTrue(response.TryDeserializeUnprocessableContentResponse(out var unprocessableResult)); + Assert.AreEqual(model, unprocessableResult); + + Assert.AreEqual(model, response.InternalServerError()); + Assert.IsTrue(response.TryDeserializeInternalServerErrorResponse(out var internalResult)); + Assert.AreEqual(model, internalResult); + } + } +} diff --git a/Adyen.Test/Core/Client/HttpRequestMessageExtensions.cs b/Adyen.Test/Core/Client/HttpRequestMessageExtensions.cs new file mode 100644 index 000000000..a7652c36e --- /dev/null +++ b/Adyen.Test/Core/Client/HttpRequestMessageExtensions.cs @@ -0,0 +1,95 @@ +using Adyen.Core.Client.Extensions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Adyen.Test.Core.Client +{ + [TestClass] + public class HttpRequestMessageExtensionsTest + { + [TestMethod] + public void Given_AddUserAgentToHeaders_When_ApplicationName_Is_Null_Returns_adyen_dotnet_api_library_And_AdyenLibraryVersion() + { + // Arrange + HttpRequestMessageExtensions.ApplicationName = null; + var request = new HttpRequestMessage(); + + // Act + request.AddUserAgentToHeaders(); + + // Assert + string target = request.Headers.GetValues("UserAgent").First(); + Assert.AreEqual($"adyen-dotnet-api-library/{HttpRequestMessageExtensions.AdyenLibraryVersion}", target); + } + + [TestMethod] + public void Given_AddUserAgentToHeaders_When_ApplicationName_Is_Set_Returns_MyApp_adyen_dotnet_api_library_And_AdyenLibraryVersion() + { + // Arrange + var request = new HttpRequestMessage(); + HttpRequestMessageExtensions.ApplicationName = "MyApp"; + + // Act + request.AddUserAgentToHeaders(); + + // Assert + string target = request.Headers.GetValues("UserAgent").First(); + Assert.AreEqual($"MyApp adyen-dotnet-api-library/{HttpRequestMessageExtensions.AdyenLibraryVersion}", target); + } + + [TestMethod] + public void Given_AddLibraryNameToHeader_When_Provided_Returns_adyen_dotnet_api_library() + { + // Arrange + HttpRequestMessageExtensions.ApplicationName = null; + var request = new HttpRequestMessage(); + + // Act + request.AddLibraryNameToHeader(); + + // Assert + string target = request.Headers.GetValues("adyen-library-name").First(); + Assert.AreEqual("adyen-dotnet-api-library", target); + } + + [TestMethod] + public void Given_AddLibraryNameToHeader_When_Provided_Returns_AdyenLibraryVersion() + { + // Arrange + HttpRequestMessageExtensions.ApplicationName = null; + var request = new HttpRequestMessage(); + + // Act + request.AddLibraryVersionToHeader(); + + // Assert + string target = request.Headers.GetValues("adyen-library-version").First(); + Assert.AreEqual(HttpRequestMessageExtensions.AdyenLibraryVersion, target); + } + + [TestMethod] + public void Given_AddUserAgentToHeaders_When_Called_MultipleTimes_Should_Not_Throw_Any_Exceptions() + { + // Arrange + HttpRequestMessageExtensions.ApplicationName = null; + var request = new HttpRequestMessage(); + + // Act + // Assert + try + { + request.AddUserAgentToHeaders(); + request.AddUserAgentToHeaders(); + + request.AddLibraryNameToHeader(); + request.AddLibraryNameToHeader(); + + request.AddLibraryVersionToHeader(); + request.AddLibraryVersionToHeader(); + } + catch (Exception e) + { + Assert.Fail(); + } + } + } +} \ No newline at end of file diff --git a/Adyen.Test/Core/Client/UrlBuilderExtensionsTest.cs b/Adyen.Test/Core/Client/UrlBuilderExtensionsTest.cs new file mode 100644 index 000000000..aeadc3c3a --- /dev/null +++ b/Adyen.Test/Core/Client/UrlBuilderExtensionsTest.cs @@ -0,0 +1,154 @@ +using Adyen.Core.Client; +using Adyen.Core.Options; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Adyen.Test.Core.Client +{ + [TestClass] + public class UrlBuilderExtensionsTest + { + #region Checkout && POS-SDK + [TestMethod] + public async Task Given_ConstructHostUrl_When_Environment_Is_Live_Then_CheckoutUrl_Contains_Prefix_And_Live_String() + { + // Arrange + AdyenOptions adyenOptions = new AdyenOptions() + { + Environment = AdyenEnvironment.Live, + LiveEndpointUrlPrefix = "prefix", + }; + + // Act + string target = UrlBuilderExtensions.ConstructHostUrl(adyenOptions, "https://checkout-test.adyen.com/v71"); + + // Assert + Assert.AreEqual("https://prefix-checkout-live.adyenpayments.com/checkout/v71", target); + } + + [TestMethod] + public async Task Given_ConstructHostUrl_When_Environment_Is_Live_And_No_Prefix_For_Checkout_Throws_InvalidOperationException() + { + // Arrange + AdyenOptions adyenOptions = new AdyenOptions() + { + Environment = AdyenEnvironment.Live, + LiveEndpointUrlPrefix = null + }; + + // Act + // Assert + Assert.Throws(() => UrlBuilderExtensions.ConstructHostUrl(adyenOptions, "https://checkout-test.adyen.com/v71")); + } + + #endregion + + [TestMethod] + public async Task Given_ConstructHostUrl_When_Environment_Is_Live_Then_Checkout_POS_SDK_Contains_Prefix_And_Live_String_Without_Checkout() + { + // Arrange + AdyenOptions adyenOptions = new AdyenOptions() + { + Environment = AdyenEnvironment.Live, + LiveEndpointUrlPrefix = "prefix", + }; + + // Act + string target = UrlBuilderExtensions.ConstructHostUrl(adyenOptions, "https://checkout-test.adyen.com/checkout/possdk/v68"); + + // Assert + Assert.AreEqual("https://prefix-checkout-live.adyenpayments.com/checkout/possdk/v68", target); + } + + + #region Pal + [TestMethod] + public async Task Given_ConstructHostUrl_When_Environment_Is_Live_Then_PalUrl_Contains_Prefix_And_Live_String() + { + // Arrange + AdyenOptions adyenOptions = new AdyenOptions() + { + Environment = AdyenEnvironment.Live, + LiveEndpointUrlPrefix = "prefix", + }; + + // Act + string target = UrlBuilderExtensions.ConstructHostUrl(adyenOptions, "https://pal-test.adyen.com/pal/servlet/Payment/v68"); + + // Assert + Assert.AreEqual("https://prefix-pal-live.adyenpayments.com/pal/servlet/Payment/v68", target); + } + + [TestMethod] + public async Task Given_ConstructHostUrl_When_Environment_Is_Live_And_No_Prefix_For_Pal_Throws_InvalidOperationException() + { + // Arrange + AdyenOptions adyenOptions = new AdyenOptions() + { + Environment = AdyenEnvironment.Live, + LiveEndpointUrlPrefix = null + }; + + // Act + // Assert + Assert.Throws(() => UrlBuilderExtensions.ConstructHostUrl(adyenOptions, "https://pal-test.adyen.com/pal/servlet/Payment/v68")); + } + + #endregion + + + #region SessionAuthentication + [TestMethod] + public async Task Given_ConstructHostUrl_When_Environment_Is_Live_Then_SessionAuthentication_Url_Contains_Prefix_And_Live_String() + { + // Arrange + AdyenOptions adyenOptions = new AdyenOptions() + { + Environment = AdyenEnvironment.Live, + LiveEndpointUrlPrefix = "prefix", + }; + + // Act + string target = UrlBuilderExtensions.ConstructHostUrl(adyenOptions, "https://test.adyen.com/authe/api/v1"); + + // Assert + Assert.AreEqual("https://authe-live.adyen.com/authe/api/v1", target); + } + + #endregion + + [TestMethod] + public async Task Given_ConstructHostUrl_When_Environment_Is_Test_And_Prefix_Given_Then_Url_Does_Not_Contain_Prefix() + { + // Arrange + AdyenOptions adyenOptions = new AdyenOptions() + { + Environment = AdyenEnvironment.Test, + LiveEndpointUrlPrefix = "prefix", + }; + + // Act + string target = UrlBuilderExtensions.ConstructHostUrl(adyenOptions, "https://test.adyen.com"); + + // Assert + Assert.IsFalse(target.Contains("prefix")); + } + + [TestMethod] + public async Task Given_ConstructHostUrl_When_Environment_Is_Live_And_No_Prefix_Does_Not_Throw_InvalidOperationException_And_Contains_String_Value_Test() + { + // Arrange + AdyenOptions adyenOptions = new AdyenOptions() + { + Environment = AdyenEnvironment.Live, + LiveEndpointUrlPrefix = null + }; + + // Act + string target = UrlBuilderExtensions.ConstructHostUrl(adyenOptions, "https://test.adyen.com/"); + + // Assert + Assert.IsTrue(target.Contains("test")); + } + + } +} \ No newline at end of file diff --git a/Adyen.Test/Core/Converters/ByteArrayTest.cs b/Adyen.Test/Core/Converters/ByteArrayTest.cs new file mode 100644 index 000000000..542775b8e --- /dev/null +++ b/Adyen.Test/Core/Converters/ByteArrayTest.cs @@ -0,0 +1,114 @@ +using System.Text; +using System.Text.Json; +using Adyen.Core.Converters; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Adyen.Test.Core.Converters +{ + [TestClass] + public class ByteArrayTest + { + private readonly ByteArrayConverter _converter = new ByteArrayConverter(); + + [TestMethod] + public void Given_ByteArray_When_Write_Then_Result_Should_Write_UTF8_String() + { + // Arrange + byte[] bytes = Encoding.UTF8.GetBytes("adyen"); + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + + // Act + _converter.Write(writer, bytes, new JsonSerializerOptions()); + writer.Flush(); + string target = Encoding.UTF8.GetString(stream.ToArray()); + + // Assert + Assert.AreEqual("\"adyen\"", target); + } + + [TestMethod] + public void Given_ByteArray_Null_When_Write_Then_Result_Returns_Null() + { + // Arrange + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + + // Act + _converter.Write(writer, null, new JsonSerializerOptions()); + writer.Flush(); + string json = Encoding.UTF8.GetString(stream.ToArray()); + + // Assert + Assert.AreEqual("null", json); + } + + [TestMethod] + public void Given_Json_When_Read_And_Decoded_Then_Should_Return_ByteArray() + { + // Arrange + string json = "\"adyen\""; + var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json)); + reader.Read(); + + // Act + byte[] result = _converter.Read(ref reader, typeof(byte[]), new JsonSerializerOptions()); + string decoded = Encoding.UTF8.GetString(result); + + // Assert + Assert.AreEqual("adyen", decoded); + } + + [TestMethod] + public void Given_Json_Null_When_Read_Then_Returns_Null() + { + // Arrange + string json = "null"; + var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json)); + reader.Read(); + + // Act + byte[] result = _converter.Read(ref reader, typeof(byte[]), new JsonSerializerOptions()); + + // Assert + Assert.IsNull(result); + } + + [TestMethod] + public void Given_ByteArray_When_Write_And_Read_Then_Result_Returns_Original_Bytes() + { + // Arrange + byte[] original = Encoding.UTF8.GetBytes("adyen"); + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + + _converter.Write(writer, original, new JsonSerializerOptions()); + writer.Flush(); + string json = Encoding.UTF8.GetString(stream.ToArray()); + + var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json)); + reader.Read(); + + // Act + byte[] result = _converter.Read(ref reader, typeof(byte[]), new JsonSerializerOptions()); + + // Assert + CollectionAssert.AreEqual(original, result); + } + + [TestMethod] + public void Given_EmptyString_When_Read_Then_ShouldReturnEmptyByteArray() + { + // Arrange + string json = "\"\""; + Utf8JsonReader reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json)); + reader.Read(); + + // Act + byte[] result = _converter.Read(ref reader, typeof(byte[]), new JsonSerializerOptions()); + + // Assert + Assert.AreEqual(Array.Empty(), result); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/Core/Converters/DateOnlyJsonConverterTest.cs b/Adyen.Test/Core/Converters/DateOnlyJsonConverterTest.cs new file mode 100644 index 000000000..1c7013897 --- /dev/null +++ b/Adyen.Test/Core/Converters/DateOnlyJsonConverterTest.cs @@ -0,0 +1,105 @@ +using System.Text.Json; +using Adyen.Core.Converters; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Adyen.Test.Core.Converters +{ + [TestClass] + public class DateOnlyJsonConverterTests + { + private readonly DateOnlyJsonConverter _converter = new DateOnlyJsonConverter(); + + [TestMethod] + public void Given_DateWithDashes_yyyy_MM_dd_When_Read_Then_ReturnsCorrectDate() + { + // Arrange + string json = "\"2025-12-25\""; + var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(json)); + + // Act + reader.Read(); + var result = _converter.Read(ref reader, typeof(DateOnly), new JsonSerializerOptions()); + + // Assert + Assert.AreEqual(new DateOnly(2025, 12, 25), result); + } + + [TestMethod] + public void Given_Date_yyyyMMdd_When_Read_Then_ReturnsCorrectDate() + { + // Arrange + string json = "\"20251225\""; + var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(json)); + + // Act + reader.Read(); + var result = _converter.Read(ref reader, typeof(DateOnly), new JsonSerializerOptions()); + + // Assert + Assert.AreEqual(new DateOnly(2025, 12, 25), result); + } + + [TestMethod] + public void Given_WrongFormatDateOnlyString_When_Read_Then_ThrowsNotSupportedException() + { + // Arrange + string json = "\"25-12-2025\""; // Incorrect format dd-MM-yyyy + + // Act + // Assert + Assert.ThrowsException(() => + { + var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(json)); + reader.Read(); + _converter.Read(ref reader, typeof(DateOnly), new JsonSerializerOptions()); + }); + } + [TestMethod] + public void Given_InvalidDateOnlyString_When_Read_Then_ThrowsJsonException() + { + // Arrange + string json = "invalid-date"; + + // Act + // Assert + Assert.Throws(() => + { + var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(json)); + reader.Read(); + _converter.Read(ref reader, typeof(DateOnly), new JsonSerializerOptions()); + }); + } + + [TestMethod] + public void Given_NullToken_When_Read_Then_ThrowsNotSupportedException() + { + // Arrange + string json = "null"; + + // Act + // Assert + Assert.ThrowsException(() => { + Utf8JsonReader reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(json)); + reader.Read(); + _converter.Read(ref reader, typeof(DateOnly), new JsonSerializerOptions()); + }); + } + + [TestMethod] + public void Given_DateOnlyValue_When_Write_Then_WritesCorrectDateOnlyValue() + { + // Arrange + var date = new DateOnly(2025, 12, 25); + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + + // Act + _converter.Write(writer, date, new JsonSerializerOptions()); + writer.Flush(); + string json = System.Text.Encoding.UTF8.GetString(stream.ToArray()); + + // Assert + Assert.AreEqual("\"2025-12-25\"", json); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/Core/Converters/DateOnlyNullableJsonConverterTest.cs b/Adyen.Test/Core/Converters/DateOnlyNullableJsonConverterTest.cs new file mode 100644 index 000000000..8f75f6470 --- /dev/null +++ b/Adyen.Test/Core/Converters/DateOnlyNullableJsonConverterTest.cs @@ -0,0 +1,106 @@ +using System.Text.Json; +using Adyen.Core.Converters; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Adyen.Test.Core.Converters +{ + [TestClass] + public class DateOnlyNullableJsonConverterTest + { + private readonly DateOnlyNullableJsonConverter _converter = new DateOnlyNullableJsonConverter(); + + [TestMethod] + public void Given_DateWithDashes_yyyy_MM_dd_When_Read_Then_ReturnsCorrectDate() + { + // Arrange + string json = "\"2025-12-25\""; + var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(json)); + + // Act + reader.Read(); + var result = _converter.Read(ref reader, typeof(DateOnly), new JsonSerializerOptions()); + + // Assert + Assert.AreEqual(new DateOnly(2025, 12, 25), result); + } + + [TestMethod] + public void Given_Date_yyyyMMdd_When_Read_Then_ReturnsCorrectDate() + { + // Arrange + string json = "\"20251225\""; + var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(json)); + + // Act + reader.Read(); + var result = _converter.Read(ref reader, typeof(DateOnly), new JsonSerializerOptions()); + + // Assert + Assert.AreEqual(new DateOnly(2025, 12, 25), result); + } + + [TestMethod] + public void Given_WrongFormatDateOnlyString_When_Read_Then_ThrowsNotSupportedException() + { + // Arrange + string json = "\"25-12-2025\""; // Incorrect format dd-MM-yyyy + + // Act + // Assert + Assert.ThrowsException(() => + { + var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(json)); + reader.Read(); + _converter.Read(ref reader, typeof(DateOnly), new JsonSerializerOptions()); + }); + } + + [TestMethod] + public void Given_InvalidDateOnlyString_When_Read_Then_ThrowsJsonException() + { + // Arrange + string json = "invalid-date"; + + // Act + // Assert + Assert.Throws(() => + { + var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(json)); + reader.Read(); + _converter.Read(ref reader, typeof(DateOnly), new JsonSerializerOptions()); + }); + } + + [TestMethod] + public void Given_NullToken_When_Read_Then_ThrowsNotSupportedException() + { + // Arrange + string json = "null"; + + // Act + Utf8JsonReader reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(json)); + reader.Read(); + var result = _converter.Read(ref reader, typeof(DateOnly), new JsonSerializerOptions()); + + // Assert + Assert.IsNull(result); + } + + [TestMethod] + public void Given_DateOnlyValue_When_Write_Then_WritesCorrectDateOnlyValue() + { + // Arrange + DateOnly? dateOnly = null; + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + + // Act + _converter.Write(writer, dateOnly, new JsonSerializerOptions()); + writer.Flush(); + string json = System.Text.Encoding.UTF8.GetString(stream.ToArray()); + + // Assert + Assert.AreEqual("null", json); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/Core/Converters/DateTimeJsonConverterTest.cs b/Adyen.Test/Core/Converters/DateTimeJsonConverterTest.cs new file mode 100644 index 000000000..68938e00c --- /dev/null +++ b/Adyen.Test/Core/Converters/DateTimeJsonConverterTest.cs @@ -0,0 +1,76 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Text.Json; +using Adyen.Core.Converters; + +namespace Adyen.Test.Core.Converters +{ + [TestClass] + public class DateTimeJsonConverterTest + { + private readonly DateTimeJsonConverter _converter = new DateTimeJsonConverter(); + + [TestMethod] + public void Given_ValidDateTimeWithFraction_When_Read_Then_ReturnsCorrectDateTime() + { + // Arrange + string json = "\"2025-12-25T14:30:15.1234567Z\""; + var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(json)); + + // Act + reader.Read(); + var result = _converter.Read(ref reader, typeof(DateTime), new JsonSerializerOptions()); + + // Assert + Assert.AreEqual(DateTime.Parse("2025-12-25T14:30:15.1234567Z").ToUniversalTime(), result); + } + + [TestMethod] + public void Given_ValidDateTimeWithoutFraction_When_Read_Then_ReturnsCorrectDateTime() + { + // Arrange + string json = "\"2025-12-25T14:30:15Z\""; + var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(json)); + + // Act + reader.Read(); + var result = _converter.Read(ref reader, typeof(DateTime), new JsonSerializerOptions()); + + // Assert + Assert.AreEqual(DateTime.Parse("2025-12-25T14:30:15Z").ToUniversalTime(), result); + } + + [TestMethod] + public void Given_InvalidDateTime_When_Read_Then_ThrowsNotSupportedException() + { + // Arrange + string json = "\"invalid-datetime\""; + + // Act + // Assert + Assert.ThrowsException(() => + { + var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(json)); + reader.Read(); + _converter.Read(ref reader, typeof(DateTime), new JsonSerializerOptions()); + }); + } + + [TestMethod] + public void Given_DateTimeValue_When_Write_Then_WritesCorrectJson() + { + // Arrange + var dateTime = new DateTime(2025, 12, 25, 14, 30, 15, 123, DateTimeKind.Utc).AddTicks(4567); + + // Act + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + _converter.Write(writer, dateTime, new JsonSerializerOptions()); + writer.Flush(); + string json = System.Text.Encoding.UTF8.GetString(stream.ToArray()); + + // Assert + Assert.AreEqual($"\"{dateTime.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK", System.Globalization.CultureInfo.InvariantCulture)}\"", json); + } + } +} diff --git a/Adyen.Test/Core/Converters/DateTimeNullableJsonConverterTest.cs b/Adyen.Test/Core/Converters/DateTimeNullableJsonConverterTest.cs new file mode 100644 index 000000000..dc528d0a2 --- /dev/null +++ b/Adyen.Test/Core/Converters/DateTimeNullableJsonConverterTest.cs @@ -0,0 +1,111 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Globalization; +using System.Text; +using System.Text.Json; +using Adyen.Core.Converters; + +namespace Adyen.Test.Core.Converters +{ + [TestClass] + public class DateTimeNullableJsonConverterTests + { + private readonly DateTimeNullableJsonConverter _converter = new(); + + [TestMethod] + public void Given_ValidDateTimeWithFraction_When_Read_Then_ReturnsCorrectDateTime() + { + // Arrange + string json = "\"2025-12-25T14:30:15.1234567Z\""; + + // Act + var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(json)); + reader.Read(); + var result = _converter.Read(ref reader, typeof(DateTime?), new JsonSerializerOptions()); + + // Assert + Assert.AreEqual(DateTime.Parse("2025-12-25T14:30:15.1234567Z").ToUniversalTime(), result); + } + + [TestMethod] + public void Given_ValidDateTimeWithoutFraction_When_Read_Then_ReturnsCorrectDateTime() + { + // Arrange + string json = "\"2025-12-25T14:30:15Z\""; + + // Act + var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json)); + reader.Read(); + var result = _converter.Read(ref reader, typeof(DateTime?), new JsonSerializerOptions()); + + // Assert + Assert.AreEqual(DateTime.Parse("2025-12-25T14:30:15Z").ToUniversalTime(), result); + } + + [TestMethod] + public void Given_NullJson_When_Read_Then_ReturnsNull() + { + // Arrange + string json = "null"; + + // Act + var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json)); + reader.Read(); + var result = _converter.Read(ref reader, typeof(DateTime?), new JsonSerializerOptions()); + + // Assert + Assert.IsNull(result); + } + + [TestMethod] + public void Given_InvalidDateTime_When_Read_Then_ReturnsNull() + { + // Arrange + string json = "\"invalid-datetime\""; + + // Act + var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(json)); + reader.Read(); + var result = _converter.Read(ref reader, typeof(DateTime?), new JsonSerializerOptions()); + + // Assert + Assert.IsNull(result); + } + + [TestMethod] + public void Given_DateTimeValue_When_Write_Then_WritesCorrectJson() + { + // Arrange + var dateTime = new DateTime(2025, 12, 25, 14, 30, 15, 123, DateTimeKind.Utc).AddTicks(4567); + + // Act + using var stream = new System.IO.MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + + _converter.Write(writer, dateTime, new JsonSerializerOptions()); + writer.Flush(); + string json = Encoding.UTF8.GetString(stream.ToArray()); + + // Assert + Assert.AreEqual($"\"{dateTime.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK", CultureInfo.InvariantCulture)}\"", json); + } + + [TestMethod] + public void Given_NullDateTime_When_Write_Then_WritesNullJson() + { + // Arrange + DateTime? dateTime = null; + + // Act + using var stream = new System.IO.MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + + _converter.Write(writer, dateTime, new JsonSerializerOptions()); + writer.Flush(); + string json = Encoding.UTF8.GetString(stream.ToArray()); + + // Assert + Assert.AreEqual("null", json); + } + } +} diff --git a/Adyen.Test/Core/IEnumTest.cs b/Adyen.Test/Core/IEnumTest.cs new file mode 100644 index 000000000..1923dea98 --- /dev/null +++ b/Adyen.Test/Core/IEnumTest.cs @@ -0,0 +1,359 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Adyen.Core; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Adyen.Test.Core +{ + [TestClass] + public class IEnumTest + { + [TestMethod] + public async Task Given_Enums_When_Equal_Returns_Correct_True() + { + ExampleEnum? nullEnum = null; + + Assert.AreEqual(nullEnum, nullEnum); + Assert.AreEqual(ExampleEnum.A, ExampleEnum.A); + Assert.AreEqual(ExampleEnum.B, ExampleEnum.B); + } + + [TestMethod] + public async Task Given_Enums_When_NotEqual_Returns_Correct_False() + { + ExampleEnum? nullEnum = null; + + Assert.AreNotEqual(ExampleEnum.A, ExampleEnum.B); + Assert.AreNotEqual(ExampleEnum.B, nullEnum); + Assert.AreNotEqual(ExampleEnum.A, nullEnum); + } + + [TestMethod] + public async Task Given_ImplicitConversion_When_Initialized_Then_Returns_Correct_Values() + { + ExampleEnum? resultA = "a"; + ExampleEnum? resultB = "b"; + + Assert.AreEqual(ExampleEnum.A, resultA); + Assert.AreEqual(ExampleEnum.B, resultB); + } + + [TestMethod] + public async Task Given_ImplicitConversion_When_Null_Then_Returns_Null() + { + ExampleEnum? input = null; + string? result = input; + + Assert.IsNull(result); + } + + [TestMethod] + public async Task Given_ToString_When_Called_Then_Returns_Correct_Value() + { + Assert.AreEqual("a", ExampleEnum.A.ToString()); + Assert.AreEqual("b", ExampleEnum.B.ToString()); + } + + [TestMethod] + public async Task Given_ToString_When_Null_Then_Returns_Empty_String() + { + ExampleEnum result = ExampleEnum.FromStringOrDefault("this-is-not-a-valid-enum"); + + Assert.IsNull(result); + } + + [TestMethod] + public async Task Given_Equals_When_ComparingCaseInsensitive_Then_Returns_True() + { + ExampleEnum result = ExampleEnum.A; + + Assert.IsTrue(result.Equals(ExampleEnum.A)); + Assert.IsFalse(result.Equals(ExampleEnum.B)); + } + + [TestMethod] + public async Task Given_FromStringOrDefault_When_InvalidString_Then_Returns_Null() + { + Assert.IsNull(ExampleEnum.FromStringOrDefault("this-is-not-a-valid-enum")); + } + + [TestMethod] + public async Task Given_EqualityOperator_When_ComparingValues_Then_Returns_Correct_Values() + { + ExampleEnum target = ExampleEnum.A; + ExampleEnum otherA = ExampleEnum.A; + ExampleEnum otherB = ExampleEnum.B; + + Assert.IsTrue(target == otherA); + Assert.IsFalse(target == otherB); + Assert.IsTrue(target != otherB); + Assert.IsFalse(target != otherA); + } + + [TestMethod] + public async Task Given_FromStringOrDefault_When_ValidStrings_Then_Returns_Correct_Enum() + { + Assert.AreEqual(ExampleEnum.A, ExampleEnum.FromStringOrDefault("a")); + Assert.AreEqual(ExampleEnum.B, ExampleEnum.FromStringOrDefault("b")); + } + + [TestMethod] + public async Task Given_ToJsonValue_When_KnownEnum_Then_Returns_String() + { + Assert.AreEqual("a", ExampleEnum.ToJsonValue(ExampleEnum.A)); + Assert.AreEqual("b", ExampleEnum.ToJsonValue(ExampleEnum.B)); + } + + [TestMethod] + public async Task Given_ToJsonValue_When_Null_Then_Returns_Null() + { + Assert.IsNull(ExampleEnum.ToJsonValue(null)); + } + + [TestMethod] + public async Task Given_ToJsonValue_When_CustomEnum_Then_Returns_Null() + { + ExampleEnum custom = ExampleEnum.FromStringOrDefault("this-is-not-a-valid-enum"); + Assert.IsNull(ExampleEnum.ToJsonValue(custom)); + } + + [TestMethod] + public async Task Given_JsonSerialization_When_KnownEnum_Then_Serialize_and_Deserialize_Correctly() + { + JsonSerializerOptions options = new JsonSerializerOptions(); + options.Converters.Add(new ExampleEnum.ExampleJsonConverter()); + + string serializedA = JsonSerializer.Serialize(ExampleEnum.A, options); + string serializedB = JsonSerializer.Serialize(ExampleEnum.B, options); + + Assert.AreEqual("\"a\"", serializedA); + Assert.AreEqual("\"b\"", serializedB); + + ExampleEnum? deserializedA = JsonSerializer.Deserialize(serializedA, options); + ExampleEnum? deserializedB = JsonSerializer.Deserialize(serializedB, options); + + Assert.AreEqual(ExampleEnum.A, deserializedA); + Assert.AreEqual(ExampleEnum.B, deserializedB); + } + + [TestMethod] + public async Task Given_JsonSerialization_When_EnumNotInList_Then_Returns_Null() + { + var options = new JsonSerializerOptions(); + options.Converters.Add(new ExampleEnum.ExampleJsonConverter()); + + ExampleEnum? value = ExampleEnum.FromStringOrDefault("not-in-list"); + string serialized = JsonSerializer.Serialize(value, options); + Assert.AreEqual("null", serialized); + + ExampleEnum? deserialized = JsonSerializer.Deserialize(serialized, options); + Assert.AreEqual(null, deserialized); + } + + [TestMethod] + public async Task Given_JsonSerialization_When_Null_Value_Then_Serialize_And_Deserialize_Correctly() + { + var options = new JsonSerializerOptions(); + options.Converters.Add(new ExampleEnum.ExampleJsonConverter()); + + ExampleEnum? value = null; + string serialized = JsonSerializer.Serialize(value, options); + Assert.AreEqual("null", serialized); + + ExampleEnum? deserialized = JsonSerializer.Deserialize("null", options); + Assert.IsNull(deserialized); + } + + + #region Arrange ExampleModelResponse for testing (an example) model deserialization + + [TestMethod] + public async Task Given_JsonDeserialization_When_ExampleEnum_Is_Null_Then_Deserialize_Correctly_And_Not_Throw_Exception() + { + // Arrange + string json = @" +{ + ""exampleEnum"": null +}"; + var options = new JsonSerializerOptions(); + options.Converters.Add(new ExampleEnum.ExampleJsonConverter()); + options.Converters.Add(new ExampleModelResponse.ExampleModelResponseJsonConverter()); + + // Act + ExampleModelResponse result = JsonSerializer.Deserialize(json, options); + + // Assert + Assert.IsNull(result.ExampleEnum); + } + + [TestMethod] + public async Task Given_JsonDeserialization_When_ExampleEnum_Is_A_Then_Deserialize_Correctly_To_A() + { + // Arrange + string json = @" +{ + ""exampleEnum"": ""a"" +}"; + var options = new JsonSerializerOptions(); + options.Converters.Add(new ExampleEnum.ExampleJsonConverter()); + options.Converters.Add(new ExampleModelResponse.ExampleModelResponseJsonConverter()); + + // Act + ExampleModelResponse result = JsonSerializer.Deserialize(json, options); + + // Assert + Assert.AreEqual(ExampleEnum.A, result.ExampleEnum); + } + + internal class ExampleModelResponse + { + /// + /// The optional enum to test. + /// + [JsonPropertyName("exampleEnum")] + public ExampleEnum? ExampleEnum + { + get { return this._ExampleEnumOption; } + set { this._ExampleEnumOption = new(value); } + } + + /// + /// Used to track if an optional field is set. If so, set the . + /// + [JsonIgnore] + public Option _ExampleEnumOption { get; private set; } + + [JsonConstructor] + public ExampleModelResponse(Option exampleEnum) + { + this._ExampleEnumOption = exampleEnum; + } + + internal class ExampleModelResponseJsonConverter : JsonConverter + { + public override ExampleModelResponse Read(ref Utf8JsonReader utf8JsonReader, Type typeToConvert, JsonSerializerOptions jsonSerializerOptions) + { + int currentDepth = utf8JsonReader.CurrentDepth; + + if (utf8JsonReader.TokenType != JsonTokenType.StartObject && utf8JsonReader.TokenType != JsonTokenType.StartArray) + throw new JsonException(); + + JsonTokenType startingTokenType = utf8JsonReader.TokenType; + + Option exampleEnum = default; + + while (utf8JsonReader.Read()) + { + if (startingTokenType == JsonTokenType.StartObject && utf8JsonReader.TokenType == JsonTokenType.EndObject && currentDepth == utf8JsonReader.CurrentDepth) + break; + + if (startingTokenType == JsonTokenType.StartArray && utf8JsonReader.TokenType == JsonTokenType.EndArray && currentDepth == utf8JsonReader.CurrentDepth) + break; + + if (utf8JsonReader.TokenType == JsonTokenType.PropertyName && currentDepth == utf8JsonReader.CurrentDepth - 1) + { + string? jsonPropertyName = utf8JsonReader.GetString(); + utf8JsonReader.Read(); + + switch (jsonPropertyName) + { + case "exampleEnum": + exampleEnum = new Option(JsonSerializer.Deserialize(ref utf8JsonReader, jsonSerializerOptions)); + break; + default: + break; + } + } + } + + return new ExampleModelResponse(exampleEnum); + } + + public override void Write(Utf8JsonWriter writer, ExampleModelResponse response, JsonSerializerOptions jsonSerializerOptions) + { + writer.WritePropertyName("exampleEnum"); + JsonSerializer.Serialize(writer, response.ExampleEnum, jsonSerializerOptions); + } + } + } + + #endregion + } + + #region Arrange ExampleEnum for testing + + [JsonConverter(typeof(ExampleJsonConverter))] + internal class ExampleEnum : IEnum + { + public string? Value { get; set; } + + /// + /// ExampleEnum.A: a + /// + public static readonly ExampleEnum A = new("a"); + + /// + /// ExampleEnum.B: b + /// + public static readonly ExampleEnum B = new("b"); + + private ExampleEnum(string? value) + { + Value = value; + } + + public static implicit operator ExampleEnum?(string? value) => value == null ? null : new ExampleEnum(value); + + public static implicit operator string?(ExampleEnum? option) => option?.Value; + + public override string ToString() => Value ?? string.Empty; + + public override bool Equals(object? obj) => obj is ExampleEnum other && string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + public override int GetHashCode() => Value?.GetHashCode() ?? 0; + + public static bool operator ==(ExampleEnum? left, ExampleEnum? right) => + string.Equals(left?.Value, right?.Value, StringComparison.OrdinalIgnoreCase); + + public static bool operator !=(ExampleEnum? left, ExampleEnum? right) => + !string.Equals(left?.Value, right?.Value, StringComparison.OrdinalIgnoreCase); + + public static ExampleEnum? FromStringOrDefault(string value) + { + return value switch { + "a" => ExampleEnum.A, + "b" => ExampleEnum.B, + _ => null, + }; + } + + public static string? ToJsonValue(ExampleEnum? value) + { + if (value == null) + return null; + + if (value == ExampleEnum.A) + return "a"; + + if (value == ExampleEnum.B) + return "b"; + + return null; + } + + public class ExampleJsonConverter : JsonConverter + { + public override ExampleEnum? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions jsonOptions) + { + string str = reader.GetString(); + return str == null ? null : ExampleEnum.FromStringOrDefault(str) ?? new ExampleEnum(str); + } + + public override void Write(Utf8JsonWriter writer, ExampleEnum value, JsonSerializerOptions jsonOptions) + { + writer.WriteStringValue(ExampleEnum.ToJsonValue(value)); + } + } + } + #endregion +} \ No newline at end of file diff --git a/Adyen.Test/DataProtection/DataProtectionTest.cs b/Adyen.Test/DataProtection/DataProtectionTest.cs new file mode 100644 index 000000000..d120b20e2 --- /dev/null +++ b/Adyen.Test/DataProtection/DataProtectionTest.cs @@ -0,0 +1,41 @@ +using Adyen.Core.Options; +using Adyen.DataProtection.Client; +using Adyen.DataProtection.Models; +using Adyen.DataProtection.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Text.Json; + +namespace Adyen.Test.DataProtection +{ + [TestClass] + public class DataProtectionTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public DataProtectionTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureDataProtection((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_Deserialize_When_SubjectErasureResponse_Returns_Not_Null_And_Correct_Enum() + { + string json = TestUtilities.GetTestFileContent("mocks/data-protection-response.json"); + + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + Assert.AreEqual(response.Result, SubjectErasureResponse.ResultEnum.ACTIVERECURRINGTOKENEXISTS); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/DataProtectionTest.cs b/Adyen.Test/DataProtectionTest.cs deleted file mode 100644 index 84d5eca6f..000000000 --- a/Adyen.Test/DataProtectionTest.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Adyen.Model.DataProtection; -using Adyen.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Adyen.Test -{ - [TestClass] - public class DataProtectionTest : BaseTest - { - [TestMethod] - public void TestRequestSubjectErasure() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/data-protection-response.json"); - var service = new DataProtectionService(client); - var response = service.RequestSubjectErasure(new SubjectErasureByPspReferenceRequest()); - Assert.AreEqual(response.Result, SubjectErasureResponse.ResultEnum.ACTIVERECURRINGTOKENEXISTS); - } - } -} \ No newline at end of file diff --git a/Adyen.Test/Disputes/DisputesTest.cs b/Adyen.Test/Disputes/DisputesTest.cs new file mode 100644 index 000000000..9bcff9e7c --- /dev/null +++ b/Adyen.Test/Disputes/DisputesTest.cs @@ -0,0 +1,106 @@ +using System.Text.Json; +using Adyen.Core.Options; +using Adyen.Disputes.Client; +using Adyen.Disputes.Extensions; +using Adyen.Disputes.Models; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Adyen.Test.Disputes +{ + [TestClass] + public class DisputesTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public DisputesTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureDisputes((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_Deserialize_AcceptDispute_Returns_Success() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/disputes/accept-dispute.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.IsTrue(response.DisputeServiceResult.Success); + } + + [TestMethod] + public async Task Given_Deserialize_DefendDispute_Returns_Success() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/disputes/defend-dispute.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.IsTrue(response.DisputeServiceResult.Success); + } + + [TestMethod] + public async Task Given_Deserialize_DeleteDefenseDocumentResponse_Returns_Success() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/disputes/delete-dispute.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.IsTrue(response.DisputeServiceResult.Success); + } + + [TestMethod] + public async Task Given_Deserialize_DefenseReasonsResponse_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/disputes/retrieve-applicable-defense-reasons.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.IsTrue(response.DisputeServiceResult.Success); + Assert.IsTrue(response.DefenseReasons.Count!=0); + Assert.AreEqual(response.DefenseReasons[0].DefenseDocumentTypes[0].DefenseDocumentTypeCode, "TIDorInvoice"); + Assert.IsFalse(response.DefenseReasons[0].DefenseDocumentTypes[0].Available); + Assert.AreEqual(response.DefenseReasons[0].DefenseDocumentTypes[0].RequirementLevel, "Optional"); + Assert.AreEqual(response.DefenseReasons[0].DefenseDocumentTypes[1].DefenseDocumentTypeCode, "GoodsNotReturned"); + Assert.IsFalse(response.DefenseReasons[0].DefenseDocumentTypes[1].Available); + Assert.AreEqual(response.DefenseReasons[0].DefenseDocumentTypes[1].RequirementLevel, "Required"); + Assert.AreEqual(response.DefenseReasons[0].DefenseReasonCode, "GoodsNotReturned"); + Assert.IsFalse(response.DefenseReasons[0].Satisfied); + } + + [TestMethod] + public async Task Given_Deserialize_SupplyDefenceDocumentResponse_Returns_Success() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/disputes/supply-dispute-defense-document.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.IsTrue(response.DisputeServiceResult.Success); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/DisputesTest.cs b/Adyen.Test/DisputesTest.cs deleted file mode 100644 index e7cf1594c..000000000 --- a/Adyen.Test/DisputesTest.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Adyen.Model.Disputes; -using Adyen.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Adyen.Test -{ - [TestClass] - public class DisputesTest: BaseTest - { - [TestMethod] - public void AcceptDisputesSuccess() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/disputes/accept-disputes.json"); - var disputes = new DisputesService(client); - var response = disputes.AcceptDispute(new AcceptDisputeRequest()); - Assert.IsTrue(response.DisputeServiceResult.Success); - } - - [TestMethod] - public void DefendDisputesSuccess() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/disputes/defend-dispute.json"); - var disputes = new DisputesService(client); - var response = disputes.DefendDispute(new DefendDisputeRequest()); - Assert.IsTrue(response.DisputeServiceResult.Success); - } - - [TestMethod] - public void DeleteDisputesSuccess() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/disputes/delete-dispute.json"); - var disputes = new DisputesService(client); - var response = disputes.DeleteDisputeDefenseDocument(new DeleteDefenseDocumentRequest()); - Assert.IsTrue(response.DisputeServiceResult.Success); - } - - [TestMethod] - public void RetrieveApplicableDefenseReasonsSuccess() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/disputes/retrieve-appicable-defense-reasons.json"); - var disputes = new DisputesService(client); - var response = disputes.RetrieveApplicableDefenseReasons(new DefenseReasonsRequest()); - Assert.IsTrue(response.DisputeServiceResult.Success); - Assert.IsTrue(response.DefenseReasons.Count!=0); - Assert.AreEqual(response.DefenseReasons[0].DefenseDocumentTypes[0].DefenseDocumentTypeCode, "TIDorInvoice"); - Assert.IsFalse(response.DefenseReasons[0].DefenseDocumentTypes[0].Available); - Assert.AreEqual(response.DefenseReasons[0].DefenseDocumentTypes[0].RequirementLevel, "Optional"); - Assert.AreEqual(response.DefenseReasons[0].DefenseDocumentTypes[1].DefenseDocumentTypeCode, "GoodsNotReturned"); - Assert.IsFalse(response.DefenseReasons[0].DefenseDocumentTypes[1].Available); - Assert.AreEqual(response.DefenseReasons[0].DefenseDocumentTypes[1].RequirementLevel, "Required"); - Assert.AreEqual(response.DefenseReasons[0].DefenseReasonCode, "GoodsNotReturned"); - Assert.IsFalse(response.DefenseReasons[0].Satisfied); - } - - [TestMethod] - public void SupplyDisputesDefenceDocumentSuccess() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/disputes/supply-dispute-defense-document.json"); - var disputes = new DisputesService(client); - var response = disputes.SupplyDefenseDocument(new SupplyDefenseDocumentRequest()); - Assert.IsTrue(response.DisputeServiceResult.Success); - } - } -} \ No newline at end of file diff --git a/Adyen.Test/ExtensionsTest.cs b/Adyen.Test/ExtensionsTest.cs deleted file mode 100644 index defc01802..000000000 --- a/Adyen.Test/ExtensionsTest.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.Collections.Generic; -using System.Net.Http; -using Adyen.Model.BalanceControl; -using Adyen.Model.Checkout; -using Adyen.Service; -using Adyen.Service.BalancePlatform; -using Adyen.Service.Checkout; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using NSubstitute; -using Environment = Adyen.Model.Environment; - -namespace Adyen.Test -{ - [TestClass] - public class ExtensionsTest : BaseTest - { - private Client _client; - - [TestInitialize] - public void Init() - { - _client = CreateMockTestClientApiKeyBasedRequestAsync(""); - _client.Config.Environment = Environment.Live; - _client.Config.LiveEndpointUrlPrefix = "prefix"; - } - - [TestMethod] - public void TestDeviceRenderOptionsObjectListToString() - { - var deviceRenderOptions = new DeviceRenderOptions - { - SdkInterface = DeviceRenderOptions.SdkInterfaceEnum.Native, - SdkUiType = new List - { DeviceRenderOptions.SdkUiTypeEnum.MultiSelect, DeviceRenderOptions.SdkUiTypeEnum.OtherHtml } - }; - var expected = "\"multiSelect\""; - Assert.IsTrue(deviceRenderOptions.ToJson().Contains(expected)); - } - - [TestMethod] - public void TestPalLiveEndPoint() - { - var service = new BalanceControlService(_client); - service.BalanceTransfer(new BalanceTransferRequest()); - ClientInterfaceSubstitute.Received().RequestAsync( - "https://prefix-pal-live.adyenpayments.com/pal/servlet/BalanceControl/v1/balanceTransfer", - "{}", null, HttpMethod.Post, default); - } - - [TestMethod] - public void TestCheckoutLiveEndPoint() - { - var service = new DonationsService(_client); - service.Donations(new DonationPaymentRequest()); - ClientInterfaceSubstitute.Received().RequestAsync( - "https://prefix-checkout-live.adyenpayments.com/checkout/v71/donations", - Arg.Any(), null, HttpMethod.Post, default); - } - - [TestMethod] - public void TestBclLiveEndPoint() - { - var service = new AccountHoldersService(_client); - service.GetAllBalanceAccountsOfAccountHolder("id", offset: 3, limit: 5); - ClientInterfaceSubstitute.Received().RequestAsync( - "https://balanceplatform-api-live.adyen.com/bcl/v2/accountHolders/id/balanceAccounts?offset=3&limit=5", - null, null, HttpMethod.Get, default); - } - } -} diff --git a/Adyen.Test/GrantsTest.cs b/Adyen.Test/GrantsTest.cs deleted file mode 100644 index 3728fcc48..000000000 --- a/Adyen.Test/GrantsTest.cs +++ /dev/null @@ -1,22 +0,0 @@ - -using Adyen.Model.Transfers; -using Adyen.Service.Transfers; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Adyen.Test -{ - public class GrantsTest : BaseTest - { - [TestMethod] - public void StoredValueIssueTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/grants/post-grants-success.json"); - var service = new CapitalService(client); - var response = service.RequestGrantPayout(new CapitalGrantInfo()); - Assert.AreEqual(response.Id, "CG00000000000000000000001"); - Assert.AreEqual(response.GrantAccountId, "CG00000000000000000000001"); - Assert.AreEqual(response.Status, CapitalGrant.StatusEnum.Pending); - Assert.IsNotNull(response.Balances); - } - } -} \ No newline at end of file diff --git a/Adyen.Test/HttpClientWrapperTest.cs b/Adyen.Test/HttpClientWrapperTest.cs deleted file mode 100644 index 0704beec8..000000000 --- a/Adyen.Test/HttpClientWrapperTest.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Adyen.HttpClient; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Adyen.Test -{ - [TestClass] - public class HttpClientWrapperTest : BaseTest - { - [TestMethod] - public void EmptyRequestBodyPostTest() - { - var configWithApiKey = new Config - { - Environment = Model.Environment.Test, - XApiKey = "AQEyhmfxK....LAG84XwzP5pSpVd" - }; - var mockHttpMessageHandler = new MockHttpMessageHandler("{}", System.Net.HttpStatusCode.OK); - var httpClient = new System.Net.Http.HttpClient(mockHttpMessageHandler); - var httpClientWrapper = new HttpClientWrapper(configWithApiKey, httpClient); - var _ = httpClientWrapper.Request("https://test.com/testpath", null, null, HttpMethod.Post); - Assert.AreEqual("{}", mockHttpMessageHandler.Input); - } - - } -} \ No newline at end of file diff --git a/Adyen.Test/LegalEntityManagement/LegalEntityManagementTest.cs b/Adyen.Test/LegalEntityManagement/LegalEntityManagementTest.cs new file mode 100644 index 000000000..6dc6f03c6 --- /dev/null +++ b/Adyen.Test/LegalEntityManagement/LegalEntityManagementTest.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using Adyen.Checkout.Extensions; + +using Adyen.Core.Options; +using Adyen.LegalEntityManagement.Extensions; +using Adyen.LegalEntityManagement.Models; +using Adyen.LegalEntityManagement.Services; +using Adyen.LegalEntityManagement.Client; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NSubstitute; + +namespace Adyen.Test.LegalEntityManagement +{ + [TestClass] + public class LegalEntityManagementTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public LegalEntityManagementTest() + { + IHost testHost = Host.CreateDefaultBuilder() + .ConfigureLegalEntityManagement((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + _jsonSerializerOptionsProvider = testHost.Services.GetRequiredService(); + } + + /// + /// Test createDocument + /// + [TestMethod] + public void CreateDocument() + { + // Arrange + var json = TestUtilities.GetTestFileContent("mocks/legalentitymanagement/Document.json"); + + // Act + var result = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.IsNotNull(result); + } + // + // /// + // /// Test createDocument + // /// + // [TestMethod] + // public void UpdateDocument() + // { + // var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/legalentitymanagement/Document.json"); + // var service = new DocumentsService(client); + // var document = new Document + // { + // Attachment = new Attachment() + // }; + // var response = service.UpdateDocument("SE322KT223222D5FJ7TJN2986",document); + // Assert.AreEqual(Encoding.ASCII.GetString(response.Attachments[0].Content), "This is a string"); + // Assert.AreEqual(response.Id, "SE322KT223222D5FJ7TJN2986"); + // + // } + // + // /// + // /// Test deleteDocument + // /// + // [TestMethod] + // public void DeleteDocumentTest() + // { + // var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/legalentitymanagement/Document.json"); + // var service = new DocumentsService(client); + // service.DeleteDocument("SE322KT223222D5FJ7TJN2986"); + // + // } + // #endregion + // + // #region TransferInstruments + // /// + // /// Test createTransferInstruments + // /// + // [TestMethod] + // public void CreateTransferInstrumentsTest() + // { + // var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/legalentitymanagement/TransferInstrument.json"); + // var service = new TransferInstrumentsService(client); + // var transferInstrumentInfo = new TransferInstrumentInfo + // { + // LegalEntityId = "", + // Type = TransferInstrumentInfo.TypeEnum.BankAccount + // }; + // var response = service.CreateTransferInstrument(transferInstrumentInfo); + // Assert.AreEqual(response.LegalEntityId, "LE322KH223222D5GG4C9J83RN"); + // Assert.AreEqual(response.Id, "SE576BH223222F5GJVKHH6BDT"); + // } + // /// + // /// Test updateTransferInstruments + // /// + // [TestMethod] + // public void UpdateTransferInstrumentsTest() + // { + // var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/legalentitymanagement/TransferInstrument.json"); + // var service = new TransferInstrumentsService(client); + // var transferInstrumentInfo = new TransferInstrumentInfo + // { + // LegalEntityId = "", + // Type = TransferInstrumentInfo.TypeEnum.BankAccount + // }; + // var task = service.UpdateTransferInstrumentAsync("SE576BH223222F5GJVKHH6BDT", transferInstrumentInfo); + // var response = task.Result; + // Assert.AreEqual(response.LegalEntityId, "LE322KH223222D5GG4C9J83RN"); + // Assert.AreEqual(response.Id, "SE576BH223222F5GJVKHH6BDT"); + // } + // #endregion + // + // #region HostedOnboarding + // /// + // /// Test hostedOnboarding + // /// + // [TestMethod] + // public void ListThemesTest() + // { + // var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/legalentitymanagement/OnboardingThemes.json"); + // var service = new HostedOnboardingService(client); + // var task = service.ListHostedOnboardingPageThemesAsync(); + // var response = task.Result; + // Assert.AreEqual(response.Themes[0].Id, "SE322KT223222D5FJ7TJN2986"); + // Assert.AreEqual(response.Themes[0].CreatedAt, DateTime.Parse("2022-10-31T01:30:00+01:00")); + // } + // #endregion + // + // #region LegalEntities + // /// + // /// Test update LegalEntities + // /// + // [TestMethod] + // public void UpdateLegalEntitiesTest() + // { + // var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/legalentitymanagement/LegalEntity.json"); + // var service = new LegalEntitiesService(client); + // var legalEntityInfo = new LegalEntityInfo + // { + // Organization = new Organization(), + // Type = LegalEntityInfo.TypeEnum.Individual, + // EntityAssociations = new List(), + // SoleProprietorship = new SoleProprietorship() + // }; + // var response = service.UpdateLegalEntity("LE322JV223222D5GG42KN6869", legalEntityInfo); + // Assert.AreEqual(response.Id, "LE322JV223222D5GG42KN6869"); + // Assert.AreEqual(response.Type, LegalEntity.TypeEnum.Individual); + // } + // #endregion + // + // #region LegalEntities + // /// + // /// Test update BusinessLines + // /// + // [TestMethod] + // public void UpdateBusinessLinesTest() + // { + // var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/legalentitymanagement/BusinessLine.json"); + // var service = new BusinessLinesService(client); + // var businessLine = new BusinessLineInfoUpdate + // { + // IndustryCode = "124rrdfer", + // SourceOfFunds = new SourceOfFunds() + // }; + // var response = service.UpdateBusinessLine("SE322KT223222D5FJ7TJN2986", businessLine); + // Assert.AreEqual(response.Id, "SE322KT223222D5FJ7TJN2986"); + // Assert.AreEqual(response.IndustryCode, "55"); + // } + // #endregion + // + // #region TermsOfService + // /// + // /// Test get TermsOfService Status + // /// + // [TestMethod] + // public void GetTermsOfServiceStatus() + // { + // var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/legalentitymanagement/TermsOfServiceStatus.json"); + // var service = new TermsOfServiceService(client); + // var response = service.GetTermsOfServiceInformationForLegalEntity("id123"); + // ClientInterfaceSubstitute.Received().RequestAsync( + // "https://kyc-test.adyen.com/lem/v3/legalEntities/id123/termsOfServiceAcceptanceInfos", + // null, + // null, + // new HttpMethod("GET"), default); + // Assert.AreEqual(response.Data[0].Type, TermsOfServiceAcceptanceInfo.TypeEnum.AdyenIssuing); + // } + // #endregion + } +} diff --git a/Adyen.Test/Management/ManagementTest.cs b/Adyen.Test/Management/ManagementTest.cs new file mode 100644 index 000000000..c125d8cf6 --- /dev/null +++ b/Adyen.Test/Management/ManagementTest.cs @@ -0,0 +1,87 @@ +using Adyen.Core.Options; +using Adyen.Management.Client; +using Adyen.Management.Extensions; +using Adyen.Management.Models; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Text.Json; + +namespace Adyen.Test.Management +{ + [TestClass] + public class ManagementTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public ManagementTest() + { + IHost testHost = Host.CreateDefaultBuilder() + .ConfigureManagement((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + _jsonSerializerOptionsProvider = testHost.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_Deserialize_When_MeApiCredential_Result_Return_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/management/me.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("S2-6262224667", response.Id); + Assert.IsTrue(response.Active); + Assert.AreEqual("Management API - Users read and write", response.Roles[0]); + } + + [TestMethod] + public async Task Given_Deserialize_When_ListMerchantResponse_Returns_Correct_Data() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/management/list-merchant-accounts.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(22, response.ItemsTotal); + Assert.AreEqual("YOUR_MERCHANT_ACCOUNT_1", response.Data[0].Id); + } + + [TestMethod] + public async Task Given_Deserialize_When_UpdateResource_Returns_Data_String() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/management/logo.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("BASE-64_ENCODED_STRING_FROM_THE_REQUEST", response.Data); + } + + [TestMethod] + public async Task Given_Deserialize_When_ListTerminalsResponse_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/management/list-terminals.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(2, response.Data.Count); + Assert.AreEqual("V400m-080020970", response.Data.First(o => o.SerialNumber == "080-020-970").Id); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/ManagementTest.cs b/Adyen.Test/ManagementTest.cs deleted file mode 100644 index 010c79ba1..000000000 --- a/Adyen.Test/ManagementTest.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System.Linq; -using System.Net.Http; -using System.Threading.Tasks; -using Adyen.Model.Management; -using Adyen.Service.Management; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using NSubstitute; - -namespace Adyen.Test -{ - [TestClass] - public class ManagementTest : BaseTest - { - [TestMethod] - public void Me() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/management/me.json"); - var service = new MyAPICredentialService(client); - - var response = service.GetApiCredentialDetails(); - - Assert.AreEqual("S2-6262224667", response.Id); - Assert.IsTrue(response.Active); - Assert.AreEqual("Management API - Users read and write", response.Roles[0]); - } - - [TestMethod] - public void ListMerchantAccounts() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/management/list-merchant-accounts.json"); - var service = new AccountCompanyLevelService(client); - - var response = service.ListMerchantAccounts("ABC123", 1, 10); - - Assert.AreEqual(22, response.ItemsTotal); - Assert.AreEqual("YOUR_MERCHANT_ACCOUNT_1", response.Data[0].Id); - ClientInterfaceSubstitute.Received().RequestAsync( - "https://management-test.adyen.com/v3/companies/ABC123/merchants?pageNumber=1&pageSize=10", null, null, - HttpMethod.Get, default); - } - - [TestMethod] - public async Task UpdateResource() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/management/logo.json"); - var service = new TerminalSettingsCompanyLevelService(client); - - var logo = await service.UpdateTerminalLogoAsync("123ABC", "E355", new Logo("base64")); - - Assert.AreEqual("BASE-64_ENCODED_STRING_FROM_THE_REQUEST", logo.Data); - await ClientInterfaceSubstitute.Received().RequestAsync( - "https://management-test.adyen.com/v3/companies/123ABC/terminalLogos?model=E355", - Arg.Any(), - null, - new HttpMethod("PATCH"), default); - } - - [TestMethod] - public void ListTerminals() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/management/list-terminals.json"); - var service = new TerminalsTerminalLevelService(client); - - var terminals = service.ListTerminals(searchQuery: "ABC OR 123", pageSize: 2); - - Assert.AreEqual(2, terminals.Data.Count); - ClientInterfaceSubstitute.Received().RequestAsync( - "https://management-test.adyen.com/v3/terminals?searchQuery=ABC+OR+123&pageSize=2", - null, null, new HttpMethod("GET"), default); - var terminal = - from o in terminals.Data - where o.SerialNumber == "080-020-970" - select o; - Assert.AreEqual("V400m-080020970", terminal.First().Id); - } - } -} \ No newline at end of file diff --git a/Adyen.Test/MockOpenInvoicePayment.cs b/Adyen.Test/MockOpenInvoicePayment.cs deleted file mode 100644 index 36549db78..000000000 --- a/Adyen.Test/MockOpenInvoicePayment.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Collections.Generic; -using Adyen.Model.Payment; - -namespace Adyen.Test -{ - public class MockOpenInvoicePayment - { - public static PaymentRequest CreateOpenInvoicePaymentRequest() - { - - DateTime dateOfBirth = DateTime.Parse("1970-07-10"); - - PaymentRequest paymentRequest = MockPaymentData.CreateFullPaymentRequest(); - - // Set Shopper Data - paymentRequest.ShopperEmail = "youremail@email.com"; - paymentRequest.DateOfBirth = dateOfBirth; - paymentRequest.TelephoneNumber = "0612345678"; - paymentRequest.ShopperReference = "4"; - - // Set Shopper Info - Name shopperName = new Name - { - FirstName = "Testperson-nl", - LastName = "Approved" - }; - paymentRequest.ShopperName = shopperName; - - // Set Billing and Delivery address - Address address = new Address - { - City = "Gravenhage", - Country = "NL", - HouseNumberOrName = "1", - PostalCode = "2521VA", - StateOrProvince = "Zuid-Holland", - Street = "Neherkade" - }; - paymentRequest.DeliveryAddress = address; - paymentRequest.BillingAddress = address; - - // Use OpenInvoice Provider (klarna, ratepay) - paymentRequest.SelectedBrand = "klarna"; - - long itemAmount = long.Parse("9000"); - long itemVatAmount = long.Parse("1000"); - long itemVatPercentage = long.Parse("1000"); - - List invoiceLines = new List(); - - // invoiceLine1 - AdditionalDataOpenInvoice invoiceLine = new AdditionalDataOpenInvoice - { - OpeninvoicedataLineItemNrCurrencyCode = ("EUR"), - OpeninvoicedataLineItemNrDescription = ("Test product"), - OpeninvoicedataLineItemNrItemVatAmount = ("1000"), - OpeninvoicedataLineItemNrItemAmount = (itemAmount).ToString(), - OpeninvoicedataLineItemNrItemVatPercentage = (itemVatPercentage).ToString(), - OpeninvoicedataLineItemNrNumberOfItems = (1).ToString(), - OpeninvoicedataLineItemNrItemId = ("1234") - }; - - // invoiceLine2 - // invoiceLine1 - AdditionalDataOpenInvoice invoiceLine2 = new AdditionalDataOpenInvoice - { - OpeninvoicedataLineItemNrCurrencyCode = ("EUR"), - OpeninvoicedataLineItemNrDescription = ("Test product2"), - OpeninvoicedataLineItemNrItemVatAmount = (itemVatAmount).ToString(), - OpeninvoicedataLineItemNrItemAmount = (itemAmount).ToString(), - OpeninvoicedataLineItemNrItemVatPercentage = (itemVatPercentage).ToString(), - OpeninvoicedataLineItemNrNumberOfItems = (1).ToString(), - OpeninvoicedataLineItemNrItemId = ("456") - }; - - invoiceLines.Add(invoiceLine); - invoiceLines.Add(invoiceLine2); - - /*paymentRequest.AdditionalData(invoiceLines);*/ - - return paymentRequest; - } - - } -} diff --git a/Adyen.Test/MockPaymentData.cs b/Adyen.Test/MockPaymentData.cs deleted file mode 100644 index 687c0ef98..000000000 --- a/Adyen.Test/MockPaymentData.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using System.Collections.Generic; -using Adyen.Constants; -using Adyen.Model.Payment; -using Environment = Adyen.Model.Environment; - -namespace Adyen.Test -{ - internal class MockPaymentData - { - #region Mock payment data - - public static Config CreateConfigApiKeyBasedMock() - { - return new Config - { - Environment = Environment.Test, - XApiKey = "AQEyhmfxK....LAG84XwzP5pSpVd"//mock api key - }; - } - - public static PaymentRequest CreateFullPaymentRequest() - { - var paymentRequest = new PaymentRequest - { - ApplicationInfo = new ApplicationInfo - { - AdyenLibrary = new CommonField(name: ClientConfig.LibName, version: ClientConfig.LibVersion) - }, - MerchantAccount = "MerchantAccount", - Amount = new Amount("EUR", 1500), - Card = CreateTestCard(), - Reference = "payment - " + DateTime.Now.ToString("yyyyMMdd"), - AdditionalData = CreateAdditionalData() - }; - return paymentRequest; - } - - public static PaymentRequest3ds2 CreateFullPaymentRequest3DS2() - { - var paymentRequest = new PaymentRequest3ds2 - { - MerchantAccount = "MerchantAccount", - Amount = new Amount("EUR", 1500), - Reference = "payment - " + DateTime.Now.ToString("yyyyMMdd"), - AdditionalData = CreateAdditionalData(), - ThreeDS2RequestData = new ThreeDS2RequestData(threeDSCompInd: "Y", - deviceChannel: "browser"), - BrowserInfo = CreateMockBrowserInfo(), - }; - return paymentRequest; - } - - public static PaymentRequest CreateFullPaymentRequestWithShopperInteraction(PaymentRequest.ShopperInteractionEnum shopperInteraction) - { - var paymentRequest = CreateFullPaymentRequest(); - paymentRequest.ShopperInteraction = shopperInteraction; - return paymentRequest; - } - - protected static Dictionary CreateAdditionalData() - { - return new Dictionary - { - { "liabilityShift", "true"}, - { "fraudResultType", "GREEN"}, - { "authCode", "43733"}, - { "avsResult", "4 AVS not supported for this card type"} - }; - } - - public static PaymentRequest3d CreateFullPaymentRequest3D() - { - var paymentRequest = new PaymentRequest3d - { - ApplicationInfo = new ApplicationInfo - { - AdyenLibrary = new CommonField(name: ClientConfig.LibName, version: ClientConfig.LibVersion) - }, - MerchantAccount = "MerchantAccount", - BrowserInfo = CreateMockBrowserInfo(), - Reference = "payment - " + DateTime.Now.ToString("yyyyMMdd"), - CaptureDelayHours = 0 - }; - return paymentRequest; - } - - - public static BrowserInfo CreateMockBrowserInfo() - { - return new BrowserInfo - { - UserAgent = "User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36", - AcceptHeader = "*/*" - }; - } - - public static Card CreateTestCard() - { - return new Card(number: "4111111111111111", expiryMonth: "08", expiryYear: "2018", cvc: "737", holderName: "John Smith"); - } - - public static Card CreateTestCard3D() - { - return new Card(number: "5212345678901234", expiryMonth: "08", expiryYear: "2018", cvc: "737", holderName: "John Smith"); - } - - public static string GetTestPspReferenceMocked() - { - return "8514836072314693"; - } - #endregion - } -} diff --git a/Adyen.Test/ModificationTest.cs b/Adyen.Test/ModificationTest.cs deleted file mode 100644 index b1864bd42..000000000 --- a/Adyen.Test/ModificationTest.cs +++ /dev/null @@ -1,128 +0,0 @@ -using Adyen.Model.Payment; -using Adyen.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Adyen.Test -{ - [TestClass] - public class ModificationTest : BaseTest - { - [TestMethod] - public void TestCaptureMockedSuccess() - { - var paymentResultPspReference = MockPaymentData.GetTestPspReferenceMocked(); - //Call authorization test - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/capture-success.json"); - var modification = new PaymentService(client); - //Send capture call with psp refernce - var captureRequest = CreateCaptureTestRequest(paymentResultPspReference); - var captureResult = modification.Capture(captureRequest); - Assert.AreEqual(captureResult.Response, ModificationResult.ResponseEnum.CaptureReceived); - Assert.AreEqual(captureRequest.AdditionalData["authorisationType"],"PreAuth"); - } - - [TestMethod] - public void TestCancelOrRefundReceivedMocked() - { - var paymentResultPspReference = MockPaymentData.GetTestPspReferenceMocked(); - //Call authorization test - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/cancelOrRefund-received.json"); - var modification = new PaymentService(client); - var cancelOrRefundRequest = CreateCancelOrRefundTestRequest(pspReference: paymentResultPspReference); - var cancelOrRefundResult = modification.CancelOrRefund(cancelOrRefundRequest); - Assert.AreEqual(cancelOrRefundResult.Response, ModificationResult.ResponseEnum.CancelOrRefundReceived); - } - - [TestMethod] - public void TestRefundReceivedMocked() - { - var paymentResultPspReference = MockPaymentData.GetTestPspReferenceMocked(); - //Call authorization test - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/refund-received.json"); - var modification = new PaymentService(client); - var refundRequest = CreateRefundTestRequest(pspReference: paymentResultPspReference); - var refundResult = modification.Refund(refundRequest); - Assert.AreEqual(refundResult.Response, ModificationResult.ResponseEnum.RefundReceived); - } - - [TestMethod] - public void TestCancelReceivedMocked() - { - var paymentResultPspReference = MockPaymentData.GetTestPspReferenceMocked(); - //Call authorization test - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/cancel-received.json"); - var modification = new PaymentService(client); - var cancelRequest = CreateCancelTestRequest(pspReference: paymentResultPspReference); - var cancelResult = modification.Cancel(cancelRequest); - Assert.AreEqual(cancelResult.Response, ModificationResult.ResponseEnum.CancelReceived); - } - - [TestMethod] - public void TestAdjustAuthorisationReceivedMocked() - { - var paymentResultPspReference = MockPaymentData.GetTestPspReferenceMocked(); - //Call authorization test - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/adjustAuthorisation-received.json"); - var modification = new PaymentService(client); - var authorisationRequest = CreateAdjustAuthorisationRequest(pspReference: paymentResultPspReference); - var adjustAuthorisationResult = modification.AdjustAuthorisation(authorisationRequest); - Assert.AreEqual(adjustAuthorisationResult.Response, ModificationResult.ResponseEnum.AdjustAuthorisationReceived); - Assert.AreEqual(adjustAuthorisationResult.PspReference, "853569123456789D"); - Assert.AreEqual(adjustAuthorisationResult.AdditionalData["merchantReference"], "payment - 20190901"); - } - - [TestMethod] - public void TestCaptureRequest() - { - var paymentResultPspReference = MockPaymentData.GetTestPspReferenceMocked(); - var captureRequest = CreateCaptureTestRequest(paymentResultPspReference); - Assert.IsNotNull(captureRequest.AdditionalData); - } - - [TestMethod] - public void TestCancelOrRefundRequest() - { - var paymentResultPspReference = MockPaymentData.GetTestPspReferenceMocked(); - var cancelOrRefundRequest = CreateCancelOrRefundTestRequest(pspReference: paymentResultPspReference); - Assert.IsNull(cancelOrRefundRequest.AdditionalData); - Assert.AreEqual(cancelOrRefundRequest.MerchantAccount, "MerchantAccount"); - } - - [TestMethod] - public void TestRefundRequest() - { - var paymentResultPspReference = MockPaymentData.GetTestPspReferenceMocked(); - var refundRequest = CreateRefundTestRequest(pspReference: paymentResultPspReference); - Assert.IsNull(refundRequest.AdditionalData); - Assert.AreEqual(refundRequest.MerchantAccount, "MerchantAccount"); - } - - [TestMethod] - public void TestAdjustAuthorisationRequest() - { - var paymentResultPspReference = MockPaymentData.GetTestPspReferenceMocked(); - var authorisationRequest = CreateAdjustAuthorisationRequest(pspReference: paymentResultPspReference); - Assert.IsNull(authorisationRequest.AdditionalData); - Assert.AreEqual(authorisationRequest.ModificationAmount, new Amount("EUR",150)); - } - - [TestMethod] - public void TestCancelRequest() - { - var paymentResultPspReference = MockPaymentData.GetTestPspReferenceMocked(); - var cancelRequest = CreateCancelTestRequest(pspReference: paymentResultPspReference); - Assert.IsNull(cancelRequest.AdditionalData); - Assert.AreEqual(cancelRequest.MerchantAccount, "MerchantAccount"); - } - - [TestMethod] - public void TestPendingRefundReceived() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/voidPendingRefund-received.json"); - var modification = new PaymentService(client); - var voidPendingRefundRequest = new VoidPendingRefundRequest(); - var modificationResult = modification.VoidPendingRefund(voidPendingRefundRequest); - Assert.AreEqual(modificationResult.Response, ModificationResult.ResponseEnum.VoidPendingRefundReceived); - } - } -} diff --git a/Adyen.Test/Payment/ModificationTest.cs b/Adyen.Test/Payment/ModificationTest.cs new file mode 100644 index 000000000..6e70f9a10 --- /dev/null +++ b/Adyen.Test/Payment/ModificationTest.cs @@ -0,0 +1,119 @@ +using Adyen.Core.Options; +using Adyen.Payment.Extensions; +using Adyen.Payment.Client; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.Extensions.Hosting; +using System.Text.Json; +using Adyen.Constants; +using Adyen.Core; +using Adyen.Payment.Models; + +namespace Adyen.Test.Payment +{ + /// + /// ClassicPayments - Modification. + /// + [TestClass] + public class ModificationTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public ModificationTest() + { + IHost testHost = Host.CreateDefaultBuilder() + .ConfigurePayment((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = testHost.Services.GetRequiredService(); + } + + [TestMethod] + public void Given_Deserialize_When_ModificationResult_Result_Is_CaptureReceived() + { + //Assert.AreEqual(json.Contains(AdditionalData["authorisationType"],"PreAuth"); + + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/capture-success.json"); + + // Act + var captureResult = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(captureResult.Response, ModificationResult.ResponseEnum.CaptureReceived); + } + + [TestMethod] + public void Given_Deserialize_When_ModificationResult_Result_Is_CancelOrRefundReceived() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/cancelOrRefund-received.json"); + + // Act + var cancelOrRefundResult = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(cancelOrRefundResult.Response, ModificationResult.ResponseEnum.CancelOrRefundReceived); + } + + [TestMethod] + public void Given_Deserialize_When_ModificationResult_Result_Is_RefundReceived() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/refund-received.json"); + + // Act + var refundResult = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(refundResult.Response, ModificationResult.ResponseEnum.RefundReceived); + } + + [TestMethod] + public void Given_Deserialize_When_ModificationResult_Result_Is_CancelReceived() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/cancel-received.json"); + + // Act + var cancelResult = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(cancelResult.Response, ModificationResult.ResponseEnum.CancelReceived); + } + + [TestMethod] + public void Given_Deserialize_When_ModificationResult_Result_Is_AdjustAuthorisationReceived() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/adjustAuthorisation-received.json"); + + // Act + var adjustAuthorisationResult = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(adjustAuthorisationResult.Response, ModificationResult.ResponseEnum.AdjustAuthorisationReceived); + Assert.AreEqual(adjustAuthorisationResult.PspReference, "853569123456789D"); + Assert.AreEqual(adjustAuthorisationResult.AdditionalData["merchantReference"], "payment - 20190901"); + } + + [TestMethod] + public void Given_Deserialize_When_ModificationResult_Result_Is_VoidPendingRefundReceived() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/voidPendingRefund-received.json"); + + // Act + var modificationResult = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(modificationResult.Response, ModificationResult.ResponseEnum.VoidPendingRefundReceived); + } + } +} diff --git a/Adyen.Test/Payment/PaymentTest.cs b/Adyen.Test/Payment/PaymentTest.cs new file mode 100644 index 000000000..b436efe2c --- /dev/null +++ b/Adyen.Test/Payment/PaymentTest.cs @@ -0,0 +1,379 @@ +using Adyen.Core.Options; +using Adyen.Payment.Extensions; +using Adyen.Payment.Client; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.Extensions.Hosting; +using System.Text.Json; +using Adyen.Constants; +using Adyen.Payment.Models; + +namespace Adyen.Test.Payment +{ + /// + /// Classic Payments - Payment. + /// + [TestClass] + public class PaymentTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public PaymentTest() + { + IHost testHost = Host.CreateDefaultBuilder() + .ConfigurePayment((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = testHost.Services.GetRequiredService(); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentResult_Result_Is_Authorised() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/authorise-success.json"); + + // Act + var result = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(result.ResultCode, PaymentResult.ResultCodeEnum.Authorised); + Assert.AreEqual("411111", GetAdditionalData(result.AdditionalData, "cardBin")); + Assert.AreEqual("43733", GetAdditionalData(result.AdditionalData, "authCode")); + Assert.AreEqual("4 AVS not supported for this card type", GetAdditionalData(result.AdditionalData, "avsResult")); + Assert.AreEqual("1 Matches", GetAdditionalData(result.AdditionalData, "cvcResult")); + Assert.AreEqual("visa", GetAdditionalData(result.AdditionalData, "paymentMethod")); + } + + + [TestMethod] + public void Given_Deserialize_When_PaymentResult_3D_Result_Is_Authorise_Success() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/authorise-success-3d.json"); + + // Act + var result = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.IsNotNull(result.Md); + Assert.IsNotNull(result.IssuerUrl); + Assert.IsNotNull(result.PaRequest); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentResult_3DS_IdentifyShopper_Result_Is_IdentifyShopper() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/threedsecure2/authorise-response-identifyshopper.json"); + + // Act + var result = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(result.ResultCode, PaymentResult.ResultCodeEnum.IdentifyShopper); + Assert.IsNotNull(result.PspReference); + Assert.AreEqual("74044f6c-7d79-4dd1-9859-3b2879a32fb0", GetAdditionalData(result.AdditionalData, "threeds2.threeDSServerTransID")); + Assert.AreEqual(@"https://pal-test.adyen.com/threeds2simulator/acs/startMethod.shtml", GetAdditionalData(result.AdditionalData, "threeds2.threeDSMethodURL")); + Assert.AreEqual("[token]", GetAdditionalData(result.AdditionalData, "threeds2.threeDS2Token")); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentResult_3DS2_ChallengeShopper_Result_Is_ChallengerShopper() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/threedsecure2/authorise3ds2-response-challengeshopper.json"); + + // Act + var paymentResult = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(paymentResult.ResultCode, PaymentResult.ResultCodeEnum.ChallengeShopper); + Assert.IsNotNull(paymentResult.PspReference); + + Assert.AreEqual("C", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2ResponseData.transStatus")); + Assert.AreEqual("Y", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2ResponseData.acsChallengeMandated")); + Assert.AreEqual("https://pal-test.adyen.com/threeds2simulator/acs/challenge.shtml", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2ResponseData.acsURL")); + Assert.AreEqual("74044f6c-7d79-4dd1-9859-3b2879a32fb1", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2ResponseData.threeDSServerTransID")); + Assert.AreEqual("01", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2ResponseData.authenticationType")); + Assert.AreEqual("2.1.0", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2ResponseData.messageVersion")); + Assert.AreEqual("[token]", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2Token")); + Assert.AreEqual("ba961c4b-33f2-4830-3141-744b8586aeb0", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2ResponseData.acsTransID")); + Assert.AreEqual("ADYEN-ACS-SIMULATOR", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2ResponseData.acsReferenceNumber")); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentResult_Authorise_3DS2_Result_Is_Authorised() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/threedsecure2/authorise3ds2-success.json"); + + // Act + var result = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(result.ResultCode, PaymentResult.ResultCodeEnum.Authorised); + Assert.IsNotNull(result.PspReference); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentResult_Authorise_3D_Result_Is_Authorised() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/authorise3d-success.json"); + + // Act + var result = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(result.ResultCode, PaymentResult.ResultCodeEnum.Authorised); + Assert.IsNotNull(result.PspReference); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentResult_CVC_Declined_Result_Is_Refused() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/authorise-error-cvc-declined.json"); + + // Act + var result = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(PaymentResult.ResultCodeEnum.Refused, result.ResultCode); + } + + [TestMethod] + public void Given_Deserialize_When_PaymentResult_CSE_Result_Is_Authorised() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/authorise-success-cse.json"); + + // Act + var result = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(PaymentResult.ResultCodeEnum.Authorised, result.ResultCode); + } + + [TestMethod] + public void Given_CreatePaymentRequest_When_AdyenLibraryName_And_AdyenLibraryVersion_Are_Set_Returns_ApplicationInfo() + { + // Arrange + // Act + var paymentRequest = MockPaymentData.CreateFullPaymentRequest(); + + // Assert + Assert.IsNotNull(paymentRequest.ApplicationInfo); + Assert.AreEqual(paymentRequest.ApplicationInfo.AdyenLibrary.Name, Adyen.Core.Client.Extensions.HttpRequestMessageExtensions.AdyenLibraryName); + Assert.AreEqual(paymentRequest.ApplicationInfo.AdyenLibrary.Version, Adyen.Core.Client.Extensions.HttpRequestMessageExtensions.AdyenLibraryVersion); + } + + [TestMethod] + public void Given_CreatePaymentRequest3D_When_AdyenLibraryName_And_AdyenLibraryVersion_Are_Set_Returns_ApplicationInfo() + { + // Arrange + // Act + PaymentRequest3d paymentRequest = MockPaymentData.CreateFullPaymentRequest3D(); + + // Assert + Assert.IsNotNull(paymentRequest.ApplicationInfo); + Assert.AreEqual(paymentRequest.ApplicationInfo.AdyenLibrary.Name, Adyen.Core.Client.Extensions.HttpRequestMessageExtensions.AdyenLibraryName); + Assert.AreEqual(paymentRequest.ApplicationInfo.AdyenLibrary.Version, Adyen.Core.Client.Extensions.HttpRequestMessageExtensions.AdyenLibraryVersion); + } + + [TestMethod] + public void Given_Serialize_When_PaymentRequest3D_With_CaptureDelaysHours_And_FraudOffSet_Result_Should_Contain_0() + { + // Arrange + var paymentRequest = MockPaymentData.CreateFullPaymentRequest3D(); + + // Act + string target = JsonSerializer.Serialize(paymentRequest); + + // Assert + using var jsonDoc = JsonDocument.Parse(target); + JsonElement root = jsonDoc.RootElement; + Assert.AreEqual(0, root.GetProperty("captureDelayHours").GetInt32()); + Assert.AreEqual(0, root.GetProperty("fraudOffset").GetInt32()); + } + + [TestMethod] + public void Given_Deserialize_When_AuthenticationResultResponse_Result_3DS1_Success_And_3DS2_Is_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/authentication-result-success-3ds1.json"); + + // Act + var authenticationResultResponse = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Arrange + Assert.IsNotNull(authenticationResultResponse); + Assert.IsNotNull(authenticationResultResponse.ThreeDS1Result); + Assert.IsNull(authenticationResultResponse.ThreeDS2Result); + } + + [TestMethod] + public void Given_Deserialize_When_AuthenticationResultResponse_Result_3DS2_Is_Success_And_3DS1_Is_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/authentication-result-success-3ds2.json"); + + // Act + var authenticationResultResponse = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.IsNotNull(authenticationResultResponse); + Assert.IsNull(authenticationResultResponse.ThreeDS1Result); + Assert.IsNotNull(authenticationResultResponse.ThreeDS2Result); + } + + [TestMethod] + public void Given_Deserialize_When_AuthenticationResultResponse_Result_ThreeDSServerTransId_And_Ds_TransID_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/ThreeDS2Result.json"); + + // Act + var threeDs2Result = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.IsNotNull(threeDs2Result); + Assert.AreEqual("f04ec32b-f46b-46ef-9ccd-44be42fb0d7e", threeDs2Result.ThreeDS2Result.ThreeDSServerTransID); + Assert.AreEqual("80a16fa0-4eea-43c9-8de5-b0470d09d14d", threeDs2Result.ThreeDS2Result.DsTransID); + } + + [TestMethod] + public void Given_Serialize_When_PaymentRequest_With_ShopperInteraction_Moto_Result_Should_Be_Moto() + { + // Arrange + var paymentRequest = MockPaymentData.CreateFullPaymentRequestWithShopperInteraction(PaymentRequest.ShopperInteractionEnum.Moto); + + // Act + string target = JsonSerializer.Serialize(paymentRequest); + + // Assert + using var jsonDoc = JsonDocument.Parse(target); + JsonElement root = jsonDoc.RootElement; + Assert.AreEqual("Moto", root.GetProperty("shopperInteraction").GetString()); + } + + [TestMethod] + public void Given_Serialize_When_PaymentRequest_With_ShopperInteraction_Result_Should_Be_Null() + { + // Arrange + var paymentRequest = MockPaymentData.CreateFullPaymentRequestWithShopperInteraction(null); + + // Act + string target = JsonSerializer.Serialize(paymentRequest); + + // Assert + using var jsonDoc = JsonDocument.Parse(target); + JsonElement root = jsonDoc.RootElement; + Assert.IsNull(root.GetProperty("shopperInteraction").GetString()); + } + + private string GetAdditionalData(Dictionary additionalData, string assertKey) + { + string result = ""; + if (additionalData.ContainsKey(assertKey)) + { + result = additionalData[assertKey]; + } + return result; + } + } + + internal class MockPaymentData + { + #region Mock payment data + + public static PaymentRequest CreateFullPaymentRequest() + { + PaymentRequest paymentRequest = new PaymentRequest( + applicationInfo: new ApplicationInfo(adyenLibrary: new CommonField(name: Adyen.Core.Client.Extensions.HttpRequestMessageExtensions.AdyenLibraryName, version: Adyen.Core.Client.Extensions.HttpRequestMessageExtensions.AdyenLibraryVersion)), + merchantAccount: "MerchantAccount", + amount: new Amount("EUR", 1500), card: CreateTestCard(), + reference: "payment - " + DateTime.Now.ToString("yyyyMMdd"), + additionalData: CreateAdditionalData()); + return paymentRequest; + } + + public static PaymentRequest3ds2 CreateFullPaymentRequest3DS2() + { + PaymentRequest3ds2 paymentRequest = new PaymentRequest3ds2( + amount: new Amount("EUR", 1500), + merchantAccount: "MerchantAccount", + reference: "payment - " + DateTime.Now.ToString("yyyyMMdd"), + additionalData: CreateAdditionalData(), + threeDS2RequestData: new ThreeDS2RequestData(threeDSCompInd: "Y", + deviceChannel: "browser"), + browserInfo: CreateMockBrowserInfo() ); + return paymentRequest; + } + + public static PaymentRequest CreateFullPaymentRequestWithShopperInteraction(PaymentRequest.ShopperInteractionEnum shopperInteraction) + { + PaymentRequest paymentRequest = CreateFullPaymentRequest(); + paymentRequest.ShopperInteraction = shopperInteraction; + return paymentRequest; + } + + protected static Dictionary CreateAdditionalData() + { + return new Dictionary + { + { "liabilityShift", "true"}, + { "fraudResultType", "GREEN"}, + { "authCode", "43733"}, + { "avsResult", "4 AVS not supported for this card type"} + }; + } + + public static PaymentRequest3d CreateFullPaymentRequest3D() + { + PaymentRequest3d paymentRequest = new PaymentRequest3d( + md: "md", + merchantAccount: "MerchantAccount", + paResponse: "paResponse", + applicationInfo: new ApplicationInfo(adyenLibrary: new CommonField(name: Adyen.Core.Client.Extensions.HttpRequestMessageExtensions.AdyenLibraryName, version: Adyen.Core.Client.Extensions.HttpRequestMessageExtensions.AdyenLibraryVersion)), + browserInfo: CreateMockBrowserInfo(), + reference: "payment - " + DateTime.Now.ToString("yyyyMMdd"), + captureDelayHours: 0, + fraudOffset: 0); + return paymentRequest; + } + + + public static BrowserInfo CreateMockBrowserInfo() + { + return new BrowserInfo + { + UserAgent = "User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36", + AcceptHeader = "*/*" + }; + } + + public static Card CreateTestCard() + { + return new Card(number: "4111111111111111", expiryMonth: "08", expiryYear: "2018", cvc: "737", holderName: "John Smith"); + } + + public static Card CreateTestCard3D() + { + return new Card(number: "5212345678901234", expiryMonth: "08", expiryYear: "2018", cvc: "737", holderName: "John Smith"); + } + + public static string GetTestPspReferenceMocked() + { + return "8514836072314693"; + } + #endregion + } +} diff --git a/Adyen.Test/PaymentMethodDetailsTest.cs b/Adyen.Test/PaymentMethodDetailsTest.cs deleted file mode 100644 index 2612d880d..000000000 --- a/Adyen.Test/PaymentMethodDetailsTest.cs +++ /dev/null @@ -1,180 +0,0 @@ -using Adyen.Model.Checkout; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Adyen.Test -{ - [TestClass] - public class PaymentMethodDetailsTest - { - [TestMethod] - public void TestAchPaymentMethod() - { - var achDetails = new AchDetails - { - BankAccountNumber = "1234567", - BankLocationId = "1234567", - EncryptedBankAccountNumber = "1234asdfg", - OwnerName = "John Smith" - }; - var paymentRequest = new PaymentRequest - { - MerchantAccount = "YOUR_MERCHANT_ACCOUNT", - Amount = new Amount("EUR", 1000), - Reference = "ACH test", - PaymentMethod = new CheckoutPaymentMethod(achDetails), - ShopperIP = "192.0.2.1", - Channel = PaymentRequest.ChannelEnum.Web, - Origin = "https://your-company.com", - ReturnUrl = "https://your-company.com/checkout?shopperOrder=12xy..", - - }; - var paymentMethodDetails = paymentRequest.PaymentMethod.GetAchDetails(); - Assert.AreEqual(paymentMethodDetails.Type, AchDetails.TypeEnum.Ach); - Assert.AreEqual(paymentMethodDetails.BankAccountNumber, "1234567"); - Assert.AreEqual(paymentMethodDetails.BankLocationId, "1234567"); - Assert.AreEqual(paymentMethodDetails.EncryptedBankAccountNumber, "1234asdfg"); - Assert.AreEqual(paymentMethodDetails.OwnerName, "John Smith"); - Assert.AreEqual(paymentRequest.MerchantAccount, "YOUR_MERCHANT_ACCOUNT"); - Assert.AreEqual(paymentRequest.Reference, "ACH test"); - Assert.AreEqual(paymentRequest.ReturnUrl, "https://your-company.com/checkout?shopperOrder=12xy.."); - } - - - [TestMethod] - public void TestApplePayPaymentMethod() - { - var applePay = new ApplePayDetails - { - ApplePayToken = "VNRWtuNlNEWkRCSm1xWndjMDFFbktkQU..." - }; - var paymentRequest = new PaymentRequest - { - MerchantAccount = "YOUR_MERCHANT_ACCOUNT", - Amount = new Amount("EUR", 1000), - Reference = "apple pay test", - PaymentMethod = new CheckoutPaymentMethod(applePay), - ReturnUrl = "https://your-company.com/checkout?shopperOrder=12xy.." - }; - var paymentMethodDetails = paymentRequest.PaymentMethod.GetApplePayDetails(); - Assert.AreEqual(paymentMethodDetails.Type, ApplePayDetails.TypeEnum.Applepay); - Assert.AreEqual(paymentMethodDetails.ApplePayToken, "VNRWtuNlNEWkRCSm1xWndjMDFFbktkQU..."); - Assert.AreEqual(paymentRequest.MerchantAccount, "YOUR_MERCHANT_ACCOUNT"); - Assert.AreEqual(paymentRequest.Reference, "apple pay test"); - Assert.AreEqual(paymentRequest.ReturnUrl, "https://your-company.com/checkout?shopperOrder=12xy.."); - } - - [TestMethod] - public void TestGooglePayPaymentMethod() - { - var paymentRequest = new PaymentRequest - { - MerchantAccount = "YOUR_MERCHANT_ACCOUNT", - Amount = new Amount("EUR", 1000), - Reference = "google pay test", - PaymentMethod = new CheckoutPaymentMethod(new GooglePayDetails - { - GooglePayToken = "==Payload as retrieved from Google Pay response==", - FundingSource = GooglePayDetails.FundingSourceEnum.Debit - }), - ReturnUrl = "https://your-company.com/checkout?shopperOrder=12xy.." - }; - var paymentMethodDetails = (GooglePayDetails)paymentRequest.PaymentMethod.ActualInstance; - Assert.AreEqual(paymentMethodDetails.Type, GooglePayDetails.TypeEnum.Googlepay); - Assert.AreEqual(paymentMethodDetails.GooglePayToken, "==Payload as retrieved from Google Pay response=="); - Assert.AreEqual(paymentMethodDetails.FundingSource, GooglePayDetails.FundingSourceEnum.Debit); - Assert.AreEqual(paymentRequest.MerchantAccount, "YOUR_MERCHANT_ACCOUNT"); - Assert.AreEqual(paymentRequest.Reference, "google pay test"); - Assert.AreEqual(paymentRequest.ReturnUrl, "https://your-company.com/checkout?shopperOrder=12xy.."); - } - - [TestMethod] - public void TestIdealPaymentMethod() - { - - var paymentRequest = new PaymentRequest - { - MerchantAccount = "YOUR_MERCHANT_ACCOUNT", - Amount = new Amount("EUR", 1000), - Reference = "ideal test", - PaymentMethod = new CheckoutPaymentMethod(new IdealDetails - { - Issuer = "1121" - }), - ReturnUrl = "https://your-company.com/checkout?shopperOrder=12xy.." - }; - var paymentMethodDetails = paymentRequest.PaymentMethod.GetIdealDetails(); - Assert.AreEqual(paymentMethodDetails.Type, IdealDetails.TypeEnum.Ideal); - Assert.AreEqual(paymentRequest.MerchantAccount, "YOUR_MERCHANT_ACCOUNT"); - Assert.AreEqual(paymentRequest.Reference, "ideal test"); - Assert.AreEqual(paymentRequest.ReturnUrl, "https://your-company.com/checkout?shopperOrder=12xy.."); - } - - [TestMethod] - public void TestBacsDirectDebitDetails() - { - var paymentRequest = new PaymentRequest - { - MerchantAccount = "YOUR_MERCHANT_ACCOUNT", - Amount = new Amount("GBP", 1000), - Reference = "bacs direct debit test", - PaymentMethod = new CheckoutPaymentMethod(new BacsDirectDebitDetails - { - BankAccountNumber = "NL0123456789", - BankLocationId = "121000358", - HolderName = "John Smith" - }), - ReturnUrl = "https://your-company.com/checkout?shopperOrder=12xy.." - }; - var paymentMethodDetails = paymentRequest.PaymentMethod.GetBacsDirectDebitDetails(); - Assert.AreEqual(paymentMethodDetails.Type, BacsDirectDebitDetails.TypeEnum.DirectdebitGB); - Assert.AreEqual(paymentMethodDetails.BankAccountNumber, "NL0123456789"); - Assert.AreEqual(paymentMethodDetails.BankLocationId, "121000358"); - Assert.AreEqual(paymentMethodDetails.HolderName, "John Smith"); - Assert.AreEqual(paymentRequest.MerchantAccount, "YOUR_MERCHANT_ACCOUNT"); - Assert.AreEqual(paymentRequest.Reference, "bacs direct debit test"); - Assert.AreEqual(paymentRequest.ReturnUrl, "https://your-company.com/checkout?shopperOrder=12xy.."); - } - - - [TestMethod] - public void TestPaypalSuccess() - { - var paymentRequest = new PaymentRequest - { - MerchantAccount = "YOUR_MERCHANT_ACCOUNT", - Amount = new Amount("USD", 1000), - Reference = "paypal test", - PaymentMethod = new CheckoutPaymentMethod( new PayPalDetails - { - Subtype = PayPalDetails.SubtypeEnum.Sdk, - StoredPaymentMethodId = "2345654212345432345" - }), - ReturnUrl = "https://your-company.com/checkout?shopperOrder=12xy.." - }; - var paymentMethodDetails = (PayPalDetails)paymentRequest.PaymentMethod.ActualInstance; - Assert.AreEqual(paymentMethodDetails.Type, PayPalDetails.TypeEnum.Paypal); - Assert.AreEqual(paymentMethodDetails.Subtype, PayPalDetails.SubtypeEnum.Sdk); - } - - [TestMethod] - public void TestZipSuccess() - { - var paymentRequest = new PaymentRequest - { - MerchantAccount = "YOUR_MERCHANT_ACCOUNT", - Amount = new Amount("USD", 1000), - Reference = "zip test", - PaymentMethod = new CheckoutPaymentMethod(new ZipDetails - { - Type = ZipDetails.TypeEnum.Zip - }), - ReturnUrl = "https://your-company.com/checkout?shopperOrder=12xy..", - }; - var paymentMethodDetails = (ZipDetails)paymentRequest.PaymentMethod.ActualInstance; - Assert.AreEqual(paymentMethodDetails.Type, ZipDetails.TypeEnum.Zip); - Assert.AreEqual(paymentRequest.MerchantAccount, "YOUR_MERCHANT_ACCOUNT"); - Assert.AreEqual(paymentRequest.Reference, "zip test"); - Assert.AreEqual(paymentRequest.ReturnUrl, "https://your-company.com/checkout?shopperOrder=12xy.."); - } - } -} diff --git a/Adyen.Test/PaymentTest.cs b/Adyen.Test/PaymentTest.cs deleted file mode 100644 index 0e2ea1b7c..000000000 --- a/Adyen.Test/PaymentTest.cs +++ /dev/null @@ -1,207 +0,0 @@ -using System.Collections.Generic; -using Adyen.Constants; -using Adyen.Model.Payment; -using Adyen.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Adyen.Test -{ - [TestClass] - public class PaymentTest : BaseTest - { - [TestMethod] - public void TestAuthoriseBasicAuthenticationSuccessMockedResponse() - { - var paymentResult = CreatePaymentResultFromFile("mocks/authorise-success.json"); - Assert.AreEqual(paymentResult.ResultCode, PaymentResult.ResultCodeEnum.Authorised); - Assert.AreEqual("411111", GetAdditionalData(paymentResult.AdditionalData, "cardBin")); - Assert.AreEqual("43733", GetAdditionalData(paymentResult.AdditionalData, "authCode")); - Assert.AreEqual("4 AVS not supported for this card type", GetAdditionalData(paymentResult.AdditionalData, "avsResult")); - Assert.AreEqual("1 Matches", GetAdditionalData(paymentResult.AdditionalData, "cvcResult")); - Assert.AreEqual("visa", GetAdditionalData(paymentResult.AdditionalData, "paymentMethod")); - } - - [TestMethod] - public void TestAuthoriseApiKeyBasedSuccessMockedResponse() - { - var paymentResult = CreatePaymentApiKeyBasedResultFromFile("mocks/authorise-success.json"); - Assert.AreEqual(paymentResult.ResultCode, PaymentResult.ResultCodeEnum.Authorised); - Assert.AreEqual("411111", GetAdditionalData(paymentResult.AdditionalData, "cardBin")); - Assert.AreEqual("43733", GetAdditionalData(paymentResult.AdditionalData, "authCode")); - Assert.AreEqual("4 AVS not supported for this card type", GetAdditionalData(paymentResult.AdditionalData, "avsResult")); - Assert.AreEqual("1 Matches", GetAdditionalData(paymentResult.AdditionalData, "cvcResult")); - Assert.AreEqual("visa", GetAdditionalData(paymentResult.AdditionalData, "paymentMethod")); - } - - [TestMethod] - public void TestAuthoriseSuccess3DMocked() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/authorise-success-3d.json"); - var payment = new PaymentService(client); - var paymentRequest = MockPaymentData.CreateFullPaymentRequest(); - var paymentResult = payment.Authorise(paymentRequest); - Assert.IsNotNull(paymentResult.Md); - Assert.IsNotNull(paymentResult.IssuerUrl); - Assert.IsNotNull(paymentResult.PaRequest); - } - - [TestMethod] - public void TestAuthorise3DS2IdentifyShopperMocked() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/threedsecure2/authorise-response-identifyshopper.json"); - var payment = new PaymentService(client); - var paymentRequest = MockPaymentData.CreateFullPaymentRequest3DS2(); - var paymentResult = payment.Authorise3ds2(paymentRequest); - - Assert.AreEqual(paymentResult.ResultCode, PaymentResult.ResultCodeEnum.IdentifyShopper); - Assert.IsNotNull(paymentResult.PspReference); - - Assert.AreEqual("74044f6c-7d79-4dd1-9859-3b2879a32fb0", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDSServerTransID")); - Assert.AreEqual(@"https://pal-test.adyen.com/threeds2simulator/acs/startMethod.shtml", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDSMethodURL")); - Assert.AreEqual("[token]", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2Token")); - } - - [TestMethod] - public void TestAuthorise3DS2ChallengeShopperMocked() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/threedsecure2/authorise3ds2-response-challengeshopper.json"); - var payment = new PaymentService(client); - var paymentRequest = MockPaymentData.CreateFullPaymentRequest3DS2(); - var paymentResult = payment.Authorise3ds2(paymentRequest); - - Assert.AreEqual(paymentResult.ResultCode, PaymentResult.ResultCodeEnum.ChallengeShopper); - Assert.IsNotNull(paymentResult.PspReference); - - Assert.AreEqual("C", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2ResponseData.transStatus")); - Assert.AreEqual("Y", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2ResponseData.acsChallengeMandated")); - Assert.AreEqual("https://pal-test.adyen.com/threeds2simulator/acs/challenge.shtml", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2ResponseData.acsURL")); - Assert.AreEqual("74044f6c-7d79-4dd1-9859-3b2879a32fb1", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2ResponseData.threeDSServerTransID")); - Assert.AreEqual("01", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2ResponseData.authenticationType")); - Assert.AreEqual("2.1.0", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2ResponseData.messageVersion")); - Assert.AreEqual("[token]", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2Token")); - Assert.AreEqual("ba961c4b-33f2-4830-3141-744b8586aeb0", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2ResponseData.acsTransID")); - Assert.AreEqual("ADYEN-ACS-SIMULATOR", GetAdditionalData(paymentResult.AdditionalData, "threeds2.threeDS2ResponseData.acsReferenceNumber")); - } - - [TestMethod] - public void TestAuthorise3DS2SuccessMocked() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/threedsecure2/authorise3ds2-success.json"); - var payment = new PaymentService(client); - var paymentRequest = MockPaymentData.CreateFullPaymentRequest3DS2(); - var paymentResult = payment.Authorise3ds2(paymentRequest); - - Assert.AreEqual(paymentResult.ResultCode, PaymentResult.ResultCodeEnum.Authorised); - Assert.IsNotNull(paymentResult.PspReference); - } - - [TestMethod] - public void TestAuthorise3DSuccessMocked() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/authorise3d-success.json"); - var payment = new PaymentService(client); - var paymentRequest = MockPaymentData.CreateFullPaymentRequest3D(); - var paymentResult = payment.Authorise3d(paymentRequest); - Assert.AreEqual(paymentResult.ResultCode, PaymentResult.ResultCodeEnum.Authorised); - Assert.IsNotNull(paymentResult.PspReference); - } - - [TestMethod] - public void TestAuthoriseErrorCvcDeclinedMocked() - { - var paymentResult = CreatePaymentResultFromFile("mocks/authorise-error-cvc-declined.json"); - Assert.AreEqual(PaymentResult.ResultCodeEnum.Refused, paymentResult.ResultCode); - } - - [TestMethod] - public void TestAuthoriseCseSuccessMocked() - { - var paymentResult = CreatePaymentResultFromFile("mocks/authorise-success-cse.json"); - Assert.AreEqual(PaymentResult.ResultCodeEnum.Authorised, paymentResult.ResultCode); - } - [Ignore] // Fix this test -> not sure how to add additionalDataOpenInvoice class to paymentrequest - [TestMethod] - public void TestOpenInvoice() - { - var client = CreateMockTestClientRequest("mocks/authorise-success-klarna.json"); - var payment = new PaymentService(client); - var paymentRequest = MockOpenInvoicePayment.CreateOpenInvoicePaymentRequest(); - var paymentResult = payment.Authorise(paymentRequest); - Assert.AreEqual("2374421290", paymentResult.AdditionalData["additionalData.acquirerReference"]); - Assert.AreEqual("klarna", paymentResult.AdditionalData["paymentMethodVariant"]); - } - - [TestMethod] - public void TestPaymentRequestApplicationInfo() - { - var paymentRequest = MockPaymentData.CreateFullPaymentRequest(); - Assert.IsNotNull(paymentRequest.ApplicationInfo); - Assert.AreEqual(paymentRequest.ApplicationInfo.AdyenLibrary.Name, ClientConfig.LibName); - Assert.AreEqual(paymentRequest.ApplicationInfo.AdyenLibrary.Version, ClientConfig.LibVersion); - } - - [TestMethod] - public void TestPaymentRequest3DApplicationInfo() - { - var paymentRequest = MockPaymentData.CreateFullPaymentRequest3D(); - Assert.IsNotNull(paymentRequest.ApplicationInfo); - Assert.AreEqual(paymentRequest.ApplicationInfo.AdyenLibrary.Name, ClientConfig.LibName); - Assert.AreEqual(paymentRequest.ApplicationInfo.AdyenLibrary.Version, ClientConfig.LibVersion); - } - - [TestMethod] - public void TestCaptureDelaySerialization() - { - var paymentRequest = MockPaymentData.CreateFullPaymentRequest3D(); - string jsonString = paymentRequest.ToJson(); - Assert.IsTrue(jsonString.Contains("\"captureDelayHours\": 0,")); - Assert.IsFalse(jsonString.Contains("\"fraudOffset\": 0")); - } - - [TestMethod] - public void TestAuthenticationResult3ds1Success() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/authentication-result-success-3ds1.json"); - var payment = new PaymentService(client); - var authenticationResultRequest = new AuthenticationResultRequest(); - var authenticationResultResponse = payment.GetAuthenticationResult(authenticationResultRequest); - Assert.IsNotNull(authenticationResultResponse); - Assert.IsNotNull(authenticationResultResponse.ThreeDS1Result); - Assert.IsNull(authenticationResultResponse.ThreeDS2Result); - } - - [TestMethod] - public void TestAuthenticationResult3ds2Success() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/authentication-result-success-3ds2.json"); - var payment = new PaymentService(client); - var authenticationResultRequest = new AuthenticationResultRequest(); - var authenticationResultResponse = payment.GetAuthenticationResult(authenticationResultRequest); - Assert.IsNotNull(authenticationResultResponse); - Assert.IsNull(authenticationResultResponse.ThreeDS1Result); - Assert.IsNotNull(authenticationResultResponse.ThreeDS2Result); - } - - [TestMethod] - public void TestRetrieve3ds2ResultSuccess() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/ThreeDS2Result.json"); - var payment = new PaymentService(client); - var authenticationResultRequest = new AuthenticationResultRequest(); - var ThreeDSTwoResult = payment.GetAuthenticationResult(authenticationResultRequest); - Assert.AreEqual("f04ec32b-f46b-46ef-9ccd-44be42fb0d7e", ThreeDSTwoResult.ThreeDS2Result.ThreeDSServerTransID); - Assert.AreEqual("80a16fa0-4eea-43c9-8de5-b0470d09d14d", ThreeDSTwoResult.ThreeDS2Result.DsTransID); - Assert.IsNotNull(ThreeDSTwoResult); - } - - private string GetAdditionalData(Dictionary additionalData, string assertKey) - { - string result = ""; - if (additionalData.ContainsKey(assertKey)) - { - result = additionalData[assertKey]; - } - return result; - } - } -} diff --git a/Adyen.Test/PaymentsApp/PaymentsAppServiceTest.cs b/Adyen.Test/PaymentsApp/PaymentsAppServiceTest.cs new file mode 100644 index 000000000..3553dd343 --- /dev/null +++ b/Adyen.Test/PaymentsApp/PaymentsAppServiceTest.cs @@ -0,0 +1,80 @@ +using Adyen.Core.Options; +using Adyen.PaymentsApp.Client; +using Adyen.PaymentsApp.Models; +using Adyen.PaymentsApp.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Text.Json; +using Adyen.PaymentsApp.Services; + +namespace Adyen.Test.PaymentsApp +{ + [TestClass] + public class PaymentsAppTest + { + [TestMethod] + public async Task Given_PaymentsAppService_When_Initialized_Result_Should_Return_Management_Test_Url() + { + // Arrange + // Act + IHost testHost = Host.CreateDefaultBuilder() + .ConfigurePaymentsApp((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + var paymentsAppService = testHost.Services.GetRequiredService(); + + // Assert + Assert.AreEqual("https://management-test.adyen.com/v1", paymentsAppService.HttpClient.BaseAddress.ToString()); + } + + [TestMethod] + public void Given_PaymentsAppService_When_Initialized_Result_Should_Return_Management_Live_Url() + { + // Arrange + // Act + IHost testHost = Host.CreateDefaultBuilder() + .ConfigurePaymentsApp((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Live; + }); + }) + .Build(); + + var paymentsAppService = testHost.Services.GetRequiredService(); + + // Assert + Assert.AreEqual("https://management-live.adyen.com/v1", paymentsAppService.HttpClient.BaseAddress.ToString()); + } + + [TestMethod] + public void Given_PaymentsAppService_When_Initialized_Result_Should_Return_Management_Live_Url_And_No_Prefix() + { + // Arrange + // Act + IHost testHost = Host.CreateDefaultBuilder() + .ConfigurePaymentsApp((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Live; + options.LiveEndpointUrlPrefix = "prefix"; + }); + }) + .Build(); + + var paymentsAppService = testHost.Services.GetRequiredService(); + + // Assert + Assert.AreEqual("https://management-live.adyen.com/v1", paymentsAppService.HttpClient.BaseAddress.ToString()); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/PaymentsAppServiceTest.cs b/Adyen.Test/PaymentsAppServiceTest.cs deleted file mode 100644 index b9b412296..000000000 --- a/Adyen.Test/PaymentsAppServiceTest.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Net.Http; -using Adyen.Model; -using Adyen.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using NSubstitute; -using Environment = Adyen.Model.Environment; - -namespace Adyen.Test -{ - [TestClass] - public class PaymentsAppServiceTest : BaseTest - { - [TestMethod] - public void PaymentsAppServiceTESTUrlTest() - { - var client = CreateMockForAdyenClientTest(new Config()); - client.SetEnvironment(Environment.Test, "companyUrl"); - var checkout = new PaymentsAppService(client); - checkout.RevokePaymentsAppAsync("{merchantId}", "{installationId}").GetAwaiter(); - - ClientInterfaceSubstitute.Received().RequestAsync("https://management-test.adyen.com/v1/merchants/{merchantId}/paymentsApps/{installationId}/revoke", - Arg.Any(), null, new HttpMethod("POST"), default); - } - - [TestMethod] - public void PaymentsAppServiceLIVEUrlTest() - { - var client = CreateMockForAdyenClientTest(new Config()); - client.SetEnvironment(Environment.Live, "companyUrl"); - var checkout = new PaymentsAppService(client); - checkout.RevokePaymentsAppAsync("{merchantId}", "{installationId}").GetAwaiter(); - - ClientInterfaceSubstitute.Received().RequestAsync("https://management-live.adyen.com/v1/merchants/{merchantId}/paymentsApps/{installationId}/revoke", - Arg.Any(), null, new HttpMethod("POST"), default); - } - } -} \ No newline at end of file diff --git a/Adyen.Test/Payout/PayoutTest.cs b/Adyen.Test/Payout/PayoutTest.cs new file mode 100644 index 000000000..ac764d4c0 --- /dev/null +++ b/Adyen.Test/Payout/PayoutTest.cs @@ -0,0 +1,122 @@ +using Adyen.Core.Options; +using Adyen.Payout.Client; +using Adyen.Payout.Models; +using Adyen.Payout.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Text.Json; + +namespace Adyen.Test +{ + [TestClass] + public class PayoutTest : BaseTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public PayoutTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigurePayout((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_Deserialize_When_StoreDetailAndSubmitThirdParty_Returns_Not_Null() + { + // Arrange + string client = TestUtilities.GetTestFileContent("mocks/payout/storeDetailAndSubmitThirdParty-success.json"); + + // Act + var response = JsonSerializer.Deserialize(client, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("[payout-submit-received]", response.ResultCode); + Assert.AreEqual("8515131751004933", response.PspReference); + Assert.AreEqual("GREEN", response.AdditionalData["fraudResultType"]); + Assert.AreEqual("false", response.AdditionalData["fraudManualReview"]); + } + + [TestMethod] + public async Task Given_Deserialize_When_StoreDetail_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/payout/storeDetail-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("Success", response.ResultCode); + Assert.AreEqual("8515136787207087", response.PspReference); + Assert.AreEqual("8415088571022720", response.RecurringDetailReference); + } + + [TestMethod] + public async Task Given_Deserialize_When_ConfirmThirdParty_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/payout/modifyResponse-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("[payout-confirm-received]", response.Response); + Assert.AreEqual("8815131762537886", response.PspReference); + } + + [TestMethod] + public async Task Given_Deserialize_When_SubmitThirdParty_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/payout/submitResponse-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("[payout-submit-received]", response.ResultCode); + Assert.AreEqual("8815131768219992", response.PspReference); + Assert.AreEqual("GREEN", response.AdditionalData["fraudResultType"]); + Assert.AreEqual("false", response.AdditionalData["fraudManualReview"]); + } + + [TestMethod] + public async Task Given_Deserialize_When_DeclineThirdParty_ModifyResponse_Then_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/payout/modifyResponse-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("[payout-confirm-received]", response.Response); + Assert.AreEqual("8815131762537886", response.PspReference); + } + + [TestMethod] + public async Task PayoutSuccessTest() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/payout/payout-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("8814689190961342", response.PspReference); + Assert.AreEqual("12345", response.AuthCode); + } + } +} + diff --git a/Adyen.Test/PayoutTest.cs b/Adyen.Test/PayoutTest.cs deleted file mode 100644 index 08ed3c12e..000000000 --- a/Adyen.Test/PayoutTest.cs +++ /dev/null @@ -1,88 +0,0 @@ -using Adyen.Model.Payout; -using Adyen.Service.Payout; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Adyen.Test -{ - [TestClass] - public class PayoutTest : BaseTest - { - [TestMethod] - public void StoreDetailAndSubmitThirdPartySuccessTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/payout/storeDetailAndSubmitThirdParty-success.json"); - var payout = new InitializationService(client); - - var request = new StoreDetailAndSubmitRequest(); - var result = payout.StoreDetailAndSubmitThirdParty(request); - - Assert.AreEqual("[payout-submit-received]", result.ResultCode); - Assert.AreEqual("8515131751004933", result.PspReference); - Assert.AreEqual("GREEN", result.AdditionalData["fraudResultType"]); - Assert.AreEqual("false", result.AdditionalData["fraudManualReview"]); - } - - [TestMethod] - public void StoreDetailSuccessTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/payout/storeDetail-success.json"); - var payout = new InitializationService(client); - - var request = new StoreDetailRequest(); - var result = payout.StoreDetail(request); - - Assert.AreEqual("Success", result.ResultCode); - Assert.AreEqual("8515136787207087", result.PspReference); - Assert.AreEqual("8415088571022720", result.RecurringDetailReference); - } - - [TestMethod] - public void ConfirmThirdPartySuccessTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/payout/modifyResponse-success.json"); - var payout = new ReviewingService(client); - - var request = new ModifyRequest(); - var result = payout.ConfirmThirdParty(request); - - Assert.AreEqual("[payout-confirm-received]", result.Response); - Assert.AreEqual("8815131762537886", result.PspReference); - } - - [TestMethod] - public void SubmitThirdPartySuccessTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/payout/submitResponse-success.json"); - var payout = new InitializationService(client); - var request = new SubmitRequest(); - var result = payout.SubmitThirdParty(request); - Assert.AreEqual("[payout-submit-received]", result.ResultCode); - Assert.AreEqual("8815131768219992", result.PspReference); - Assert.AreEqual("GREEN", result.AdditionalData["fraudResultType"]); - Assert.AreEqual("false", result.AdditionalData["fraudManualReview"]); - } - - [TestMethod] - public void DeclineThirdPartySuccessTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/payout/modifyResponse-success.json"); - var payout = new ReviewingService(client); - var request = new ModifyRequest(); - var result = payout.DeclineThirdParty(request); - Assert.AreEqual("[payout-confirm-received]", result.Response); - Assert.AreEqual("8815131762537886", result.PspReference); - } - - [TestMethod] - public void PayoutSuccessTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/payout/payout-success.json"); - var payout = new InstantPayoutsService(client); - var request = new PayoutRequest(); - var result = payout.Payout(request); - Assert.AreEqual("8814689190961342", result.PspReference); - Assert.AreEqual("12345", result.AuthCode); - } - } -} - diff --git a/Adyen.Test/PosMobile/PosMobileTest.cs b/Adyen.Test/PosMobile/PosMobileTest.cs new file mode 100644 index 000000000..a47c88fac --- /dev/null +++ b/Adyen.Test/PosMobile/PosMobileTest.cs @@ -0,0 +1,45 @@ +using Adyen.Core.Options; +using Adyen.PosMobile.Models; +using Adyen.PosMobile.Client; +using Adyen.PosMobile.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Text.Json; + +namespace Adyen.Test.PosMobile +{ + [TestClass] + public class PosMobileTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public PosMobileTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigurePosMobile((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_Deserialize_When_CreateSessionResponse_Returns_Correct_Id() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/posmobile/create-session.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.Id, "CS451F2AB1ED897A94"); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/PosMobileTest.cs b/Adyen.Test/PosMobileTest.cs deleted file mode 100644 index fb038a8e8..000000000 --- a/Adyen.Test/PosMobileTest.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Net.Http; -using Adyen.Model.PosMobile; -using Adyen.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using NSubstitute; -using Environment = Adyen.Model.Environment; - -namespace Adyen.Test -{ - [TestClass] - public class PosMobileTest : BaseTest - { - [TestMethod] - public void TestPosMobileCreateSession() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/posmobile/create-session.json"); - client.Config.Environment = Environment.Live; - client.Config.LiveEndpointUrlPrefix = "prefix"; - var service = new PosMobileService(client); - var response = service.CreateCommunicationSession(new CreateSessionRequest()); - Assert.AreEqual(response.Id, "CS451F2AB1ED897A94"); - ClientInterfaceSubstitute.Received().RequestAsync( - "https://prefix-checkout-live.adyenpayments.com/checkout/possdk/v68/sessions", - Arg.Any(), null, new HttpMethod("POST"), default); - } - } -} \ No newline at end of file diff --git a/Adyen.Test/PosTerminalManagement/PosTerminalManagementTest.cs b/Adyen.Test/PosTerminalManagement/PosTerminalManagementTest.cs new file mode 100644 index 000000000..faadcca2a --- /dev/null +++ b/Adyen.Test/PosTerminalManagement/PosTerminalManagementTest.cs @@ -0,0 +1,128 @@ +using Adyen.Core.Options; +using Adyen.PosTerminalManagement.Client; +using Adyen.PosTerminalManagement.Models; +using Adyen.PosTerminalManagement.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Text.Json; + +namespace Adyen.Test.PosTerminalManagement +{ + [TestClass] + public class PosTerminalManagementTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public PosTerminalManagementTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigurePosTerminalManagement((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_Deserialize_When_FindTerminal_Result_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/pos-terminal-management/find-terminals-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.Terminal, "V400m-123456789"); + Assert.AreEqual(response.CompanyAccount, "TestCompany"); + Assert.AreEqual(response.MerchantAccount, "TestMerchant"); + Assert.AreEqual(response.Store, "MyStore"); + Assert.AreEqual(response.MerchantInventory, false); + } + + [TestMethod] + public async Task Given_Deserialize_When_AssignTerminals_Result_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/pos-terminal-management/assigning-terminals-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.Results["V400m-123456789"], "ActionScheduled"); + Assert.AreEqual(response.Results["P400Plus-123456789"], "Done"); + } + + [TestMethod] + public async Task Given_Deserialize_When_GetTerminalsUnderAccount_Result_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/pos-terminal-management/get-terminals-under-account-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.CompanyAccount, "TestCompany"); + Assert.AreEqual(response.MerchantAccounts[0].VarMerchantAccount, "TestMerchantPOS_EU"); + Assert.AreEqual(response.MerchantAccounts[1].VarMerchantAccount, "TestMerchantPOS_US"); + Assert.AreEqual(response.InventoryTerminals[0], "V400m-123456789"); + Assert.AreEqual(response.InventoryTerminals[1], "P400Plus-123456789"); + Assert.AreEqual(response.MerchantAccounts[0].VarMerchantAccount, "TestMerchantPOS_EU"); + Assert.AreEqual(response.MerchantAccounts[0].InventoryTerminals[0], "M400-123456789"); + Assert.AreEqual(response.MerchantAccounts[0].InventoryTerminals[1], "VX820-123456789"); + Assert.AreEqual(response.MerchantAccounts[0].InStoreTerminals[0], "E355-123456789"); + Assert.AreEqual(response.MerchantAccounts[0].InStoreTerminals[1], "V240mPlus-123456789"); + Assert.AreEqual(response.MerchantAccounts[0].Stores[0].VarStore, "TestStore"); + Assert.AreEqual(response.MerchantAccounts[0].Stores[0].InStoreTerminals[0], "MX925-123456789"); + Assert.AreEqual(response.MerchantAccounts[1].InStoreTerminals[0], "VX820-123456789"); + Assert.AreEqual(response.MerchantAccounts[1].InStoreTerminals[1], "VX690-123456789"); + } + + [TestMethod] + public async Task Given_Deserialize_When_GetTerminalDetails_Result_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/pos-terminal-management/get-terminal-details-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.CompanyAccount, "YOUR_COMPANY_ACCOUNT"); + Assert.AreEqual(response.MerchantAccount, "YOUR_MERCHANT_ACCOUNT"); + Assert.AreEqual(response.MerchantInventory, false); + Assert.AreEqual(response.Terminal, "P400Plus-275479597"); + Assert.AreEqual(response.DeviceModel, "P400Plus"); + Assert.AreEqual(response.SerialNumber, "275-479-597"); + Assert.AreEqual(response.PermanentTerminalId, "12000000"); + Assert.AreEqual(response.FirmwareVersion, "Verifone_VOS 1.50.7"); + Assert.AreEqual(response.TerminalStatus, GetTerminalDetailsResponse.TerminalStatusEnum.ReAssignToInventoryPending); + Assert.AreEqual(response.Country, "NETHERLANDS"); + Assert.AreEqual(response.DhcpEnabled, false); + } + + [TestMethod] + public async Task Given_Deserialize_When_GetStoresUnderAccount_Result_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/pos-terminal-management/get-stores-under-account-success.json"); + + // Act + var response =JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.Stores[0].MerchantAccountCode, "YOUR_MERCHANT_ACCOUNT"); + Assert.AreEqual(response.Stores[0].VarStore, "YOUR_STORE"); + Assert.AreEqual(response.Stores[0].Description, "YOUR_STORE"); + Assert.AreEqual(response.Stores[0].Status, "Active"); + } + } +} diff --git a/Adyen.Test/PosTerminalManagementTest.cs b/Adyen.Test/PosTerminalManagementTest.cs deleted file mode 100644 index 6dc9c511b..000000000 --- a/Adyen.Test/PosTerminalManagementTest.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System.Collections.Generic; -using Adyen.Model.PosTerminalManagement; -using Adyen.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Adyen.Test -{ - [TestClass] - public class PosTerminalManagementTest : BaseTest - { - /// - /// Test post /findTerminals - /// - [TestMethod] - public void FindTerminalSuccess() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/pos-terminal-management/find-terminals-success.json"); - var posTerminalManagement = new PosTerminalManagementService(client); - var findTerminalRequest = new FindTerminalRequest - { - Terminal = "V400m-123456789" - }; - var findTerminalResponse = posTerminalManagement.FindTerminal(findTerminalRequest); - Assert.AreEqual(findTerminalResponse.Terminal, "V400m-123456789"); - Assert.AreEqual(findTerminalResponse.CompanyAccount, "TestCompany"); - Assert.AreEqual(findTerminalResponse.MerchantAccount, "TestMerchant"); - Assert.AreEqual(findTerminalResponse.Store, "MyStore"); - Assert.AreEqual(findTerminalResponse.MerchantInventory, false); - } - /// - /// Test post /assignTerminals - /// - [TestMethod] - public void AssignTerminalsSuccess() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/pos-terminal-management/assing-terminals-success.json"); - var posTerminalManagement = new PosTerminalManagementService(client); - var assignTerminalsRequest = new AssignTerminalsRequest - { - MerchantAccount = "TestMerchant", - CompanyAccount = "TestMerchantAccount", - MerchantInventory = true, - Terminals = new List { "P400Plus-123456789" } - }; - var assignTerminalsResponse = posTerminalManagement.AssignTerminals(assignTerminalsRequest); - Assert.AreEqual(assignTerminalsResponse.Results["V400m-123456789"], "ActionScheduled"); - Assert.AreEqual(assignTerminalsResponse.Results["P400Plus-123456789"], "Done"); - } - - /// - /// Test post /getTerminalsUnderAccountResponse - /// - [TestMethod] - public void GetTerminalsUnderAccountSuccess() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/pos-terminal-management/get-terminals-under-account-success.json"); - var posTerminalManagement = new PosTerminalManagementService(client); - var getTerminalsUnderAccountRequest = new GetTerminalsUnderAccountRequest - { - CompanyAccount = "TestCompany", - }; - var getTerminalsUnderAccountResponse = posTerminalManagement.GetTerminalsUnderAccount(getTerminalsUnderAccountRequest); - Assert.AreEqual(getTerminalsUnderAccountResponse.CompanyAccount, "TestCompany"); - Assert.AreEqual(getTerminalsUnderAccountResponse.MerchantAccounts[0]._MerchantAccount, "TestMerchantPOS_EU"); - Assert.AreEqual(getTerminalsUnderAccountResponse.MerchantAccounts[1]._MerchantAccount, "TestMerchantPOS_US"); - Assert.AreEqual(getTerminalsUnderAccountResponse.InventoryTerminals[0], "V400m-123456789"); - Assert.AreEqual(getTerminalsUnderAccountResponse.InventoryTerminals[1], "P400Plus-123456789"); - Assert.AreEqual(getTerminalsUnderAccountResponse.MerchantAccounts[0]._MerchantAccount, "TestMerchantPOS_EU"); - Assert.AreEqual(getTerminalsUnderAccountResponse.MerchantAccounts[0].InventoryTerminals[0], "M400-123456789"); - Assert.AreEqual(getTerminalsUnderAccountResponse.MerchantAccounts[0].InventoryTerminals[1], "VX820-123456789"); - Assert.AreEqual(getTerminalsUnderAccountResponse.MerchantAccounts[0].InStoreTerminals[0], "E355-123456789"); - Assert.AreEqual(getTerminalsUnderAccountResponse.MerchantAccounts[0].InStoreTerminals[1], "V240mPlus-123456789"); - Assert.AreEqual(getTerminalsUnderAccountResponse.MerchantAccounts[0].Stores[0]._Store, "TestStore"); - Assert.AreEqual(getTerminalsUnderAccountResponse.MerchantAccounts[0].Stores[0].InStoreTerminals[0], "MX925-123456789"); - Assert.AreEqual(getTerminalsUnderAccountResponse.MerchantAccounts[1].InStoreTerminals[0], "VX820-123456789"); - Assert.AreEqual(getTerminalsUnderAccountResponse.MerchantAccounts[1].InStoreTerminals[1], "VX690-123456789"); - } - - /// - /// Test post /getTerminalDetails - /// - [TestMethod] - public void GetTerminalDetailsSuccess() - { - var client = - CreateMockTestClientApiKeyBasedRequestAsync( - "mocks/pos-terminal-management/get-terminals-details-success.json"); - var posTerminalManagement = new PosTerminalManagementService(client); - var getTerminalDetailsRequest = new GetTerminalDetailsRequest - { - Terminal = "P400Plus-275479597", - }; - var getTerminalDetailsResponse = - posTerminalManagement.GetTerminalDetails(getTerminalDetailsRequest); - Assert.AreEqual(getTerminalDetailsResponse.CompanyAccount, "YOUR_COMPANY_ACCOUNT"); - Assert.AreEqual(getTerminalDetailsResponse.MerchantAccount, "YOUR_MERCHANT_ACCOUNT"); - Assert.AreEqual(getTerminalDetailsResponse.MerchantInventory, false); - Assert.AreEqual(getTerminalDetailsResponse.Terminal, "P400Plus-275479597"); - Assert.AreEqual(getTerminalDetailsResponse.DeviceModel, "P400Plus"); - Assert.AreEqual(getTerminalDetailsResponse.SerialNumber, "275-479-597"); - Assert.AreEqual(getTerminalDetailsResponse.PermanentTerminalId, "12000000"); - Assert.AreEqual(getTerminalDetailsResponse.FirmwareVersion, "Verifone_VOS 1.50.7"); - Assert.AreEqual(getTerminalDetailsResponse.TerminalStatus, GetTerminalDetailsResponse.TerminalStatusEnum.ReAssignToInventoryPending); - Assert.AreEqual(getTerminalDetailsResponse.Country, "NETHERLANDS"); - Assert.AreEqual(getTerminalDetailsResponse.DhcpEnabled, false); - } - - /// - /// Test post /getStoresUnderAccount - /// - [TestMethod] - public void GetStoresUnderAccountSuccess() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync( - "mocks/pos-terminal-management/get-stores-under-account-success.json"); - var posTerminalManagement = new PosTerminalManagementService(client); - var getStoresUnderAccountSuccessRequest = new GetStoresUnderAccountRequest - { - CompanyAccount = "MockCompanyAccount", - MerchantAccount = "TestMerchantAccount", - }; - var getStoresUnderAccountSuccessResponse = - posTerminalManagement.GetStoresUnderAccount(getStoresUnderAccountSuccessRequest); - Assert.AreEqual(getStoresUnderAccountSuccessResponse.Stores[0].MerchantAccountCode, "YOUR_MERCHANT_ACCOUNT"); - Assert.AreEqual(getStoresUnderAccountSuccessResponse.Stores[0]._Store, "YOUR_STORE"); - Assert.AreEqual(getStoresUnderAccountSuccessResponse.Stores[0].Description, "YOUR_STORE"); - Assert.AreEqual(getStoresUnderAccountSuccessResponse.Stores[0].Status, "Active"); - - } - } -} diff --git a/Adyen.Test/ReadmeTest.cs b/Adyen.Test/ReadmeTest.cs deleted file mode 100644 index 414c693b9..000000000 --- a/Adyen.Test/ReadmeTest.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Adyen; -using Adyen.Model.Checkout; -using Adyen.Service.Checkout; -using Environment = Adyen.Model.Environment; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Adyen.Test -{ - [TestClass] - public class ReadmeTest - { - //Only test if compiles properly. It is not an integration test - [TestMethod] - public void PaymentTest() - { - var config = new Config() - { - XApiKey = "your-api-key", - Environment = Environment.Test - }; - var client = new Client(config); - var paymentsService = new PaymentsService(client); - var amount = new Model.Checkout.Amount("USD", 1000); - var cardDetails = new CardDetails - { - EncryptedCardNumber = "test_4111111111111111", - EncryptedSecurityCode = "test_737", - EncryptedExpiryMonth = "test_03", - EncryptedExpiryYear = "test_2030", - HolderName = "John Smith", - Type = CardDetails.TypeEnum.Card - }; - var paymentsRequest = new Model.Checkout.PaymentRequest - { - Reference = "Your order number ", - ReturnUrl = @"https://your-company.com/...", - MerchantAccount = "your-merchant-account", - Amount = amount, - PaymentMethod = new CheckoutPaymentMethod(cardDetails) - }; - } - } -} \ No newline at end of file diff --git a/Adyen.Test/Recurring/RecurringTest.cs b/Adyen.Test/Recurring/RecurringTest.cs new file mode 100644 index 000000000..c642b98df --- /dev/null +++ b/Adyen.Test/Recurring/RecurringTest.cs @@ -0,0 +1,126 @@ +using Adyen.Core.Options; +using Adyen.Recurring.Client; +using Adyen.Recurring.Models; +using Adyen.Recurring.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Text.Json; + +namespace Adyen.Test.Recurring +{ + [TestClass] + public class RecurringTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public RecurringTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureRecurring((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_Deserialize_When_ListRecurringDetails_Result_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/recurring/listRecurringDetails-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(3L, response.Details.Count); + + Assert.AreEqual("BFXCHLC5L6KXWD82", response.Details[0].RecurringDetail.RecurringDetailReference); + Assert.AreEqual("K652534298119846", response.Details[0].RecurringDetail.Alias); + Assert.AreEqual("0002", response.Details[0].RecurringDetail.Card.Number); + + Assert.AreEqual("JW6RTP5PL6KXWD82", response.Details[1].RecurringDetail.RecurringDetailReference); + Assert.AreEqual("Wirecard", response.Details[1].RecurringDetail.Bank.BankName); + Assert.AreEqual("sepadirectdebit", response.Details[1].RecurringDetail.Variant); + } + + [TestMethod] + public async Task Given_Deserialize_When_Disable_Result_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/recurring/disable-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("[detail-successfully-disabled]", response.Response); + } + + [TestMethod] + public async Task Given_Deserialize_When_NotifyShopper_Result_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/recurring/notifyShopper-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("Example displayed reference", response.DisplayedReference); + Assert.AreEqual("8516167336214570", response.PspReference); + Assert.AreEqual("Request processed successfully", response.Message); + Assert.AreEqual("Example reference", response.Reference); + Assert.AreEqual("Success", response.ResultCode); + Assert.AreEqual("IA0F7500002462", response.ShopperNotificationReference); + } + + [TestMethod] + public async Task Given_Deserialize_When_CreatePermit_Result_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/recurring/createPermit-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("string", response.PspReference); + Assert.AreEqual(1, response.PermitResultList.Count); + } + + [TestMethod] + public async Task Given_Deserialize_When_DisablePermit_Result_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/recurring/disablePermit-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("string", response.PspReference); + Assert.AreEqual("disabled",response.Status); + } + + [TestMethod] + public async Task Given_Deserialize_When_ScheduleAccountUpdater_Result_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/recurring/scheduleAccountUpdater-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual("string", response.PspReference); + Assert.AreEqual("string", response.Result); + } + } +} diff --git a/Adyen.Test/RecurringTest.cs b/Adyen.Test/RecurringTest.cs deleted file mode 100644 index 101fc6fa2..000000000 --- a/Adyen.Test/RecurringTest.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System.Threading.Tasks; -using Adyen.Model.Recurring; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Adyen.Test -{ - [TestClass] - public class RecurringTest : BaseTest - { - - [TestMethod] - public void TestListRecurringDetails() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/recurring/listRecurringDetails-success.json"); - var recurring = new Service.RecurringService(client); - var recurringDetailsRequest = CreateRecurringDetailsRequest(); - var recurringDetailsResult = recurring.ListRecurringDetails(recurringDetailsRequest); - Assert.AreEqual(3L, recurringDetailsResult.Details.Count); - var recurringDetail = recurringDetailsResult.Details[0].RecurringDetail; - - Assert.AreEqual("BFXCHLC5L6KXWD82", recurringDetail.RecurringDetailReference); - Assert.AreEqual("K652534298119846", recurringDetail.Alias); - Assert.AreEqual("0002", recurringDetail.Card.Number); - } - - [TestMethod] - public async Task TestListRecurringDetailsAsync() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/recurring/listRecurringDetails-success.json"); - var recurring = new Service.RecurringService(client); - var recurringDetailsRequest = CreateRecurringDetailsRequest(); - var recurringDetailsResult = await recurring.ListRecurringDetailsAsync(recurringDetailsRequest); - Assert.AreEqual(3L, recurringDetailsResult.Details.Count); - var recurringDetail = recurringDetailsResult.Details[1].RecurringDetail; - Assert.AreEqual("JW6RTP5PL6KXWD82", recurringDetail.RecurringDetailReference); - Assert.AreEqual("Wirecard", recurringDetail.Bank.BankName); - Assert.AreEqual("sepadirectdebit", recurringDetail.Variant); - } - - [TestMethod] - public void TestDisable() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/recurring/disable-success.json"); - var recurring = new Service.RecurringService(client); - var disableRequest = CreateDisableRequest(); - var disableResult = recurring.Disable(disableRequest); - Assert.AreEqual("[detail-successfully-disabled]", disableResult.Response); - } - - [TestMethod] - public async Task TestDisableAsync() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/recurring/disable-success.json"); - var recurring = new Service.RecurringService(client); - var disableRequest = CreateDisableRequest(); - var disableResult = await recurring.DisableAsync(disableRequest); - Assert.AreEqual("[detail-successfully-disabled]", disableResult.Response); - } - - [TestMethod] - public void NotifyShopperTest() - { - Client client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/recurring/notifyShopper-success.json"); - var recurring = new Service.RecurringService(client); - NotifyShopperRequest request = CreateNotifyShopperRequest(); - NotifyShopperResult result = recurring.NotifyShopper(request); - Assert.IsNotNull(result); - Assert.AreEqual("Example displayed reference", result.DisplayedReference); - Assert.AreEqual("8516167336214570", result.PspReference); - Assert.AreEqual("Request processed successfully", result.Message); - Assert.AreEqual("Example reference", result.Reference); - Assert.AreEqual("Success", result.ResultCode); - Assert.AreEqual("IA0F7500002462", result.ShopperNotificationReference); - } - - [TestMethod] - public void CreatePermitTest() - { - Client client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/recurring/createPermit-success.json"); - var recurring = new Service.RecurringService(client); - var createPermitResult = recurring.CreatePermit(new CreatePermitRequest()); - Assert.IsNotNull(createPermitResult); - Assert.AreEqual("string", createPermitResult.PspReference); - Assert.AreEqual(1,createPermitResult.PermitResultList.Count); - } - - [TestMethod] - public void DisablePermitTest() - { - Client client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/recurring/disablePermit-success.json"); - var recurring = new Service.RecurringService(client); - var disablePermitResult = recurring.DisablePermit(new DisablePermitRequest()); - Assert.IsNotNull(disablePermitResult); - Assert.AreEqual("string", disablePermitResult.PspReference); - Assert.AreEqual("disabled",disablePermitResult.Status); - } - - [TestMethod] - public void ScheduleAccountUpdaterTest() - { - Client client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/recurring/scheduleAccountUpdater-success.json"); - var recurring = new Service.RecurringService(client); - var scheduleAccountUpdaterResult = recurring.ScheduleAccountUpdater(new ScheduleAccountUpdaterRequest()); - Assert.IsNotNull(scheduleAccountUpdaterResult); - Assert.AreEqual("string", scheduleAccountUpdaterResult.PspReference); - Assert.AreEqual("string",scheduleAccountUpdaterResult.Result); - } - - private RecurringDetailsRequest CreateRecurringDetailsRequest() - { - var request = new RecurringDetailsRequest - { - ShopperReference = "test-123", - MerchantAccount = "DotNetAlexandros", - Recurring = new Recurring(Recurring.ContractEnum.RECURRING) - }; - return request; - } - - private DisableRequest CreateDisableRequest() - { - var request = new DisableRequest - { - ShopperReference = "test-123", - MerchantAccount = "DotNetAlexandros" - }; - return request; - } - - private NotifyShopperRequest CreateNotifyShopperRequest() - { - return new NotifyShopperRequest - { - MerchantAccount = "TestMerchant", - RecurringDetailReference = "8316158654144897", - Reference = "Example reference", - ShopperReference = "1234567", - BillingDate = "2021-03-31", - DisplayedReference = "Example displayed reference" - }; - } - } -} diff --git a/Adyen.Test/StoredValue/StoredValueTest.cs b/Adyen.Test/StoredValue/StoredValueTest.cs new file mode 100644 index 000000000..95a9f4286 --- /dev/null +++ b/Adyen.Test/StoredValue/StoredValueTest.cs @@ -0,0 +1,116 @@ +using Adyen.Core.Options; +using Adyen.StoredValue.Client; +using Adyen.StoredValue.Models; +using Adyen.StoredValue.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Text.Json; + +namespace Adyen.Test.StoredValue +{ + [TestClass] + public class StoredValueTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public StoredValueTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureStoredValue((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_Deserialize_When_StoredValueIssue_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/storedvalue/issue-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.ResultCode, StoredValueIssueResponse.ResultCodeEnum.Success); + Assert.AreEqual(response.AuthCode, "authCode"); + } + + [TestMethod] + public async Task Given_Deserialize_When_ChangeStatus_StoredValueStatusChange_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/storedvalue/changeStatus-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.ResultCode, StoredValueStatusChangeResponse.ResultCodeEnum.Refused); + Assert.AreEqual(response.AuthCode, "authCode"); + } + + [TestMethod] + public async Task Given_Deserialize_When_StoredValueLoad_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/storedvalue/load-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.ResultCode, StoredValueLoadResponse.ResultCodeEnum.Success); + Assert.AreEqual(response.AuthCode, "authCode"); + } + + [TestMethod] + public async Task Given_Deserialize_When_StoredValueBalanceCheck_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/storedvalue/checkBalance-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.ResultCode, StoredValueBalanceCheckResponse.ResultCodeEnum.Success); + Assert.AreEqual(response.CurrentBalance.Value, 0); + } + + [TestMethod] + public async Task Given_Deserialize_When_StoredValueBalanceMerge_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/storedvalue/mergeBalance-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.ResultCode, StoredValueBalanceMergeResponse.ResultCodeEnum.Success); + Assert.AreEqual(response.CurrentBalance.Value, 0); + } + + [TestMethod] + public async Task Given_Deserialize_When_StoredValueVoid_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/storedvalue/mergeBalance-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.ResultCode, StoredValueVoidResponse.ResultCodeEnum.Success); + Assert.AreEqual(response.CurrentBalance.Value, 0); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/StoredValueTest.cs b/Adyen.Test/StoredValueTest.cs deleted file mode 100644 index cb0f0e3d3..000000000 --- a/Adyen.Test/StoredValueTest.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Adyen.Model.StoredValue; -using Adyen.Service; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Adyen.Test -{ - [TestClass] - public class StoredValueTest : BaseTest - { - [TestMethod] - public void StoredValueIssueTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/storedvalue/issue-success.json"); - var service = new StoredValueService(client); - var response = service.Issue(new StoredValueIssueRequest()); - Assert.AreEqual(response.ResultCode, StoredValueIssueResponse.ResultCodeEnum.Success); - Assert.AreEqual(response.AuthCode, "authCode"); - } - - [TestMethod] - public void StoredValueChangeStatusTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/storedvalue/changeStatus-success.json"); - var service = new StoredValueService(client); - var response = service.ChangeStatus(new StoredValueStatusChangeRequest()); - Assert.AreEqual(response.ResultCode, StoredValueStatusChangeResponse.ResultCodeEnum.Refused); - Assert.AreEqual(response.AuthCode, "authCode"); - } - - [TestMethod] - public void StoredValueLoadTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/storedvalue/load-success.json"); - var service = new StoredValueService(client); - var response = service.Load(new StoredValueLoadRequest()); - Assert.AreEqual(response.ResultCode, StoredValueLoadResponse.ResultCodeEnum.Success); - Assert.AreEqual(response.AuthCode, "authCode"); - } - - [TestMethod] - public void StoredValueCheckBalanceTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/storedvalue/checkBalance-success.json"); - var service = new StoredValueService(client); - var response = service.CheckBalance(new StoredValueBalanceCheckRequest()); - Assert.AreEqual(response.ResultCode, StoredValueBalanceCheckResponse.ResultCodeEnum.Success); - Assert.AreEqual(response.CurrentBalance.Value, 0); - } - - [TestMethod] - public void StoredValueMergeBalanceTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/storedvalue/mergeBalance-success.json"); - var service = new StoredValueService(client); - var response = service.MergeBalance(new StoredValueBalanceMergeRequest()); - Assert.AreEqual(response.ResultCode, StoredValueBalanceMergeResponse.ResultCodeEnum.Success); - Assert.AreEqual(response.CurrentBalance.Value, 0); - } - - [TestMethod] - public void StoredValueVoidTransactionTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/storedvalue/mergeBalance-success.json"); - var service = new StoredValueService(client); - var response = service.VoidTransactionAsync(new StoredValueVoidRequest()).Result; - Assert.AreEqual(response.ResultCode, StoredValueVoidResponse.ResultCodeEnum.Success); - Assert.AreEqual(response.CurrentBalance.Value, 0); - } - } -} \ No newline at end of file diff --git a/Adyen.Test/BaseTest.cs b/Adyen.Test/TerminalApi/BaseTest.cs similarity index 91% rename from Adyen.Test/BaseTest.cs rename to Adyen.Test/TerminalApi/BaseTest.cs index f9e195846..7c1911280 100644 --- a/Adyen.Test/BaseTest.cs +++ b/Adyen.Test/TerminalApi/BaseTest.cs @@ -1,439 +1,413 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net.Http; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Adyen.Constants; -using Adyen.HttpClient.Interfaces; -using Adyen.Model; -using Adyen.Model.TerminalApi; -using Adyen.Model.Payment; -using Adyen.Service; -using NSubstitute; -using Amount = Adyen.Model.Checkout; -using Environment = System.Environment; -using PaymentResult = Adyen.Model.Payment.PaymentResult; - -namespace Adyen.Test -{ - public class BaseTest - { - private protected IClient ClientInterfaceSubstitute; - - #region Payment request - /// - /// Payment with basic authentication - /// - /// - /// - protected PaymentResult CreatePaymentResultFromFile(string fileName) - { - var client = CreateMockTestClientApiKeyBasedRequestAsync(fileName); - var payment = new PaymentService(client); - var paymentRequest = MockPaymentData.CreateFullPaymentRequest(); - var paymentResult = payment.Authorise(paymentRequest); - return paymentResult; - } - - protected PaymentResult CreatePaymentApiKeyBasedResultFromFile(string fileName) - { - var client = CreateMockTestClientApiKeyBasedRequestAsync(fileName); - var payment = new PaymentService(client); - var paymentRequest = MockPaymentData.CreateFullPaymentRequest(); - - var paymentResult = payment.Authorise(paymentRequest); - return paymentResult; - } - #endregion - - #region Modification objects - - protected CaptureRequest CreateCaptureTestRequest(string pspReference) - { - var captureRequest = new CaptureRequest - { - MerchantAccount = "MerchantAccount", - ModificationAmount = new Model.Payment.Amount("EUR", 150), - Reference = "capture - " + DateTime.Now.ToString("yyyyMMdd"), - OriginalReference = pspReference, - AdditionalData = new Dictionary {{"authorisationType", "PreAuth"}} - }; - return captureRequest; - } - - - protected CancelOrRefundRequest CreateCancelOrRefundTestRequest(string pspReference) - { - var cancelOrRefundRequest = new CancelOrRefundRequest - { - MerchantAccount = "MerchantAccount", - Reference = "cancelOrRefund - " + DateTime.Now.ToString("yyyyMMdd"), - OriginalReference = pspReference - }; - return cancelOrRefundRequest; - } - - protected RefundRequest CreateRefundTestRequest(string pspReference) - { - var refundRequest = new RefundRequest - { - MerchantAccount = "MerchantAccount", - ModificationAmount = new Model.Payment.Amount("EUR", 150), - Reference = "refund - " + DateTime.Now.ToString("yyyyMMdd"), - OriginalReference = pspReference - }; - return refundRequest; - } - - protected CancelRequest CreateCancelTestRequest(string pspReference) - { - var cancelRequest = new CancelRequest - { - MerchantAccount = "MerchantAccount", - Reference = "cancel - " + DateTime.Now.ToString("yyyyMMdd"), - OriginalReference = pspReference - }; - return cancelRequest; - } - - protected AdjustAuthorisationRequest CreateAdjustAuthorisationRequest(string pspReference) - { - var adjustAuthorisationRequest = new AdjustAuthorisationRequest - { - MerchantAccount = "MerchantAccount", - ModificationAmount = new Model.Payment.Amount("EUR", 150), - Reference = "adjustAuthorisationRequest - " + DateTime.Now.ToString("yyyyMMdd"), - OriginalReference = pspReference, - }; - return adjustAuthorisationRequest; - } - #endregion - - #region Checkout - /// - /// Check out payment request - /// - /// - /// - public Model.Checkout.PaymentRequest CreatePaymentRequestCheckout() - { - var amount = new Model.Checkout.Amount("USD", 1000); - var paymentsRequest = new Model.Checkout.PaymentRequest - { - Reference = "Your order number ", - ReturnUrl = @"https://your-company.com/...", - MerchantAccount = "MerchantAccount", - CaptureDelayHours = 0 - }; - var cardDetails = new Amount.CardDetails - { - Number = "4111111111111111", - ExpiryMonth = "10", - ExpiryYear = "2020", - HolderName = "John Smith" - }; - paymentsRequest.Amount = amount; - paymentsRequest.PaymentMethod = new Amount.CheckoutPaymentMethod(cardDetails); - paymentsRequest.ApplicationInfo = new Model.Checkout.ApplicationInfo(adyenLibrary: new Amount.CommonField()); - return paymentsRequest; - } - - - /// - /// 3DS2 payments request - /// - /// - public Model.Checkout.PaymentRequest CreatePaymentRequest3DS2() - { - var amount = new Model.Checkout.Amount("USD", 1000); - var paymentsRequest = new Model.Checkout.PaymentRequest - { - Reference = "Your order number ", - Amount = amount, - ReturnUrl = @"https://your-company.com/...", - MerchantAccount = "MerchantAccount", - Channel = Model.Checkout.PaymentRequest.ChannelEnum.Web - }; - var cardDetails = new Amount.CardDetails - { - Number = "4111111111111111", - ExpiryMonth = "10", - ExpiryYear = "2020", - HolderName = "John Smith" - }; - paymentsRequest.PaymentMethod = new Amount.CheckoutPaymentMethod(cardDetails); - return paymentsRequest; - } - - /// - ///Checkout Details request - /// - /// Returns a sample PaymentsDetailsRequest object with test data - protected Amount.PaymentDetailsRequest CreateDetailsRequest() - { - var paymentData = "Ab02b4c0!BQABAgCJN1wRZuGJmq8dMncmypvknj9s7l5Tj..."; - var details = new Amount.PaymentCompletionDetails - { - MD= "sdfsdfsdf...", - PaReq = "sdfsdfsdf..." - }; - var paymentsDetailsRequest = new Amount.PaymentDetailsRequest(details: details, paymentData: paymentData); - - return paymentsDetailsRequest; - } - - /// - /// Checkout paymentMethodsRequest - /// - /// - protected Amount.PaymentMethodsRequest CreatePaymentMethodRequest(string merchantAccount) - { - return new Amount.PaymentMethodsRequest(merchantAccount: merchantAccount); - } - - #endregion - - /// - /// Creates mock test client without any response - /// - /// IClient implementation - protected Client CreateMockForAdyenClientTest(Config config) - { - //Create a mock interface - ClientInterfaceSubstitute = Substitute.For(); - - ClientInterfaceSubstitute.RequestAsync(Arg.Any(), - Arg.Any(), Arg.Any(), Arg.Any(), - Arg.Any()).Returns(Task.FromResult("")); - - var clientMock = new Client(config) - { - HttpClient = ClientInterfaceSubstitute, - Config = config - }; - return clientMock; - } - - /// - /// Creates mock test client - /// - /// The file that is returned - /// IClient implementation - protected Client CreateMockTestClientRequest(string fileName) - { - var mockPath = GetMockFilePath(fileName); - var response = MockFileToString(mockPath); - - ClientInterfaceSubstitute = Substitute.For(); - - ClientInterfaceSubstitute.RequestAsync(Arg.Any(), - Arg.Any(), Arg.Any(), Arg.Any(), - Arg.Any()).Returns(response); - - var config = new Config - { - Environment = Model.Environment.Test - }; - var clientMock = new Client(config) - { - HttpClient = ClientInterfaceSubstitute, - Config = new Config - { - Username = "Username", - Password = "Password", - ApplicationName = "Appname" - } - }; - return clientMock; - } - - /// - /// Creates mock test client - /// - /// The file that is returned - /// IClient implementation - protected Client CreateMockTestClientApiKeyBasedRequestAsync(string fileName) - { - var mockPath = GetMockFilePath(fileName); - var response = MockFileToString(mockPath); - //Create a mock interface - ClientInterfaceSubstitute = Substitute.For(); - - ClientInterfaceSubstitute.RequestAsync(Arg.Any(), - Arg.Any(), Arg.Any(), Arg.Any(), - Arg.Any()).Returns(response); - var config = new Config - { - Environment = Model.Environment.Test - }; - var clientMock = new Client(config) - { - HttpClient = ClientInterfaceSubstitute, - Config = new Config - { - Username = "Username", - Password = "Password", - ApplicationName = "Appname" - } - }; - return clientMock; - } - - /// - /// Creates async mock test client - /// - /// The file that is returned - /// IClient implementation - protected Client CreateAsyncMockTestClientApiKeyBasedRequest(string fileName) - { - var mockPath = GetMockFilePath(fileName); - var response = MockFileToString(mockPath); - - var configWithApiKey = new Config - { - Environment = Model.Environment.Test, - XApiKey = "AQEyhmfxK....LAG84XwzP5pSpVd" - }; - - ClientInterfaceSubstitute = Substitute.For(); - - ClientInterfaceSubstitute.RequestAsync(Arg.Any(), - Arg.Any(), Arg.Any(), Arg.Any(), - Arg.Any()).Returns(response); - - var config = new Config - { - Environment = Model.Environment.Test - }; - var clientMock = new Client(config) - { - HttpClient = ClientInterfaceSubstitute, - Config = configWithApiKey - }; - return clientMock; - } - - /// - /// Creates mock test client for POS cloud. In case of cloud api the xapi should be included - /// - /// The file that is returned - /// IClient implementation - protected Client CreateMockTestClientPosCloudApiRequest(string fileName) - { - var config = new Config { CloudApiEndPoint = ClientConfig.CloudApiEndPointTest }; - var mockPath = GetMockFilePath(fileName); - var response = MockFileToString(mockPath); - - //Create a mock interface - ClientInterfaceSubstitute = Substitute.For(); - - ClientInterfaceSubstitute.Request(Arg.Any(), - Arg.Any(), Arg.Any(), Arg.Any()).Returns(response); - - ClientInterfaceSubstitute.RequestAsync(Arg.Any(), - Arg.Any(), Arg.Any(), Arg.Any()).Returns(Task.FromResult(response)); - - var anyConfig = new Config - { - Environment = Model.Environment.Test - }; - var clientMock = new Client(anyConfig) - { - HttpClient = ClientInterfaceSubstitute, - Config = config - }; - return clientMock; - } - - /// - /// Creates mock test client for terminal api. - /// - /// The file that is returned - /// IClient implementation - protected Client CreateMockTestClientPosLocalApiRequest(string fileName) - { - var config = new Config - { - Environment = Model.Environment.Test, - LocalTerminalApiEndpoint = @"https://_terminal_:8443/nexo/" - }; - var mockPath = GetMockFilePath(fileName); - var response = MockFileToString(mockPath); - //Create a mock interface - ClientInterfaceSubstitute = Substitute.For(); - - ClientInterfaceSubstitute.Request(Arg.Any(), - Arg.Any(), Arg.Any(), Arg.Any()).Returns(response); - - ClientInterfaceSubstitute.RequestAsync(Arg.Any(), - Arg.Any(), Arg.Any(), Arg.Any()).Returns(Task.FromResult(response)); - - - var anyConfig = new Config - { - Environment = Model.Environment.Test - }; - var clientMock = new Client(anyConfig) - { - HttpClient = ClientInterfaceSubstitute, - Config = config - }; - return clientMock; - } - - protected string MockFileToString(string fileName) - { - string text = ""; - - if (String.IsNullOrEmpty(fileName)) - { - return text; - } - try - { - using (var streamReader = new StreamReader(fileName, Encoding.UTF8)) - { - text = streamReader.ReadToEnd(); - } - } - catch (Exception exception) - { - throw exception; - } - - return text; - } - - /// - /// Create dummy Nexo message header - /// - /// - protected MessageHeader MockNexoMessageHeaderRequest() - { - var header = new MessageHeader - { - MessageType = MessageType.Request, - MessageClass = MessageClassType.Service, - MessageCategory = MessageCategoryType.Payment, - SaleID = "POSSystemID12345", - POIID = "MX915-284251016", - - ServiceID = (new Random()).Next(1, 9999).ToString() - }; - return header; - } - - protected static string GetMockFilePath(string fileName) - { - if (string.IsNullOrEmpty(fileName)) - { - return ""; - } - var projectPath = Directory.GetParent(Environment.CurrentDirectory).Parent.Parent.FullName; - var mockPath = Path.Combine(projectPath, fileName); - return mockPath; - } - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Adyen.Constants; +using Adyen.HttpClient.Interfaces; +using Adyen.Model; +using Adyen.Model.TerminalApi; +using Adyen.Model.Payment; +using Adyen.Service; +using NSubstitute; +using Amount = Adyen.Model.Checkout; +using Environment = System.Environment; +using PaymentResult = Adyen.Model.Payment.PaymentResult; + +namespace Adyen.Test +{ + public class BaseTest + { + private protected IClient ClientInterfaceSubstitute; + + #region Modification objects + + protected CaptureRequest CreateCaptureTestRequest(string pspReference) + { + var captureRequest = new CaptureRequest + { + MerchantAccount = "MerchantAccount", + ModificationAmount = new Model.Payment.Amount("EUR", 150), + Reference = "capture - " + DateTime.Now.ToString("yyyyMMdd"), + OriginalReference = pspReference, + AdditionalData = new Dictionary {{"authorisationType", "PreAuth"}} + }; + return captureRequest; + } + + + protected CancelOrRefundRequest CreateCancelOrRefundTestRequest(string pspReference) + { + var cancelOrRefundRequest = new CancelOrRefundRequest + { + MerchantAccount = "MerchantAccount", + Reference = "cancelOrRefund - " + DateTime.Now.ToString("yyyyMMdd"), + OriginalReference = pspReference + }; + return cancelOrRefundRequest; + } + + protected RefundRequest CreateRefundTestRequest(string pspReference) + { + var refundRequest = new RefundRequest + { + MerchantAccount = "MerchantAccount", + ModificationAmount = new Model.Payment.Amount("EUR", 150), + Reference = "refund - " + DateTime.Now.ToString("yyyyMMdd"), + OriginalReference = pspReference + }; + return refundRequest; + } + + protected CancelRequest CreateCancelTestRequest(string pspReference) + { + var cancelRequest = new CancelRequest + { + MerchantAccount = "MerchantAccount", + Reference = "cancel - " + DateTime.Now.ToString("yyyyMMdd"), + OriginalReference = pspReference + }; + return cancelRequest; + } + + protected AdjustAuthorisationRequest CreateAdjustAuthorisationRequest(string pspReference) + { + var adjustAuthorisationRequest = new AdjustAuthorisationRequest + { + MerchantAccount = "MerchantAccount", + ModificationAmount = new Model.Payment.Amount("EUR", 150), + Reference = "adjustAuthorisationRequest - " + DateTime.Now.ToString("yyyyMMdd"), + OriginalReference = pspReference, + }; + return adjustAuthorisationRequest; + } + #endregion + + #region Checkout + /// + /// Check out payment request + /// + /// + /// + public Model.Checkout.PaymentRequest CreatePaymentRequestCheckout() + { + var amount = new Model.Checkout.Amount("USD", 1000); + var paymentsRequest = new Model.Checkout.PaymentRequest + { + Reference = "Your order number ", + ReturnUrl = @"https://your-company.com/...", + MerchantAccount = "MerchantAccount", + CaptureDelayHours = 0 + }; + var cardDetails = new Amount.CardDetails + { + Number = "4111111111111111", + ExpiryMonth = "10", + ExpiryYear = "2020", + HolderName = "John Smith" + }; + paymentsRequest.Amount = amount; + paymentsRequest.PaymentMethod = new Amount.CheckoutPaymentMethod(cardDetails); + paymentsRequest.ApplicationInfo = new Model.Checkout.ApplicationInfo(adyenLibrary: new Amount.CommonField()); + return paymentsRequest; + } + + + /// + /// 3DS2 payments request + /// + /// + public Model.Checkout.PaymentRequest CreatePaymentRequest3DS2() + { + var amount = new Model.Checkout.Amount("USD", 1000); + var paymentsRequest = new Model.Checkout.PaymentRequest + { + Reference = "Your order number ", + Amount = amount, + ReturnUrl = @"https://your-company.com/...", + MerchantAccount = "MerchantAccount", + Channel = Model.Checkout.PaymentRequest.ChannelEnum.Web + }; + var cardDetails = new Amount.CardDetails + { + Number = "4111111111111111", + ExpiryMonth = "10", + ExpiryYear = "2020", + HolderName = "John Smith" + }; + paymentsRequest.PaymentMethod = new Amount.CheckoutPaymentMethod(cardDetails); + return paymentsRequest; + } + + /// + ///Checkout Details request + /// + /// Returns a sample PaymentsDetailsRequest object with test data + protected Amount.PaymentDetailsRequest CreateDetailsRequest() + { + var paymentData = "Ab02b4c0!BQABAgCJN1wRZuGJmq8dMncmypvknj9s7l5Tj..."; + var details = new Amount.PaymentCompletionDetails + { + MD= "sdfsdfsdf...", + PaReq = "sdfsdfsdf..." + }; + var paymentsDetailsRequest = new Amount.PaymentDetailsRequest(details: details, paymentData: paymentData); + + return paymentsDetailsRequest; + } + + /// + /// Checkout paymentMethodsRequest + /// + /// + protected Amount.PaymentMethodsRequest CreatePaymentMethodRequest(string merchantAccount) + { + return new Amount.PaymentMethodsRequest(merchantAccount: merchantAccount); + } + + #endregion + + /// + /// Creates mock test client without any response + /// + /// IClient implementation + protected Client CreateMockForAdyenClientTest(Config config) + { + //Create a mock interface + ClientInterfaceSubstitute = Substitute.For(); + + ClientInterfaceSubstitute.RequestAsync(Arg.Any(), + Arg.Any(), Arg.Any(), Arg.Any(), + Arg.Any()).Returns(Task.FromResult("")); + + var clientMock = new Client(config) + { + HttpClient = ClientInterfaceSubstitute, + Config = config + }; + return clientMock; + } + + /// + /// Creates mock test client + /// + /// The file that is returned + /// IClient implementation + protected Client CreateMockTestClientRequest(string fileName) + { + var mockPath = GetMockFilePath(fileName); + var response = MockFileToString(mockPath); + + ClientInterfaceSubstitute = Substitute.For(); + + ClientInterfaceSubstitute.RequestAsync(Arg.Any(), + Arg.Any(), Arg.Any(), Arg.Any(), + Arg.Any()).Returns(response); + + var config = new Config + { + Environment = Model.Environment.Test + }; + var clientMock = new Client(config) + { + HttpClient = ClientInterfaceSubstitute, + Config = new Config + { + Username = "Username", + Password = "Password", + ApplicationName = "Appname" + } + }; + return clientMock; + } + + /// + /// Creates mock test client + /// + /// The file that is returned + /// IClient implementation + protected Client CreateMockTestClientApiKeyBasedRequestAsync(string fileName) + { + var mockPath = GetMockFilePath(fileName); + var response = MockFileToString(mockPath); + //Create a mock interface + ClientInterfaceSubstitute = Substitute.For(); + + ClientInterfaceSubstitute.RequestAsync(Arg.Any(), + Arg.Any(), Arg.Any(), Arg.Any(), + Arg.Any()).Returns(response); + var config = new Config + { + Environment = Model.Environment.Test + }; + var clientMock = new Client(config) + { + HttpClient = ClientInterfaceSubstitute, + Config = new Config + { + Username = "Username", + Password = "Password", + ApplicationName = "Appname" + } + }; + return clientMock; + } + + /// + /// Creates async mock test client + /// + /// The file that is returned + /// IClient implementation + protected Client CreateAsyncMockTestClientApiKeyBasedRequest(string fileName) + { + var mockPath = GetMockFilePath(fileName); + var response = MockFileToString(mockPath); + + var configWithApiKey = new Config + { + Environment = Model.Environment.Test, + XApiKey = "AQEyhmfxK....LAG84XwzP5pSpVd" + }; + + ClientInterfaceSubstitute = Substitute.For(); + + ClientInterfaceSubstitute.RequestAsync(Arg.Any(), + Arg.Any(), Arg.Any(), Arg.Any(), + Arg.Any()).Returns(response); + + var config = new Config + { + Environment = Model.Environment.Test + }; + var clientMock = new Client(config) + { + HttpClient = ClientInterfaceSubstitute, + Config = configWithApiKey + }; + return clientMock; + } + + /// + /// Creates mock test client for POS cloud. In case of cloud api the xapi should be included + /// + /// The file that is returned + /// IClient implementation + protected Client CreateMockTestClientPosCloudApiRequest(string fileName) + { + var config = new Config { CloudApiEndPoint = ClientConfig.CloudApiEndPointTest }; + var mockPath = GetMockFilePath(fileName); + var response = MockFileToString(mockPath); + + //Create a mock interface + ClientInterfaceSubstitute = Substitute.For(); + + ClientInterfaceSubstitute.Request(Arg.Any(), + Arg.Any(), Arg.Any(), Arg.Any()).Returns(response); + + ClientInterfaceSubstitute.RequestAsync(Arg.Any(), + Arg.Any(), Arg.Any(), Arg.Any()).Returns(Task.FromResult(response)); + + var anyConfig = new Config + { + Environment = Model.Environment.Test + }; + var clientMock = new Client(anyConfig) + { + HttpClient = ClientInterfaceSubstitute, + Config = config + }; + return clientMock; + } + + /// + /// Creates mock test client for terminal api. + /// + /// The file that is returned + /// IClient implementation + protected Client CreateMockTestClientPosLocalApiRequest(string fileName) + { + var config = new Config + { + Environment = Model.Environment.Test, + LocalTerminalApiEndpoint = @"https://_terminal_:8443/nexo/" + }; + var mockPath = GetMockFilePath(fileName); + var response = MockFileToString(mockPath); + //Create a mock interface + ClientInterfaceSubstitute = Substitute.For(); + + ClientInterfaceSubstitute.Request(Arg.Any(), + Arg.Any(), Arg.Any(), Arg.Any()).Returns(response); + + ClientInterfaceSubstitute.RequestAsync(Arg.Any(), + Arg.Any(), Arg.Any(), Arg.Any()).Returns(Task.FromResult(response)); + + + var anyConfig = new Config + { + Environment = Model.Environment.Test + }; + var clientMock = new Client(anyConfig) + { + HttpClient = ClientInterfaceSubstitute, + Config = config + }; + return clientMock; + } + + protected string MockFileToString(string fileName) + { + string text = ""; + + if (String.IsNullOrEmpty(fileName)) + { + return text; + } + try + { + using (var streamReader = new StreamReader(fileName, Encoding.UTF8)) + { + text = streamReader.ReadToEnd(); + } + } + catch (Exception exception) + { + throw exception; + } + + return text; + } + + /// + /// Create dummy Nexo message header + /// + /// + protected MessageHeader MockNexoMessageHeaderRequest() + { + var header = new MessageHeader + { + MessageType = MessageType.Request, + MessageClass = MessageClassType.Service, + MessageCategory = MessageCategoryType.Payment, + SaleID = "POSSystemID12345", + POIID = "MX915-284251016", + + ServiceID = (new Random()).Next(1, 9999).ToString() + }; + return header; + } + + protected static string GetMockFilePath(string fileName) + { + if (string.IsNullOrEmpty(fileName)) + { + return ""; + } + var projectPath = Directory.GetParent(Environment.CurrentDirectory).Parent.Parent.FullName; + var mockPath = Path.Combine(projectPath, fileName); + return mockPath; + } + } +} diff --git a/Adyen.Test/ClientTest.cs b/Adyen.Test/TerminalApi/ClientTest.cs similarity index 96% rename from Adyen.Test/ClientTest.cs rename to Adyen.Test/TerminalApi/ClientTest.cs index 99a712e62..a592d91e0 100644 --- a/Adyen.Test/ClientTest.cs +++ b/Adyen.Test/TerminalApi/ClientTest.cs @@ -32,14 +32,14 @@ public void TestSetEnvironment() Assert.AreEqual("https://terminal-api-test.adyen.com", client.Config.CloudApiEndPoint); } - Client testClient; + Client _testClient; String logLine; [TestInitialize] public void TestSetup() { - testClient = new Client(new Config()); - testClient.LogCallback += new Client.CallbackLogHandler((message) => { + _testClient = new Client(new Config()); + _testClient.LogCallback += new Client.CallbackLogHandler((message) => { logLine = message; }); @@ -47,7 +47,7 @@ public void TestSetup() [TestMethod] public void TestLogLine() { - testClient.LogLine("testMessage"); + _testClient.LogLine("testMessage"); Assert.AreEqual("testMessage", logLine); } diff --git a/Adyen.Test/DeserializerTest.cs b/Adyen.Test/TerminalApi/DeserializerTest.cs similarity index 100% rename from Adyen.Test/DeserializerTest.cs rename to Adyen.Test/TerminalApi/DeserializerTest.cs diff --git a/Adyen.Test/HeaderRequestTest.cs b/Adyen.Test/TerminalApi/HeaderRequestTest.cs similarity index 92% rename from Adyen.Test/HeaderRequestTest.cs rename to Adyen.Test/TerminalApi/HeaderRequestTest.cs index 5b9282498..fe9804606 100644 --- a/Adyen.Test/HeaderRequestTest.cs +++ b/Adyen.Test/TerminalApi/HeaderRequestTest.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Net.Http; using Adyen.Constants; +using Adyen.Core.Client.Extensions; using Adyen.HttpClient; using Adyen.Model; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -165,7 +166,7 @@ public void UserAgentPresentWhenApplicationNameIsProvidedTest() var client = new HttpClientWrapper(config, new System.Net.Http.HttpClient()); HttpRequestMessage httpWebRequest = client.GetHttpRequestMessage(_endpoint, "requestBody", null, null); - Assert.AreEqual(httpWebRequest.Headers.GetValues("UserAgent").FirstOrDefault(), $"YourApplicationName {ClientConfig.LibName}/{ClientConfig.LibVersion}"); + Assert.AreEqual(httpWebRequest.Headers.GetValues("UserAgent").FirstOrDefault(), $"YourApplicationName {HttpRequestMessageExtensions.AdyenLibraryName}/{HttpRequestMessageExtensions.AdyenLibraryVersion}"); } [TestMethod] @@ -181,7 +182,7 @@ public void UserAgentPresentWhenApplicationNameIsNotProvidedTest() var client = new HttpClientWrapper(config, new System.Net.Http.HttpClient()); HttpRequestMessage httpWebRequest = client.GetHttpRequestMessage(_endpoint, "requestBody", null, null); - Assert.AreEqual(httpWebRequest.Headers.GetValues("UserAgent").FirstOrDefault(), $"{ClientConfig.LibName}/{ClientConfig.LibVersion}"); + Assert.AreEqual(httpWebRequest.Headers.GetValues("UserAgent").FirstOrDefault(), $"{HttpRequestMessageExtensions.AdyenLibraryName}/{HttpRequestMessageExtensions.AdyenLibraryVersion}"); } [TestMethod] @@ -198,9 +199,9 @@ public void LibraryAnalysisConstantsInHeaderTest() HttpRequestMessage httpWebRequest = client.GetHttpRequestMessage(_endpoint, "requestBody", null, null); Assert.IsNotNull(httpWebRequest.Headers.GetValues(ApiConstants.AdyenLibraryName)); - Assert.AreEqual(ClientConfig.LibName, httpWebRequest.Headers.GetValues(ApiConstants.AdyenLibraryName).FirstOrDefault()); + Assert.AreEqual(HttpRequestMessageExtensions.AdyenLibraryName, httpWebRequest.Headers.GetValues("adyen-library-name").FirstOrDefault()); Assert.IsNotNull(httpWebRequest.Headers.GetValues(ApiConstants.AdyenLibraryVersion)); - Assert.AreEqual(ClientConfig.LibVersion, httpWebRequest.Headers.GetValues(ApiConstants.AdyenLibraryVersion).FirstOrDefault()); + Assert.AreEqual(HttpRequestMessageExtensions.AdyenLibraryVersion, httpWebRequest.Headers.GetValues("adyen-library-version").FirstOrDefault()); } } } diff --git a/Adyen.Test/MockPosApiRequest.cs b/Adyen.Test/TerminalApi/MockPosApiRequest.cs similarity index 100% rename from Adyen.Test/MockPosApiRequest.cs rename to Adyen.Test/TerminalApi/MockPosApiRequest.cs diff --git a/Adyen.Test/NotificationDecryptionTest.cs b/Adyen.Test/TerminalApi/NotificationDecryptionTest.cs similarity index 100% rename from Adyen.Test/NotificationDecryptionTest.cs rename to Adyen.Test/TerminalApi/NotificationDecryptionTest.cs diff --git a/Adyen.Test/ProxyTest.cs b/Adyen.Test/TerminalApi/ProxyTest.cs similarity index 100% rename from Adyen.Test/ProxyTest.cs rename to Adyen.Test/TerminalApi/ProxyTest.cs diff --git a/Adyen.Test/RegionTest.cs b/Adyen.Test/TerminalApi/RegionTest.cs similarity index 100% rename from Adyen.Test/RegionTest.cs rename to Adyen.Test/TerminalApi/RegionTest.cs diff --git a/Adyen.Test/Terminal/SaleToAcquirerDataTest.cs b/Adyen.Test/TerminalApi/SaleToAcquirerDataTest.cs similarity index 98% rename from Adyen.Test/Terminal/SaleToAcquirerDataTest.cs rename to Adyen.Test/TerminalApi/SaleToAcquirerDataTest.cs index dea0b56fd..4c7248eb6 100644 --- a/Adyen.Test/Terminal/SaleToAcquirerDataTest.cs +++ b/Adyen.Test/TerminalApi/SaleToAcquirerDataTest.cs @@ -15,13 +15,13 @@ public class SaleToAcquirerDataTest { private readonly string json = "{\"metadata\":{\"key\":\"value\"},\"shopperEmail\":\"myemail@mail.com\",\"shopperReference\":\"13164308\",\"recurringProcessingModel\":\"Subscription\",\"recurringContract\":\"RECURRING,ONECLICK\",\"shopperStatement\":\"YOUR SHOPPER STATEMENT\",\"recurringDetailName\":\"VALUE\",\"recurringTokenService\":\"VALUE\",\"store\":\"store value\",\"scc\":null,\"merchantAccount\":\"merchantAccount\",\"currency\":\"EUR\",\"applicationInfo\":{\"adyenLibrary\":{\"name\":\"adyen-dotnet-api-library\",\"version\":\"" + - ClientConfig.LibVersion + + Adyen.Core.Client.Extensions.HttpRequestMessageExtensions.AdyenLibraryVersion + "\"},\"externalPlatform\":{\"integrator\":\"externalPlatformIntegrator\",\"name\":\"externalPlatformName\",\"version\":\"2.0.0\"},\"merchantDevice\":{\"os\":\"merchantDeviceOS\",\"osVersion\":\"10.12.6\",\"reference\":\"4c32759faaa7\"}},\"tenderOption\":\"ReceiptHandler,AllowPartialAuthorisation,AskGratuity\",\"authorisationType\":\"PreAuth\",\"additionalData\":{\"key.key\":\"value\",\"key.keyTwo\":\"value2\"}}"; private readonly string splitJson = "{\"metadata\":{\"key\":\"value\"},\"shopperEmail\":\"myemail@mail.com\",\"shopperReference\":\"13164308\",\"recurringProcessingModel\":\"Subscription\",\"recurringContract\":\"RECURRING,ONECLICK\",\"shopperStatement\":\"YOUR SHOPPER STATEMENT\",\"recurringDetailName\":\"VALUE\",\"recurringTokenService\":\"VALUE\",\"store\":\"store value\",\"scc\":null,\"merchantAccount\":\"merchantAccount\",\"currency\":\"EUR\",\"applicationInfo\":{\"adyenLibrary\":{\"name\":\"adyen-dotnet-api-library\",\"version\":\"" + - ClientConfig.LibVersion + + Adyen.Core.Client.Extensions.HttpRequestMessageExtensions.AdyenLibraryVersion + "\"},\"externalPlatform\":{\"integrator\":\"externalPlatformIntegrator\",\"name\":\"externalPlatformName\",\"version\":\"2.0.0\"},\"merchantDevice\":{\"os\":\"merchantDeviceOS\",\"osVersion\":\"10.12.6\",\"reference\":\"4c32759faaa7\"}},\"tenderOption\":\"ReceiptHandler,AllowPartialAuthorisation,AskGratuity\",\"authorisationType\":\"PreAuth\"," + "\"additionalData\":{\"key.key\":\"value\",\"key.keyTwo\":\"value2\",\"split.api\":\"1\",\"split.nrOfItems\":\"3\",\"split.totalAmount\":\"62000\",\"split.currencyCode\":\"EUR\",\"split.item1.amount\":\"60000\",\"split.item1.type\":\"BalanceAccount\",\"split.item1.account\":\"BA00000000000000000000001\",\"split.item1.reference\":\"reference_split_1\",\"split.item1.description\":\"description_split_1\",\"split.item2.amount\":\"2000\",\"split.item2.type\":\"Commission\",\"split.item2.reference\":\"reference_commission\",\"split.item2.description\":\"description_commission\",\"split.item3.type\":\"PaymentFee\",\"split.item3.account\":\"BA00000000000000000000001\",\"split.item3.reference\":\"reference_PaymentFee\",\"split.item3.description\":\"description_PaymentFee\"}}"; diff --git a/Adyen.Test/Terminal/SplitTest.cs b/Adyen.Test/TerminalApi/SplitTest.cs similarity index 100% rename from Adyen.Test/Terminal/SplitTest.cs rename to Adyen.Test/TerminalApi/SplitTest.cs diff --git a/Adyen.Test/TerminalApiAsyncServiceTest.cs b/Adyen.Test/TerminalApi/TerminalApiAsyncServiceTest.cs similarity index 100% rename from Adyen.Test/TerminalApiAsyncServiceTest.cs rename to Adyen.Test/TerminalApi/TerminalApiAsyncServiceTest.cs diff --git a/Adyen.Test/TerminalApiCloudRequestTest.cs b/Adyen.Test/TerminalApi/TerminalApiCloudRequestTest.cs similarity index 100% rename from Adyen.Test/TerminalApiCloudRequestTest.cs rename to Adyen.Test/TerminalApi/TerminalApiCloudRequestTest.cs diff --git a/Adyen.Test/TerminalApiInputTest.cs b/Adyen.Test/TerminalApi/TerminalApiInputTest.cs similarity index 100% rename from Adyen.Test/TerminalApiInputTest.cs rename to Adyen.Test/TerminalApi/TerminalApiInputTest.cs diff --git a/Adyen.Test/TerminalApiLocalServiceTest.cs b/Adyen.Test/TerminalApi/TerminalApiLocalServiceTest.cs similarity index 100% rename from Adyen.Test/TerminalApiLocalServiceTest.cs rename to Adyen.Test/TerminalApi/TerminalApiLocalServiceTest.cs diff --git a/Adyen.Test/TerminalApiPosRequestTest.cs b/Adyen.Test/TerminalApi/TerminalApiPosRequestTest.cs similarity index 100% rename from Adyen.Test/TerminalApiPosRequestTest.cs rename to Adyen.Test/TerminalApi/TerminalApiPosRequestTest.cs diff --git a/Adyen.Test/TerminalApiPosSecurityTest.cs b/Adyen.Test/TerminalApi/TerminalApiPosSecurityTest.cs similarity index 100% rename from Adyen.Test/TerminalApiPosSecurityTest.cs rename to Adyen.Test/TerminalApi/TerminalApiPosSecurityTest.cs diff --git a/Adyen.Test/SerializerTest.cs b/Adyen.Test/TerminalApi/TerminalApiSerializerTest.cs similarity index 66% rename from Adyen.Test/SerializerTest.cs rename to Adyen.Test/TerminalApi/TerminalApiSerializerTest.cs index 26819b1c5..86e9264d7 100644 --- a/Adyen.Test/SerializerTest.cs +++ b/Adyen.Test/TerminalApi/TerminalApiSerializerTest.cs @@ -3,7 +3,6 @@ using System.Text; using Adyen.ApiSerialization; using Adyen.Constants; -using Adyen.Model.Checkout; using Adyen.Model.Terminal; using Adyen.Model.TerminalApi; using Adyen.Model.TerminalApi.Message; @@ -21,34 +20,6 @@ namespace Adyen.Test [TestClass] public class SerializerTest { - [TestMethod] - public void SerializeByteArrayTest() - { - var plainTextBytes = Encoding.ASCII.GetBytes("Bytes-To-Be-Encoded"); - string base64String = System.Convert.ToBase64String(plainTextBytes); - var base64Bytes = Encoding.ASCII.GetBytes(base64String); - var threeDSecure = new ThreeDSecureData - { - Cavv = base64Bytes, - Xid = base64Bytes - }; - var jsonRequest = Util.JsonOperation.SerializeRequest(threeDSecure); - var json = "{\"cavv\":\"Qnl0ZXMtVG8tQmUtRW5jb2RlZA==\",\"xid\":\"Qnl0ZXMtVG8tQmUtRW5jb2RlZA==\"}"; - Assert.AreEqual(json, jsonRequest); - } - - [TestMethod] - public void DeserializeByteArrayTest() - { - var json = "{\"cavv\":\"Qnl0ZXMtVG8tQmUtRW5jb2RlZA==\",\"xid\":\"Qnl0ZXMtVG8tQmUtRW5jb2RlZA==\"}"; - var jsonRequest = Util.JsonOperation.Deserialize(json); - var xid = Encoding.ASCII.GetString(jsonRequest.Xid); - var cavv = Encoding.ASCII.GetString(jsonRequest.Cavv); - var jsonElementBase64 = "Qnl0ZXMtVG8tQmUtRW5jb2RlZA=="; - Assert.AreEqual(jsonElementBase64, xid); - Assert.AreEqual(jsonElementBase64, cavv); - } - [TestMethod] public void EnumSerializerTest() { @@ -60,40 +31,7 @@ public void EnumSerializerTest() Assert.AreEqual(paymentResponseOnline.PaymentResult.AuthenticationMethod[0], AuthenticationMethodType.OnLinePIN); Assert.AreEqual(paymentResponseOnLine.PaymentResult.AuthenticationMethod[0], AuthenticationMethodType.OnLinePIN); } - - [TestMethod] - public void CheckoutLongSerializationTest() - { - var checkoutSessionRequest = new CreateCheckoutSessionRequest - { - MerchantAccount = "merchantAccount", - Reference = "TestReference", - ReturnUrl = "http://test-url.com", - Amount = new Model.Checkout.Amount("EUR", 10000L), - Channel = CreateCheckoutSessionRequest.ChannelEnum.Web, - CountryCode = "NL", - LineItems = new List() - { - new LineItem(quantity: 1, amountIncludingTax: 5000, description: "description1", - amountExcludingTax: 0), - new LineItem(quantity: 1, amountIncludingTax: 5000, description: "description2", taxAmount: 0) - } - }.ToJson(); - // Assert that Long parameters set to zero are actually serialised (just like Int) - Assert.IsTrue(checkoutSessionRequest.Contains("amountExcludingTax\": 0,")); - Assert.IsTrue(checkoutSessionRequest.Contains("taxAmount\": 0")); - } - [TestMethod] - public void CheckoutSessionResponseCheckForIdTest() - { - var sessionResponse = @"{""mode"": ""embedded"",""amount"": {""currency"": ""EUR"",""value"": 10000},""expiresAt"": ""2023-04-06T17:11:15+02:00"",""id"": ""CS0068299CB8DA273A"",""merchantAccount"": ""TestMerchantAccount"",""reference"": ""TestReference"",""returnUrl"": ""http://test-url.com"",""sessionData"": ""Ab02b4c0!BQABAgBoacRJuRbNM/typUKATopkZ3V+cINm0WTAvwl9uQJ2e8I00P2KFemlwp4nb1bOqqYh1zx48gDAHt0QDs2JTiQIqDQRizqopLFRk/wAJHFoCuam/GvOHflg9vwS3caHbkBToIolxcYcJoJheIN9o1fGmNIHZb9VrWfiKsXMgmYsSUifenayS2tkKCTquF7vguaAwk7q5ES1GDwzP/J2mChJGH04jGrVL4szPHGmznr897wIcFQyBSZe4Uifqoe0fpiIxZLNWadLMya6SnvQYNAQL1V6ti+7F4wHHyLwHWTCua993sttue70TE4918EV80HcAWx0LE1+uW6J5KBHCKdYNi9ESlGSZreRwLCpdNbmumB6MlyHZlz2umLiw0ZZJAEpdrwXA2NiyHzJDKDKfbAhd8uoTSACrbgwbrAXI1cvb1WjiOQjLn9MVW++fuJCq6vIeX+rUKMeltBAHMeBZyC54ndAucw9nS03uyckjphunE4tl4WTT475VkfUiyJCTT3S2IOVYS9V9M8pMQ1/GlDVNCLBJJfrYlVXoC8VX68QW1HERkhNYQbTgLQ9kI3cDeMP5d4TuUL3XR2Q6sNiOMdIPGDYQ9QFKFdOE3OQ5X9wxUYbX6j88wR2gRGJcS5agqFHOR1ZTNrjumOYrEkjL8ehFXEs/KluhURllfi0POUPGAwlUWBjDCZcKaeeR0kASnsia2V5IjoiQUYwQUFBMTAzQ0E1MzdFQUVEODdDMjRERDUzOTA5QjgwQTc4QTkyM0UzODIzRDY4REFDQzk0QjlGRjgzMDVEQyJ9E0Gs1T0RaO7NluuXP59fegcW6SQKq4BhC3ZzEKPm1vJuwAJ2gZUaicaXbRPW3IyadavKRlfGdFeAYt2B3ik8fGiK3ZkKU0CrZ0qL0IO5rrWrOg74HMnpxRAn1RhAHRHfGEk67FFPNjr0aLBJXSia7xZWiyKA+i4QU63roY2sxMI/q41mvJjRUz0rPKT3SbVDt1Of7K7BP8cmiboBkWexFBD/mkJyMOpYAOoFp/tKOUHTWZYcc1GpbyV1jInXVfEUhHROYCtS4p/xwF6DdT3bI0+HRU35/xNBTZH2yJN55u9i42Vb0ddCyhzOLZYQ3JVgglty6hLgVeQzuN4b2MoalhfTl11HsElJT1kB0mznVX8CL7UMTUp/2uSgL8DN6kB4owEZ45nWRxIR/2sCidMJ1tnSI1uSqkeqXRf1vat5qPq+BzpYE0Urn2ZZEmgJyb2u0ZLn2x1tfJyPtv+hqBoT7iqJ224dSbuB4HMT49YtcheUtR0jnrImJXm+M1TeIjWB3XWOScrNV8sWEJMAiIU="",""shopperLocale"": ""en-US""}"; - // Assert that readonly parameters like ID are actually deserialised and can be serialised again. - var deserializedResponse = JsonConvert.DeserializeObject(sessionResponse); - Assert.IsNotNull(deserializedResponse.Id); - Assert.IsTrue(deserializedResponse.ToJson().Contains("\"id\": \"CS0068299CB8DA273A\",")); - } - [TestMethod] public void SaleToPoiMessageSerializationTest() { diff --git a/Adyen.Test/TerminalApiSyncServiceTest.cs b/Adyen.Test/TerminalApi/TerminalApiSyncServiceTest.cs similarity index 100% rename from Adyen.Test/TerminalApiSyncServiceTest.cs rename to Adyen.Test/TerminalApi/TerminalApiSyncServiceTest.cs diff --git a/Adyen.Test/TerminalCommonNameValidatorTest.cs b/Adyen.Test/TerminalApi/TerminalCommonNameValidatorTest.cs similarity index 100% rename from Adyen.Test/TerminalCommonNameValidatorTest.cs rename to Adyen.Test/TerminalApi/TerminalCommonNameValidatorTest.cs diff --git a/Adyen.Test/TestUtilities.cs b/Adyen.Test/TestUtilities.cs new file mode 100644 index 000000000..26d4a3748 --- /dev/null +++ b/Adyen.Test/TestUtilities.cs @@ -0,0 +1,18 @@ +namespace Adyen.Test +{ + internal static class TestUtilities + { + /// + /// Utility function, used to read (mock json) files. + /// + /// The relative file to the file. + /// The file contents in string. + public static string GetTestFileContent(string relativePath) + { + string rootPath = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "../../../")); + string absolutePath = Path.Combine(rootPath, relativePath); + + return File.ReadAllText(absolutePath); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/TransferWebhooks/TransferWebhooksTest.cs b/Adyen.Test/TransferWebhooks/TransferWebhooksTest.cs new file mode 100644 index 000000000..42314fc9e --- /dev/null +++ b/Adyen.Test/TransferWebhooks/TransferWebhooksTest.cs @@ -0,0 +1,173 @@ +using Adyen.TransferWebhooks.Client; +using Adyen.TransferWebhooks.Extensions; +using Adyen.TransferWebhooks.Models; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.Extensions.Hosting; +using System.Text.Json; + +namespace Adyen.Test.TransferWebhooks +{ + [TestClass] + public class TransferWebhooksTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public TransferWebhooksTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureTransferWebhooks((context, services, config) => + { + + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_Deserialize_When_Transfer_Webhook_Notification_Of_Chargeback_Returns_Not_Null() + { + // Arrange + string json = @" +{ + ""data"": { + ""balancePlatform"": ""YOUR_BALANCE_PLATFORM"", + ""creationDate"": ""2025-06-02T13:46:27+02:00"", + ""id"": ""01234"", + ""accountHolder"": { + ""description"": ""The Account Holder"", + ""id"": ""AH1234567890"" + }, + ""amount"": { + ""currency"": ""EUR"", + ""value"": 22700 + }, + ""balanceAccount"": { + ""description"": ""The Liable Balance Account"", + ""id"": ""BA1234567890"" + }, + ""category"": ""platformPayment"", + ""categoryData"": { + ""modificationMerchantReference"": """", + ""modificationPspReference"": ""ZX1234567890"", + ""paymentMerchantReference"": ""pv-test"", + ""platformPaymentType"": ""Remainder"", + ""pspPaymentReference"": ""PSP01234"", + ""type"": ""platformPayment"" + }, + ""description"": ""Remainder Fee for pv-test"", + ""direction"": ""incoming"", + ""reason"": ""approved"", + ""reference"": """", + ""status"": ""authorised"", + ""type"": ""capture"", + ""balances"": [ + { + ""currency"": ""EUR"", + ""received"": 0, + ""reserved"": 22700 + } + ], + ""eventId"": ""EV1234567890"", + ""events"": [ + { + ""bookingDate"": ""2025-06-02T13:46:27+02:00"", + ""id"": ""EV1234567890"", + ""mutations"": [ + { + ""currency"": ""EUR"", + ""received"": 22700 + } + ], + ""status"": ""received"", + ""type"": ""accounting"" + }, + { + ""bookingDate"": ""2025-06-02T13:46:27+02:00"", + ""id"": ""EV1234567890-2"", + ""mutations"": [ + { + ""currency"": ""EUR"", + ""received"": -22700, + ""reserved"": 22700 + } + ], + ""status"": ""authorised"", + ""type"": ""accounting"" + } + ], + ""sequenceNumber"": 2 + }, + ""environment"": ""test"", + ""timestamp"": ""2025-06-02T11:46:28.635Z"", + ""type"": ""balancePlatform.transfer.updated"" +} +"; + // Act + TransferNotificationRequest r = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.IsNotNull(r); + Assert.AreEqual("test", r.Environment); + Assert.AreEqual(TransferNotificationRequest.TypeEnum.BalancePlatformTransferUpdated, r.Type); + + Assert.AreEqual("YOUR_BALANCE_PLATFORM", r.Data.BalancePlatform); + Assert.AreEqual(DateTimeOffset.Parse("2025-06-02T13:46:27+02:00"), r.Data.CreationDate); + Assert.AreEqual("01234", r.Data.Id); + + Assert.AreEqual("The Account Holder", r.Data.AccountHolder.Description); + Assert.AreEqual("AH1234567890", r.Data.AccountHolder.Id); + + Assert.AreEqual("EUR", r.Data.Amount.Currency); + Assert.AreEqual(22700, r.Data.Amount.Value); + Assert.IsNotNull(r.Data.BalanceAccount); + Assert.AreEqual("The Liable Balance Account", r.Data.BalanceAccount.Description); + Assert.AreEqual("BA1234567890", r.Data.BalanceAccount.Id); + Assert.AreEqual(TransferData.CategoryEnum.PlatformPayment, r.Data.Category); + + Assert.AreEqual("", r.Data.CategoryData.PlatformPayment.ModificationMerchantReference); + Assert.AreEqual("ZX1234567890", r.Data.CategoryData.PlatformPayment.ModificationPspReference); + Assert.AreEqual("pv-test", r.Data.CategoryData.PlatformPayment.PaymentMerchantReference); + Assert.AreEqual(PlatformPayment.PlatformPaymentTypeEnum.Remainder, r.Data.CategoryData.PlatformPayment.PlatformPaymentType); + Assert.AreEqual("PSP01234", r.Data.CategoryData.PlatformPayment.PspPaymentReference); + Assert.AreEqual("Remainder Fee for pv-test", r.Data.Description); + Assert.AreEqual(TransferData.DirectionEnum.Incoming, r.Data.Direction); + Assert.AreEqual(TransferData.ReasonEnum.Approved, r.Data.Reason); + Assert.AreEqual("", r.Data.Reference); + Assert.AreEqual(TransferData.StatusEnum.Authorised, r.Data.Status); + Assert.AreEqual(TransferData.TypeEnum.Capture, r.Data.Type); + + Assert.AreEqual(1, r.Data.Balances.Count); + Assert.AreEqual("EUR", r.Data.Balances[0].Currency); + Assert.AreEqual(0, r.Data.Balances[0].Received); + Assert.AreEqual(22700, r.Data.Balances[0].Reserved); + Assert.AreEqual("EV1234567890", r.Data.EventId); + + Assert.AreEqual(2, r.Data.Events.Count); + + Assert.AreEqual(DateTimeOffset.Parse("2025-06-02T13:46:27+02:00"), r.Data.Events[0].BookingDate); + Assert.AreEqual("EV1234567890", r.Data.Events[0].Id); + Assert.AreEqual(TransferEvent.StatusEnum.Received, r.Data.Events[0].Status); + Assert.AreEqual(TransferEvent.TypeEnum.Accounting, r.Data.Events[0].Type); + + Assert.AreEqual(1, r.Data.Events[0].Mutations.Count); + Assert.AreEqual("EUR", r.Data.Events[0].Mutations[0].Currency); + Assert.AreEqual(22700, r.Data.Events[0].Mutations[0].Received); + + Assert.AreEqual(DateTimeOffset.Parse("2025-06-02T13:46:27+02:00"), r.Data.Events[1].BookingDate); + Assert.AreEqual("EV1234567890-2", r.Data.Events[1].Id); + Assert.AreEqual(TransferEvent.StatusEnum.Authorised, r.Data.Events[1].Status); + Assert.AreEqual(TransferEvent.TypeEnum.Accounting, r.Data.Events[1].Type); + + Assert.IsNotNull(r.Data.Events[1].Mutations); + Assert.AreEqual(1, r.Data.Events[1].Mutations.Count); + Assert.AreEqual("EUR", r.Data.Events[1].Mutations[0].Currency); + Assert.AreEqual(-22700, r.Data.Events[1].Mutations[0].Received); + Assert.AreEqual(22700, r.Data.Events[1].Mutations[0].Reserved); + + Assert.AreEqual(2, r.Data.SequenceNumber); + Assert.AreEqual(DateTimeOffset.Parse("2025-06-02T11:46:28.635Z"), r.Timestamp); + } + } +} \ No newline at end of file diff --git a/Adyen.Test/Transfers/TransfersTest.cs b/Adyen.Test/Transfers/TransfersTest.cs new file mode 100644 index 000000000..a3f6e51d9 --- /dev/null +++ b/Adyen.Test/Transfers/TransfersTest.cs @@ -0,0 +1,93 @@ +using Adyen.Core.Options; +using Adyen.Transfers.Models; +using Adyen.Transfers.Client; +using Adyen.Transfers.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Text.Json; + +namespace Adyen.Test.Transfers +{ + [TestClass] + public class TransfersTest + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public TransfersTest() + { + IHost host = Host.CreateDefaultBuilder() + .ConfigureTransfers((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + + [TestMethod] + public async Task Given_Deserialize_When_TransactionSearchResponse_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/transfers/get-all-transactions.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.Links.Next.Href, "https://balanceplatform-api-test.adyen.com/btl/v4/transactions?balancePlatform=TestBalancePlatform&createdUntil=2023-08-20T13%3A07%3A40Z&createdSince=2023-08-10T10%3A50%3A40Z&cursor=S2B-c0p1N0tdN0l6RGhYK1YpM0lgOTUyMDlLXElyKE9LMCtyaFEuMj1NMHgidCsrJi1ZNnhqXCtqVi5JPGpRK1F2fCFqWzU33JTojSVNJc1J1VXhncS10QDd6JX9FQFl5Zn0uNyUvSXJNQTo"); + } + + [TestMethod] + public async Task Given_Deserialize_When_TransferTransaction_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/transfers/get-transaction.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.Status, Transaction.StatusEnum.Booked); + Assert.AreEqual(response.Transfer.Id, "48TYZO5ZVURJ2FCW"); + } + + [TestMethod] + public async Task Given_Deserialize_When_Transfer_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/transfers/transfer-funds.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.Status, Transfer.StatusEnum.Authorised); + Assert.AreEqual(response.Category, Transfer.CategoryEnum.Bank); + } + + #region Grants + + [TestMethod] + public async Task Given_Deserialize_When_CapitalGrant_Returns_Not_Null() + { + // Arrange + string json = TestUtilities.GetTestFileContent("mocks/grants/post-grants-success.json"); + + // Act + var response = JsonSerializer.Deserialize(json, _jsonSerializerOptionsProvider.Options); + + // Assert + Assert.AreEqual(response.Id, "GR00000000000000000000001"); + Assert.AreEqual(response.GrantAccountId, "CG00000000000000000000001"); + Assert.AreEqual(response.Status, CapitalGrant.StatusEnum.Pending); + Assert.IsNotNull(response.Balances); + } + + #endregion + } +} \ No newline at end of file diff --git a/Adyen.Test/TransfersTest.cs b/Adyen.Test/TransfersTest.cs deleted file mode 100644 index 8656af648..000000000 --- a/Adyen.Test/TransfersTest.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading; -using Adyen.Model; -using Adyen.Model.Transfers; -using Adyen.Service.Transfers; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using NSubstitute; - -namespace Adyen.Test -{ - [TestClass] - public class TransfersTest : BaseTest - { - [TestMethod] - public void GetAllTransactionsTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/transfers/get-all-transactions.json"); - var service = new TransactionsService(client); - var response = service.GetAllTransactions(new DateTime(), new DateTime(), balancePlatform:"balance", accountHolderId:"id"); - ClientInterfaceSubstitute.Received().RequestAsync( - "https://balanceplatform-api-test.adyen.com/btl/v4/transactions?balancePlatform=balance&accountHolderId=id&createdSince=0001-01-01T00%3a00%3a00Z&createdUntil=0001-01-01T00%3a00%3a00Z", - null, null, HttpMethod.Get, CancellationToken.None); - Assert.AreEqual(response.Links.Next.Href, "https://balanceplatform-api-test.adyen.com/btl/v2/transactions?balancePlatform=Bastronaut&createdUntil=2022-03-21T00%3A00%3A00Z&createdSince=2022-03-11T00%3A00%3A00Z&limit=3&cursor=S2B-TSAjOkIrYlIlbjdqe0RreHRyM32lKRSxubXBHRkhHL2E32XitQQz5SfzpucD5HbHwpM1p6NDR1eXVQLFF6MmY33J32sobDxQYT90MHIud1hwLnd6JitcX32xJ"); - } - - [TestMethod] - public void GetSpecificTransactionTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/transfers/get-transaction.json"); - var service = new TransactionsService(client); - var response = service.GetTransaction("id"); - Assert.AreEqual(response.Status, Transaction.StatusEnum.Booked); - Assert.AreEqual(response.Transfer.Id, "48TYZO5ZVURJ2FCW"); - } - - [TestMethod] - public void PostTransferFundsTest() - { - var client = CreateMockTestClientApiKeyBasedRequestAsync("mocks/transfers/transfer-funds.json"); - var service = new TransfersService(client); - var response = service.TransferFunds(new TransferInfo()); - Assert.AreEqual(response.Status, Transfer.StatusEnum.Authorised); - Assert.AreEqual(response.Category, Transfer.CategoryEnum.Bank); - } - } -} \ No newline at end of file diff --git a/Adyen.Test/Webhooks/BalancePlatformWebhookHandlerTest.cs b/Adyen.Test/Webhooks/BalancePlatformWebhookHandlerTest.cs new file mode 100644 index 000000000..3872e1bc6 --- /dev/null +++ b/Adyen.Test/Webhooks/BalancePlatformWebhookHandlerTest.cs @@ -0,0 +1,551 @@ +// using System; +// using Adyen.Model.AcsWebhooks; +// using Adyen.Model.ConfigurationWebhooks; +// using Adyen.Model.NegativeBalanceWarningWebhooks; +// using Adyen.Model.ReportWebhooks; +// using Adyen.Model.TransactionWebhooks; +// using Adyen.Webhooks; +// using Microsoft.VisualStudio.TestTools.UnitTesting; +// using Newtonsoft.Json; +// using CapabilityProblemEntity = Adyen.Model.ConfigurationWebhooks.CapabilityProblemEntity; +// +// namespace Adyen.Test.WebhooksTests +// { +// [TestClass] +// public class BalancePlatformWebhookHandlerTest : BaseTest +// { +// private readonly BalancePlatformWebhookHandler _balancePlatformWebhookHandler = new BalancePlatformWebhookHandler(); +// +// [TestMethod] +// [DataRow("balancePlatform.accountHolder.created", AccountHolderNotificationRequest.TypeEnum.Created)] +// [DataRow("balancePlatform.accountHolder.updated", AccountHolderNotificationRequest.TypeEnum.Updated)] +// public void Given_AccountHolder_Webhook_When_Type_Is_Provided_Result_Should_Deserialize(string type, AccountHolderNotificationRequest.TypeEnum expected) +// { +// // Arrange +// string jsonPayload = @" +// { +// 'data': { +// 'balancePlatform': 'YOUR_BALANCE_PLATFORM', +// 'accountHolder': { +// 'contactDetails': { +// 'address': { +// 'country': 'NL', +// 'houseNumberOrName': '274', +// 'postalCode': '1020CD', +// 'street': 'Brannan Street' +// }, +// 'email': 's.hopper@example.com', +// 'phone': { +// 'number': '+315551231234', +// 'type': 'Mobile' +// } +// }, +// 'description': 'S.Hopper - Staff 123', +// 'id': 'AH00000000000000000000001', +// 'status': 'Active' +// } +// }, +// 'environment': 'test', +// 'type': '" + type + "'" + +// "}"; +// // Act +// _balancePlatformWebhookHandler.GetAccountHolderNotificationRequest(jsonPayload, out AccountHolderNotificationRequest target); +// +// // Assert +// Assert.IsNotNull(target); +// Assert.AreEqual("YOUR_BALANCE_PLATFORM", target.Data.BalancePlatform); +// Assert.AreEqual("NL", target.Data.AccountHolder.ContactDetails.Address.Country); +// Assert.AreEqual("274", target.Data.AccountHolder.ContactDetails.Address.HouseNumberOrName); +// Assert.AreEqual("1020CD", target.Data.AccountHolder.ContactDetails.Address.PostalCode); +// Assert.AreEqual("Brannan Street", target.Data.AccountHolder.ContactDetails.Address.Street); +// Assert.AreEqual("s.hopper@example.com", target.Data.AccountHolder.ContactDetails.Email); +// Assert.AreEqual("+315551231234", target.Data.AccountHolder.ContactDetails.Phone.Number); +// Assert.AreEqual(Phone.TypeEnum.Mobile, target.Data.AccountHolder.ContactDetails.Phone.Type); +// Assert.AreEqual("S.Hopper - Staff 123", target.Data.AccountHolder.Description); +// Assert.AreEqual("AH00000000000000000000001", target.Data.AccountHolder.Id); +// Assert.AreEqual(AccountHolder.StatusEnum.Active, target.Data.AccountHolder.Status); +// Assert.AreEqual("test", target.Environment); +// Assert.AreEqual(expected, target.Type); +// } +// +// [TestMethod] +// public void Given_AccountHolder_Webhook_When_Type_Is_Unknown_Result_Should_Be_Null() +// { +// string jsonPayload = @"{ 'type': 'unknowntype' }"; +// bool response = _balancePlatformWebhookHandler.GetAccountHolderNotificationRequest(jsonPayload, out AccountHolderNotificationRequest target); +// Assert.IsFalse(response); +// Assert.IsNull(target); +// } +// +// [TestMethod] +// public void Given_AccountHolder_Webhook_When_Invalid_Json_Result_Should_Throw_JsonReaderException() +// { +// string jsonPayload = "{ invalid,.json; }"; +// Assert.ThrowsException(() => _balancePlatformWebhookHandler.GetAccountHolderNotificationRequest(jsonPayload, out var _)); +// } +// +// [TestMethod] +// +// public void Test_BalancePlatform_AccountHolderUpdated_LEM_V3() +// { +// // Note: We're using double-quotes here as some descriptions in the JSON payload contain single quotes ' +// // Assert +// const string jsonPayload = @" +// { +// ""data"": { +// ""balancePlatform"": ""YOUR_BALANCE_PLATFORM"", +// ""accountHolder"": { +// ""legalEntityId"": ""LE00000000000000000001"", +// ""reference"": ""YOUR_REFERENCE-2412C"", +// ""capabilities"": { +// ""sendToTransferInstrument"": { +// ""enabled"": true, +// ""requested"": true, +// ""allowed"": false, +// ""problems"": [ +// { +// ""entity"": { +// ""id"": ""LE00000000000000000001"", +// ""type"": ""LegalEntity"" +// }, +// ""verificationErrors"": [ +// { +// ""code"": ""2_902"", +// ""message"": ""Terms Of Service forms are not accepted."", +// ""remediatingActions"": [ +// { +// ""code"": ""2_902"", +// ""message"": ""Accept TOS"" +// } +// ], +// ""type"": ""invalidInput"" +// } +// ] +// }, +// { +// ""entity"": { +// ""id"": ""SE00000000000000000001"", +// ""type"": ""BankAccount"" +// }, +// ""verificationErrors"": [ +// { +// ""code"": ""2_8037"", +// ""message"": ""'bankStatement' was missing."", +// ""remediatingActions"": [ +// { +// ""code"": ""1_703"", +// ""message"": ""Upload a bank statement"" +// } +// ], +// ""type"": ""dataMissing"" +// } +// ] +// }, +// { +// ""entity"": { +// ""id"": ""SE00000000000000000002"", +// ""type"": ""BankAccount"" +// }, +// ""verificationErrors"": [ +// { +// ""code"": ""2_8037"", +// ""message"": ""'bankStatement' was missing."", +// ""remediatingActions"": [ +// { +// ""code"": ""1_703"", +// ""message"": ""Upload a bank statement"" +// } +// ], +// ""type"": ""dataMissing"" +// } +// ] +// }, +// { +// ""entity"": { +// ""id"": ""LE00000000000000000001"", +// ""type"": ""LegalEntity"" +// }, +// ""verificationErrors"": [ +// { +// ""code"": ""2_8189"", +// ""message"": ""'UBO through control' was missing."", +// ""remediatingActions"": [ +// { +// ""code"": ""2_151"", +// ""message"": ""Add 'organization.entityAssociations' of type 'uboThroughControl' to legal entity"" +// } +// ], +// ""type"": ""dataMissing"" +// }, +// { +// ""code"": ""1_50"", +// ""message"": ""Organization details couldn't be verified"", +// ""subErrors"": [ +// { +// ""code"": ""1_5016"", +// ""message"": ""The tax ID number couldn't be verified"", +// ""remediatingActions"": [ +// { +// ""code"": ""1_500"", +// ""message"": ""Update organization details"" +// }, +// { +// ""code"": ""1_501"", +// ""message"": ""Upload a registration document"" +// } +// ], +// ""type"": ""invalidInput"" +// } +// ], +// ""type"": ""invalidInput"" +// }, +// { +// ""code"": ""2_8067"", +// ""message"": ""'Signatory' was missing."", +// ""remediatingActions"": [ +// { +// ""code"": ""2_124"", +// ""message"": ""Add 'organization.entityAssociations' of type 'signatory' to legal entity"" +// } +// ], +// ""type"": ""dataMissing"" +// } +// ] +// } +// ], +// ""transferInstruments"": [ +// { +// ""enabled"": true, +// ""requested"": true, +// ""allowed"": false, +// ""id"": ""SE00000000000000000001"", +// ""verificationStatus"": ""pending"" +// }, +// { +// ""enabled"": true, +// ""requested"": true, +// ""allowed"": false, +// ""id"": ""SE00000000000000000002"", +// ""verificationStatus"": ""pending"" +// } +// ], +// ""verificationStatus"": ""pending"" +// } +// }, +// ""id"": ""AH00000000000000000001"", +// ""status"": ""active"" +// } +// }, +// ""environment"": ""live"", +// ""timestamp"": ""2024-12-15T15:42:03+01:00"", +// ""type"": ""balancePlatform.accountHolder.updated"" +// }"; +// +// _balancePlatformWebhookHandler.GetAccountHolderNotificationRequest(jsonPayload, out AccountHolderNotificationRequest accountHolderUpdatedWebhook); +// +// Assert.IsNotNull(accountHolderUpdatedWebhook); +// Assert.AreEqual(accountHolderUpdatedWebhook.Type, AccountHolderNotificationRequest.TypeEnum.Updated); +// Assert.AreEqual("YOUR_BALANCE_PLATFORM", accountHolderUpdatedWebhook.Data.BalancePlatform); +// Assert.AreEqual("AH00000000000000000001", accountHolderUpdatedWebhook.Data.AccountHolder.Id); +// Assert.AreEqual("LE00000000000000000001", accountHolderUpdatedWebhook.Data.AccountHolder.LegalEntityId); +// Assert.AreEqual("YOUR_REFERENCE-2412C", accountHolderUpdatedWebhook.Data.AccountHolder.Reference); +// Assert.AreEqual(AccountHolder.StatusEnum.Active, accountHolderUpdatedWebhook.Data.AccountHolder.Status); +// Assert.IsTrue(accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities.TryGetValue("sendToTransferInstrument", out AccountHolderCapability capabilitiesData)); +// Assert.AreEqual(true, capabilitiesData.Enabled); +// Assert.AreEqual(true, capabilitiesData.Requested); +// Assert.AreEqual(false, capabilitiesData.Allowed); +// Assert.AreEqual(AccountHolderCapability.VerificationStatusEnum.Pending, capabilitiesData.VerificationStatus); +// Assert.AreEqual(4, accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems.Count); +// Assert.AreEqual("LE00000000000000000001", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[0].Entity.Id); +// Assert.AreEqual(CapabilityProblemEntity.TypeEnum.LegalEntity, accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[0].Entity.Type); +// Assert.AreEqual("2_902", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[0].VerificationErrors[0].Code); +// Assert.AreEqual("Terms Of Service forms are not accepted.", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[0].VerificationErrors[0].Message); +// Assert.AreEqual("Accept TOS", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[0].VerificationErrors[0].RemediatingActions[0].Message); +// Assert.AreEqual("SE00000000000000000001", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[1].Entity.Id); +// Assert.AreEqual(CapabilityProblemEntity.TypeEnum.BankAccount, accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[1].Entity.Type); +// Assert.AreEqual("2_8037", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[1].VerificationErrors[0].Code); +// Assert.AreEqual("'bankStatement' was missing.", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[1].VerificationErrors[0].Message); +// Assert.AreEqual("Upload a bank statement", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[1].VerificationErrors[0].RemediatingActions[0].Message); +// Assert.AreEqual("SE00000000000000000002", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[2].Entity.Id); +// Assert.AreEqual(CapabilityProblemEntity.TypeEnum.BankAccount, accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[2].Entity.Type); +// Assert.AreEqual("2_8037", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[2].VerificationErrors[0].Code); +// Assert.AreEqual("'bankStatement' was missing.", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[2].VerificationErrors[0].Message); +// Assert.AreEqual("Upload a bank statement", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[2].VerificationErrors[0].RemediatingActions[0].Message); +// Assert.AreEqual("LE00000000000000000001", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[3].Entity.Id); +// Assert.AreEqual(CapabilityProblemEntity.TypeEnum.LegalEntity, accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[3].Entity.Type); +// Assert.AreEqual("2_8189", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[3].VerificationErrors[0].Code); +// Assert.AreEqual("'UBO through control' was missing.", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[3].VerificationErrors[0].Message); +// Assert.AreEqual("Add 'organization.entityAssociations' of type 'uboThroughControl' to legal entity", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[3].VerificationErrors[0].RemediatingActions[0].Message); +// Assert.AreEqual("live", accountHolderUpdatedWebhook.Environment); +// Assert.AreEqual(DateTime.Parse("2024-12-15T15:42:03+01:00"), accountHolderUpdatedWebhook.Timestamp); +// } +// +// [TestMethod] +// public void Test_POC_Integration() +// { +// string jsonPayload = @" +// { +// 'data': { +// 'balancePlatform': 'YOUR_BALANCE_PLATFORM', +// 'accountHolder': { +// 'contactDetails': { +// 'address': { +// 'country': 'NL', +// 'houseNumberOrName': '274', +// 'postalCode': '1020CD', +// 'street': 'Brannan Street' +// }, +// 'email': 's.hopper@example.com', +// 'phone': { +// 'number': '+315551231234', +// 'type': 'Mobile' +// } +// }, +// 'description': 'S.Hopper - Staff 123', +// 'id': 'AH00000000000000000000001', +// 'status': 'Active' +// } +// }, +// 'environment': 'test', +// 'type': 'balancePlatform.accountHolder.created' +// }"; +// +// var handler = new BalancePlatformWebhookHandler(); +// var response = handler.GetGenericBalancePlatformWebhook(jsonPayload); +// try +// { +// if (response.GetType() == typeof(AccountHolderNotificationRequest)) +// { +// var accountHolderNotificationRequest = (AccountHolderNotificationRequest)response; +// Assert.AreEqual(accountHolderNotificationRequest.Environment, "test"); +// } +// +// if (response.GetType() == typeof(BalanceAccountNotificationRequest)) +// { +// var balanceAccountNotificationRequest = (BalanceAccountNotificationRequest)response; +// Assert.Fail(balanceAccountNotificationRequest.Data.BalancePlatform); +// } +// } +// catch (System.Exception e) +// { +// Assert.Fail(); +// throw new System.Exception(e.ToString()); +// } +// } +// +// [TestMethod] +// public void Test_TransactionWebhook_V4() +// { +// // Arrange +// const string jsonPayload = @" +// { +// 'data': { +// 'id': 'EVJN42272224222B5JB8BRC84N686ZEUR', +// 'amount': { +// 'value': 7000, +// 'currency': 'EUR' +// }, +// 'status': 'booked', +// 'transfer': { +// 'id': 'JN4227222422265', +// 'reference': 'Split_item_1', +// }, +// 'valueDate': '2023-03-01T00:00:00+02:00', +// 'bookingDate': '2023-02-28T13:30:20+02:00', +// 'creationDate': '2023-02-28T13:30:05+02:00', +// 'accountHolder': { +// 'id': 'AH00000000000000000000001', +// 'description': 'Your description for the account holder', +// 'reference': 'Your reference for the account holder' +// }, +// 'balanceAccount': { +// 'id': 'BA00000000000000000000001', +// 'description': 'Your description for the balance account', +// 'reference': 'Your reference for the balance account' +// }, +// 'balancePlatform': 'YOUR_BALANCE_PLATFORM' +// }, +// 'type': 'balancePlatform.transaction.created', +// 'environment': 'test' +// }"; +// Assert.IsFalse(_balancePlatformWebhookHandler.GetPaymentNotificationRequest(jsonPayload, out var _)); +// +// // Act +// _balancePlatformWebhookHandler.GetTransactionNotificationRequestV4(jsonPayload, out TransactionNotificationRequestV4 target); +// +// // Assert +// Assert.IsNotNull(target); +// Assert.AreEqual(TransactionNotificationRequestV4.TypeEnum.BalancePlatformTransactionCreated, target.Type); +// Assert.AreEqual(Transaction.StatusEnum.Booked, target.Data.Status); +// Assert.AreEqual("BA00000000000000000000001", target.Data.BalanceAccount.Id); +// Assert.IsTrue(target.Data.ValueDate.Equals(DateTime.Parse("2023-03-01T00:00:00+02:00"))); +// Assert.AreEqual("YOUR_BALANCE_PLATFORM", target.Data.BalancePlatform); +// Assert.AreEqual("AH00000000000000000000001", target.Data.AccountHolder.Id); +// Assert.AreEqual("Your description for the account holder", target.Data.AccountHolder.Description); +// Assert.AreEqual("Your reference for the account holder", target.Data.AccountHolder.Reference); +// Assert.AreEqual("JN4227222422265", target.Data.Transfer.Id); +// Assert.AreEqual("Split_item_1", target.Data.Transfer.Reference); +// Assert.AreEqual(DateTime.Parse("2023-02-28T13:30:05+02:00"), target.Data.CreationDate); +// Assert.AreEqual(DateTime.Parse("2023-02-28T13:30:20+02:00"), target.Data.BookingDate); +// Assert.AreEqual(7000, target.Data.Amount.Value); +// Assert.AreEqual("EUR", target.Data.Amount.Currency); +// Assert.AreEqual("test", target.Environment); +// } +// +// [TestMethod] +// public void Test_ReportCreatedWebhook() +// { +// // Arrange +// const string jsonPayload = @" +// { +// 'data': { +// 'balancePlatform': 'YOUR_BALANCE_PLATFORM', +// 'creationDate': '2024-07-02T02:01:08+02:00', +// 'id': 'balanceplatform_accounting_report_2024_07_01.csv', +// 'downloadUrl': 'https://balanceplatform-test.adyen.com/balanceplatform/.../.../.../balanceplatform_accounting_report_2024_07_01.csv', +// 'fileName': 'balanceplatform_accounting_report_2024_07_01.csv', +// 'reportType': 'balanceplatform_accounting_report' +// }, +// 'environment': 'test', +// 'timestamp': '2024-07-02T02:01:05+02:00', +// 'type': 'balancePlatform.report.created' +// }"; +// +// // Act +// _balancePlatformWebhookHandler.GetReportNotificationRequest(jsonPayload, out ReportNotificationRequest target); +// +// // Assert +// Assert.IsNotNull(target); +// Assert.AreEqual(target.Type, ReportNotificationRequest.TypeEnum.BalancePlatformReportCreated); +// Assert.AreEqual("YOUR_BALANCE_PLATFORM", target.Data.BalancePlatform); +// Assert.AreEqual("balanceplatform_accounting_report_2024_07_01.csv", target.Data.Id); +// Assert.AreEqual("balanceplatform_accounting_report_2024_07_01.csv", target.Data.FileName); +// Assert.AreEqual("balanceplatform_accounting_report", target.Data.ReportType); +// Assert.AreEqual(DateTime.Parse("2024-07-02T02:01:08+02:00"), target.Data.CreationDate); +// Assert.AreEqual("https://balanceplatform-test.adyen.com/balanceplatform/.../.../.../balanceplatform_accounting_report_2024_07_01.csv", target.Data.DownloadUrl); +// Assert.AreEqual("test", target.Environment); +// Assert.AreEqual(DateTime.Parse("2024-07-02T02:01:05+02:00"), target.Timestamp); +// } +// +// [TestMethod] +// public void Test_AuthenticationCreatedWebhook() +// { +// // Arrange +// const string jsonPayload = @" +// { +// 'data': { +// 'authentication': { +// 'acsTransId': '6a4c1709-a42e-4c7f-96c7-1043adacfc97', +// 'challenge': { +// 'flow': 'OOB_TRIGGER_FL', +// 'lastInteraction': '2022-12-22T15:49:03+01:00' +// }, +// 'challengeIndicator': '01', +// 'createdAt': '2022-12-22T15:45:03+01:00', +// 'deviceChannel': 'app', +// 'dsTransID': 'a3b86754-444d-46ca-95a2-ada351d3f42c', +// 'exemptionIndicator': 'lowValue', +// 'inPSD2Scope': true, +// 'messageCategory': 'payment', +// 'messageVersion': '2.2.0', +// 'riskScore': 0, +// 'threeDSServerTransID': '6edcc246-23ee-4e94-ac5d-8ae620bea7d9', +// 'transStatus': 'Y', +// 'type': 'challenge' +// }, +// 'balancePlatform': 'YOUR_BALANCE_PLATFORM', +// 'id': '497f6eca-6276-4993-bfeb-53cbbbba6f08', +// 'paymentInstrumentId': 'PI3227C223222B5BPCMFXD2XG', +// 'purchase': { +// 'date': '2022-12-22T15:49:03+01:00', +// 'merchantName': 'MyShop', +// 'originalAmount': { +// 'currency': 'EUR', +// 'value': 1000 +// } +// }, +// 'status': 'authenticated' +// }, +// 'environment': 'test', +// 'timestamp': '2022-12-22T15:42:03+01:00', +// 'type': 'balancePlatform.authentication.created' +// }"; +// +// // Act +// _balancePlatformWebhookHandler.GetAuthenticationNotificationRequest(jsonPayload, out AuthenticationNotificationRequest target); +// +// // Assert +// Assert.IsNotNull(target); +// Assert.AreEqual(target.Type, AuthenticationNotificationRequest.TypeEnum.BalancePlatformAuthenticationCreated); +// Assert.AreEqual("YOUR_BALANCE_PLATFORM", target.Data.BalancePlatform); +// Assert.AreEqual("497f6eca-6276-4993-bfeb-53cbbbba6f08", target.Data.Id); +// Assert.AreEqual("PI3227C223222B5BPCMFXD2XG", target.Data.PaymentInstrumentId); +// Assert.AreEqual(AuthenticationNotificationData.StatusEnum.Authenticated, target.Data.Status); +// Assert.AreEqual("MyShop", target.Data.Purchase.MerchantName); +// Assert.AreEqual("2022-12-22T15:49:03+01:00", target.Data.Purchase.Date); +// Assert.AreEqual("EUR", target.Data.Purchase.OriginalAmount.Currency); +// Assert.AreEqual(1000, target.Data.Purchase.OriginalAmount.Value); +// Assert.AreEqual(DateTime.Parse("2022-12-22T15:45:03+01:00"), target.Data.Authentication.CreatedAt); +// Assert.AreEqual(AuthenticationInfo.DeviceChannelEnum.App, target.Data.Authentication.DeviceChannel); +// Assert.AreEqual(ChallengeInfo.FlowEnum.OOBTRIGGERFL, target.Data.Authentication.Challenge.Flow); +// Assert.AreEqual(DateTime.Parse("2022-12-22T15:49:03+01:00"), target.Data.Authentication.Challenge.LastInteraction); +// Assert.AreEqual(AuthenticationInfo.ChallengeIndicatorEnum._01, target.Data.Authentication.ChallengeIndicator); +// Assert.AreEqual(AuthenticationInfo.ExemptionIndicatorEnum.LowValue, target.Data.Authentication.ExemptionIndicator); +// Assert.AreEqual(true, target.Data.Authentication.InPSD2Scope); +// Assert.AreEqual(AuthenticationInfo.MessageCategoryEnum.Payment, target.Data.Authentication.MessageCategory); +// Assert.AreEqual("2.2.0", target.Data.Authentication.MessageVersion); +// Assert.AreEqual(0, target.Data.Authentication.RiskScore); +// Assert.AreEqual("6edcc246-23ee-4e94-ac5d-8ae620bea7d9", target.Data.Authentication.ThreeDSServerTransID); +// Assert.AreEqual(AuthenticationInfo.TransStatusEnum.Y, target.Data.Authentication.TransStatus); +// Assert.AreEqual("test", target.Environment); +// Assert.AreEqual(DateTime.Parse("2022-12-22T15:42:03+01:00"), target.Timestamp); +// } +// +// [TestMethod] +// public void Test_NegativeBalanceCompensationWarningWebhook() +// { +// // Arrange +// const string jsonPayload = @" +// { +// 'data': { +// 'balancePlatform': 'YOUR_BALANCE_PLATFORM', +// 'creationDate': '2024-07-02T02:01:08+02:00', +// 'id': 'BA00000000000000000001', +// 'accountHolder': { +// 'description': 'Description for the account holder.', +// 'reference': 'YOUR_REFERENCE', +// 'id': 'AH00000000000000000001' +// }, +// 'amount': { +// 'currency': 'EUR', +// 'value': -145050 +// }, +// 'liableBalanceAccountId': 'BA11111111111111111111', +// 'negativeBalanceSince': '2024-10-19T00:33:13+02:00', +// 'scheduledCompensationAt': '2024-12-01T01:00:00+01:00' +// }, +// 'environment': 'test', +// 'timestamp': '2024-10-22T00:00:00+02:00', +// 'type': 'balancePlatform.negativeBalanceCompensationWarning.scheduled' +// }"; +// +// // Act +// _balancePlatformWebhookHandler.GetNegativeBalanceCompensationWarningNotificationRequest(jsonPayload, out NegativeBalanceCompensationWarningNotificationRequest target); +// +// // Assert +// Assert.IsNotNull(target); +// Assert.AreEqual(NegativeBalanceCompensationWarningNotificationRequest.TypeEnum.BalancePlatformNegativeBalanceCompensationWarningScheduled, target.Type); +// Assert.AreEqual("YOUR_BALANCE_PLATFORM", target.Data.BalancePlatform); +// Assert.AreEqual(DateTime.Parse("2024-07-02T02:01:08+02:00"), target.Data.CreationDate); +// Assert.AreEqual("BA00000000000000000001", target.Data.Id); +// Assert.AreEqual("YOUR_REFERENCE", target.Data.AccountHolder.Reference); +// Assert.AreEqual("EUR", target.Data.Amount.Currency); +// Assert.AreEqual(-145050, target.Data.Amount.Value); +// Assert.AreEqual("BA11111111111111111111", target.Data.LiableBalanceAccountId); +// Assert.AreEqual(DateTime.Parse("2024-10-19T00:33:13+02:00"), target.Data.NegativeBalanceSince); +// Assert.AreEqual(DateTime.Parse("2024-12-01T01:00:00+01:00"), target.Data.ScheduledCompensationAt); +// Assert.AreEqual("test", target.Environment); +// Assert.AreEqual(DateTime.Parse("2024-10-22T00:00:00+02:00"), target.Timestamp); +// } +// } +// } \ No newline at end of file diff --git a/Adyen.Test/UtilTest.cs b/Adyen.Test/Webhooks/HmacValidatorUtilityTest.cs similarity index 69% rename from Adyen.Test/UtilTest.cs rename to Adyen.Test/Webhooks/HmacValidatorUtilityTest.cs index 8bea2e2cb..600808fd8 100644 --- a/Adyen.Test/UtilTest.cs +++ b/Adyen.Test/Webhooks/HmacValidatorUtilityTest.cs @@ -1,212 +1,193 @@ -using System.Collections.Generic; -using Adyen.Model.Notification; -using Adyen.Model.Payment; -using Adyen.Util; -using Adyen.Util.TerminalApi; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Newtonsoft.Json; - -namespace Adyen.Test -{ - [TestClass] - public class UtilTest : BaseTest - { - [TestMethod] - public void TestHmac() - { - var data = "countryCode:currencyCode:merchantAccount:merchantReference:paymentAmount:sessionValidity:skinCode:NL:EUR:MagentoMerchantTest2:TEST-PAYMENT-2017-02-01-14\\:02\\:05:199:2017-02-02T14\\:02\\:05+01\\:00:PKz2KML1"; - var hmacKey = "DFB1EB5485895CFA84146406857104ABB4CBCABDC8AAF103A624C8F6A3EAAB00"; - var hmacValidator = new HmacValidator(); - var hmacSignature = hmacValidator.CalculateHmac(data, hmacKey); - Assert.IsTrue(string.Equals(hmacSignature, "34oR8T1whkQWTv9P+SzKyp8zhusf9n0dpqrm9nsqSJs=")); - } - - [TestMethod] - public void TestBalancePlatformHmac() - { - var notification = - "{\"data\":{\"balancePlatform\":\"Integration_tools_test\",\"accountId\":\"BA32272223222H5HVKTBK4MLB\",\"sweep\":{\"id\":\"SWPC42272223222H5HVKV6H8C64DP5\",\"schedule\":{\"type\":\"balance\"},\"status\":\"active\",\"targetAmount\":{\"currency\":\"EUR\",\"value\":0},\"triggerAmount\":{\"currency\":\"EUR\",\"value\":0},\"type\":\"pull\",\"counterparty\":{\"balanceAccountId\":\"BA3227C223222H5HVKT3H9WLC\"},\"currency\":\"EUR\"}},\"environment\":\"test\",\"type\":\"balancePlatform.balanceAccountSweep.updated\"}"; - var hmacKey = "D7DD5BA6146493707BF0BE7496F6404EC7A63616B7158EC927B9F54BB436765F"; - var hmacSignature = "9Qz9S/0xpar1klkniKdshxpAhRKbiSAewPpWoxKefQA="; - var hmacValidator = new HmacValidator(); - bool response = hmacValidator.IsValidWebhook(hmacSignature, hmacKey, notification); - Assert.IsTrue(response); - } - - [TestMethod] - public void TestSerializationShopperInteractionDefaultIsZero() - { - var paymentRequest = MockPaymentData.CreateFullPaymentRequestWithShopperInteraction(default); - var serializedPaymentRequest = paymentRequest.ToJson(); - Assert.IsTrue(serializedPaymentRequest.Contains("\"shopperInteraction\": 0,")); - } - - [TestMethod] - public void TestNotificationRequestItemHmac() - { - var hmacKey = "DFB1EB5485895CFA84146406857104ABB4CBCABDC8AAF103A624C8F6A3EAAB00"; - var expectedSign = "ipnxGCaUZ4l8TUW75a71/ghd2Fe5ffvX0pV4TLTntIc="; - var additionalData = new Dictionary - { - { "hmacSignature", expectedSign } - }; - var notificationRequestItem = new NotificationRequestItem - { - PspReference = "pspReference", - OriginalReference = "originalReference", - MerchantAccountCode = "merchantAccount", - MerchantReference = "reference", - Amount = new Model.Checkout.Amount("EUR", 1000), - EventCode = "EVENT", - Success = true, - AdditionalData = additionalData - }; - var hmacValidator = new HmacValidator(); - var data = hmacValidator.GetDataToSign(notificationRequestItem); - Assert.AreEqual("pspReference:originalReference:merchantAccount:reference:1000:EUR:EVENT:true", data); - var encrypted = hmacValidator.CalculateHmac(notificationRequestItem, hmacKey); - Assert.AreEqual(expectedSign, encrypted); - Assert.IsTrue(hmacValidator.IsValidHmac(notificationRequestItem, hmacKey)); - notificationRequestItem.AdditionalData["hmacSignature"] = "notValidSign"; - Assert.IsFalse(hmacValidator.IsValidHmac(notificationRequestItem, hmacKey)); - } - - [TestMethod] - public void TestHmacCalculationNotificationRequestWithSpecialChars() - { - string hmacKey = "66B61474A0AA3736BA8789EDC6D6CD9EBA0C4F414A554E32A407F849C045C69D"; - var mockPath = GetMockFilePath("mocks/notification-response-refund-fail.json"); - var response = MockFileToString(mockPath); - var hmacValidator = new HmacValidator(); - var notificationRequest = JsonOperation.Deserialize(response); - var notificationItem = notificationRequest.NotificationItemContainers[0].NotificationItem; - var isValidHmac = hmacValidator.IsValidHmac(notificationItem, hmacKey); - Assert.IsTrue(isValidHmac); - } - - [TestMethod] - public void TestSerializationShopperInteractionMoto() - { - var paymentRequest = MockPaymentData.CreateFullPaymentRequestWithShopperInteraction(PaymentRequest.ShopperInteractionEnum.Moto); - var serializedPaymentRequest = JsonOperation.SerializeRequest(paymentRequest); - StringAssert.Contains(serializedPaymentRequest, nameof(PaymentRequest.ShopperInteractionEnum.Moto)); - } - - [TestMethod] - public void TestNullHmacValidator() - { - var hmacValidator = new HmacValidator(); - var notificationRequestItem = new NotificationRequestItem - { - PspReference = "pspReference", - OriginalReference = "originalReference", - MerchantAccountCode = "merchantAccount", - MerchantReference = "reference", - Amount = new Model.Checkout.Amount("EUR", 1000), - EventCode = "EVENT", - Success = true, - AdditionalData = null - }; - var isValidHmacAdditionalDataNull = hmacValidator.IsValidHmac(notificationRequestItem, "hmacKey"); - Assert.IsFalse(isValidHmacAdditionalDataNull); - notificationRequestItem.AdditionalData = new Dictionary(); - var isValidHmacAdditionalDataEmpty = hmacValidator.IsValidHmac(notificationRequestItem, "hmacKey"); - Assert.IsFalse(isValidHmacAdditionalDataEmpty); - } - - [TestMethod] - public void TestColonAndBackslashHmacValidator() - { - var hmacValidator = new HmacValidator(); - var jsonNotification = @"{ - 'additionalData': { - 'acquirerCode': 'TestPmmAcquirer', - 'acquirerReference': 'J8DXDJ2PV6P', - 'authCode': '052095', - 'avsResult': '5 No AVS data provided', - 'avsResultRaw': '5', - 'cardSummary': '1111', - 'checkout.cardAddedBrand': 'visa', - 'cvcResult': '1 Matches', - 'cvcResultRaw': 'M', - 'expiryDate': '03/2030', - 'hmacSignature': 'CZErGCNQaSsxbaQfZaJlakqo7KPP+mIa8a+wx3yNs9A=', - 'paymentMethod': 'visa', - 'refusalReasonRaw': 'AUTHORISED', - 'retry.attempt1.acquirer': 'TestPmmAcquirer', - 'retry.attempt1.acquirerAccount': 'TestPmmAcquirerAccount', - 'retry.attempt1.avsResultRaw': '5', - 'retry.attempt1.rawResponse': 'AUTHORISED', - 'retry.attempt1.responseCode': 'Approved', - 'retry.attempt1.scaExemptionRequested': 'lowValue', - 'scaExemptionRequested': 'lowValue' - }, - 'amount': { - 'currency': 'EUR', - 'value': 1000 - }, - 'eventCode': 'AUTHORISATION', - 'eventDate': '2023-01-10T13:42:29+01:00', - 'merchantAccountCode': 'AntoniStroinski', - 'merchantReference': '\\:/\\/slashes are fun', - 'operations': [ - 'CANCEL', - 'CAPTURE', - 'REFUND' - ], - 'paymentMethod': 'visa', - 'pspReference': 'ZVWN7D3WSMK2WN82', - 'reason': '052095:1111:03/2030', - 'success': 'true' - }"; - var notificationRequestItem = JsonConvert.DeserializeObject(jsonNotification); - var isValidHmac = hmacValidator.IsValidHmac(notificationRequestItem, "74F490DD33F7327BAECC88B2947C011FC02D014A473AAA33A8EC93E4DC069174"); - Assert.IsTrue(isValidHmac); - } - - [TestMethod] - public void TestCardAcquisitionAdditionalResponse() - { - string jsonString = @"{ - ""additionalData"": { - ""PaymentAccountReference"": ""Yv6zs1234567890ASDFGHJKL"", - ""alias"": ""G12345678"", - ""aliasType"": ""Default"", - ""applicationLabel"": ""ASDFGHJKL"", - ""applicationPreferredName"": ""ASDFGHJKL"", - ""backendGiftcardIndicator"": ""false"", - ""cardBin"": ""4111111"", - ""cardHolderName"": ""John Smith"" - }, - ""message"": ""CARD_ACQ_COMPLETED"", - ""store"": ""NL"" - }"; - string responseBased64 = "ewoiYWRkaXRpb25hbERhdGEiOnsKICJQYXltZW50QWNjb3VudFJlZmVyZW5jZSI6Ill2NnpzMTIzNDU2Nzg5MEFTREZHSEpLTCIsCiJhbGlhcyI6IkcxMjM0NTY3OCIsCiJhbGlhc1R5cGUiOiJEZWZhdWx0IiwKImFwcGxpY2F0aW9uTGFiZWwiOiJBU0RGR0hKS0wiLAoiYXBwbGljYXRpb25QcmVmZXJyZWROYW1lIjoiQVNERkdISktMIiwKICJiYWNrZW5kR2lmdGNhcmRJbmRpY2F0b3IiOiJmYWxzZSIsCiJjYXJkQmluIjoiNDExMTExMSIsCiJjYXJkSG9sZGVyTmFtZSI6IkpvaG4gU21pdGgiCgp9LAoibWVzc2FnZSI6IkNBUkRfQUNRX0NPTVBMRVRFRCIsCiJzdG9yZSI6Ik5MIgp9"; - var additionalResponse = CardAcquisitionUtil.AdditionalResponse(responseBased64); - Assert.AreEqual(additionalResponse.Message,"CARD_ACQ_COMPLETED"); - Assert.AreEqual(additionalResponse.Store,"NL"); - Assert.AreEqual(additionalResponse.AdditionalData["PaymentAccountReference"],"Yv6zs1234567890ASDFGHJKL"); - Assert.AreEqual(additionalResponse.AdditionalData["alias"],"G12345678"); - Assert.AreEqual(additionalResponse.AdditionalData["aliasType"],"Default"); - Assert.AreEqual(additionalResponse.AdditionalData["applicationLabel"],"ASDFGHJKL"); - Assert.AreEqual(additionalResponse.AdditionalData["applicationPreferredName"],"ASDFGHJKL"); - Assert.AreEqual(additionalResponse.AdditionalData["backendGiftcardIndicator"],"false"); - Assert.AreEqual(additionalResponse.AdditionalData["cardHolderName"],"John Smith"); - Assert.AreEqual(additionalResponse.AdditionalData["cardBin"],"4111111"); - - var additionalResponseFromJson = CardAcquisitionUtil.AdditionalResponse(jsonString); - Assert.AreEqual(additionalResponseFromJson.Message,"CARD_ACQ_COMPLETED"); - Assert.AreEqual(additionalResponseFromJson.Store,"NL"); - Assert.AreEqual(additionalResponseFromJson.AdditionalData["PaymentAccountReference"],"Yv6zs1234567890ASDFGHJKL"); - Assert.AreEqual(additionalResponseFromJson.AdditionalData["alias"],"G12345678"); - Assert.AreEqual(additionalResponseFromJson.AdditionalData["aliasType"],"Default"); - Assert.AreEqual(additionalResponseFromJson.AdditionalData["applicationLabel"],"ASDFGHJKL"); - Assert.AreEqual(additionalResponseFromJson.AdditionalData["applicationPreferredName"],"ASDFGHJKL"); - Assert.AreEqual(additionalResponseFromJson.AdditionalData["backendGiftcardIndicator"],"false"); - Assert.AreEqual(additionalResponseFromJson.AdditionalData["cardHolderName"],"John Smith"); - Assert.AreEqual(additionalResponseFromJson.AdditionalData["cardBin"],"4111111"); - - } - - } -} +using Adyen.Webhooks; +using Adyen.Webhooks.Models; +using Adyen.Util.TerminalApi; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; + +namespace Adyen.Test.Webhooks +{ + [TestClass] + public class HmacValidatorUtilityTest : BaseTest + { + [TestMethod] + public void TestHmac() + { + string data = "countryCode:currencyCode:merchantAccount:merchantReference:paymentAmount:sessionValidity:skinCode:NL:EUR:MagentoMerchantTest2:TEST-PAYMENT-2017-02-01-14\\:02\\:05:199:2017-02-02T14\\:02\\:05+01\\:00:PKz2KML1"; + string hmacKey = "DFB1EB5485895CFA84146406857104ABB4CBCABDC8AAF103A624C8F6A3EAAB00"; + var hmacValidator = new HmacValidatorUtility(); + string hmacSignature = hmacValidator.CalculateHmac(data, hmacKey); + Assert.IsTrue(string.Equals(hmacSignature, "34oR8T1whkQWTv9P+SzKyp8zhusf9n0dpqrm9nsqSJs=")); + } + + [TestMethod] + public void TestBalancePlatformHmac() + { + string notification = + "{\"data\":{\"balancePlatform\":\"Integration_tools_test\",\"accountId\":\"BA32272223222H5HVKTBK4MLB\",\"sweep\":{\"id\":\"SWPC42272223222H5HVKV6H8C64DP5\",\"schedule\":{\"type\":\"balance\"},\"status\":\"active\",\"targetAmount\":{\"currency\":\"EUR\",\"value\":0},\"triggerAmount\":{\"currency\":\"EUR\",\"value\":0},\"type\":\"pull\",\"counterparty\":{\"balanceAccountId\":\"BA3227C223222H5HVKT3H9WLC\"},\"currency\":\"EUR\"}},\"environment\":\"test\",\"type\":\"balancePlatform.balanceAccountSweep.updated\"}"; + string hmacKey = "D7DD5BA6146493707BF0BE7496F6404EC7A63616B7158EC927B9F54BB436765F"; + string hmacSignature = "9Qz9S/0xpar1klkniKdshxpAhRKbiSAewPpWoxKefQA="; + var hmacValidator = new HmacValidatorUtility(); + bool response = hmacValidator.IsValidWebhook(hmacSignature, hmacKey, notification); + Assert.IsTrue(response); + } + + + [TestMethod] + public void TestNotificationRequestItemHmac() + { + string hmacKey = "DFB1EB5485895CFA84146406857104ABB4CBCABDC8AAF103A624C8F6A3EAAB00"; + string expectedSign = "ipnxGCaUZ4l8TUW75a71/ghd2Fe5ffvX0pV4TLTntIc="; + var additionalData = new Dictionary + { + { "hmacSignature", expectedSign } + }; + var notificationRequestItem = new NotificationRequestItem + { + PspReference = "pspReference", + OriginalReference = "originalReference", + MerchantAccountCode = "merchantAccount", + MerchantReference = "reference", + Amount = new Amount("EUR", 1000), + EventCode = "EVENT", + Success = true, + AdditionalData = additionalData + }; + var hmacValidator = new HmacValidatorUtility(); + string data = hmacValidator.GetDataToSign(notificationRequestItem); + Assert.AreEqual("pspReference:originalReference:merchantAccount:reference:1000:EUR:EVENT:true", data); + string encrypted = hmacValidator.CalculateHmac(notificationRequestItem, hmacKey); + Assert.AreEqual(expectedSign, encrypted); + Assert.IsTrue(hmacValidator.IsValidHmac(notificationRequestItem, hmacKey)); + notificationRequestItem.AdditionalData["hmacSignature"] = "notValidSign"; + Assert.IsFalse(hmacValidator.IsValidHmac(notificationRequestItem, hmacKey)); + } + + [TestMethod] + public void TestHmacCalculationNotificationRequestWithSpecialChars() + { + string hmacKey = "66B61474A0AA3736BA8789EDC6D6CD9EBA0C4F414A554E32A407F849C045C69D"; + string mockPath = GetMockFilePath("mocks/notification-response-refund-fail.json"); + string response = MockFileToString(mockPath); + var hmacValidator = new HmacValidatorUtility(); + NotificationRequest notificationRequest = JsonConvert.DeserializeObject(response); + NotificationRequestItem notificationItem = notificationRequest.NotificationItemContainers[0].NotificationItem; + bool isValidHmac = hmacValidator.IsValidHmac(notificationItem, hmacKey); + Assert.IsTrue(isValidHmac); + } + + [TestMethod] + public void TestNullHmacValidator() + { + var hmacValidator = new HmacValidatorUtility(); + var notificationRequestItem = new NotificationRequestItem + { + PspReference = "pspReference", + OriginalReference = "originalReference", + MerchantAccountCode = "merchantAccount", + MerchantReference = "reference", + Amount = new Amount("EUR", 1000), + EventCode = "EVENT", + Success = true, + AdditionalData = null + }; + bool isValidHmacAdditionalDataNull = hmacValidator.IsValidHmac(notificationRequestItem, "hmacKey"); + Assert.IsFalse(isValidHmacAdditionalDataNull); + notificationRequestItem.AdditionalData = new Dictionary(); + bool isValidHmacAdditionalDataEmpty = hmacValidator.IsValidHmac(notificationRequestItem, "hmacKey"); + Assert.IsFalse(isValidHmacAdditionalDataEmpty); + } + + [TestMethod] + public void TestColonAndBackslashHmacValidator() + { + var hmacValidator = new HmacValidatorUtility(); + string jsonNotification = @"{ + 'additionalData': { + 'acquirerCode': 'TestPmmAcquirer', + 'acquirerReference': 'J8DXDJ2PV6P', + 'authCode': '052095', + 'avsResult': '5 No AVS data provided', + 'avsResultRaw': '5', + 'cardSummary': '1111', + 'checkout.cardAddedBrand': 'visa', + 'cvcResult': '1 Matches', + 'cvcResultRaw': 'M', + 'expiryDate': '03/2030', + 'hmacSignature': 'CZErGCNQaSsxbaQfZaJlakqo7KPP+mIa8a+wx3yNs9A=', + 'paymentMethod': 'visa', + 'refusalReasonRaw': 'AUTHORISED', + 'retry.attempt1.acquirer': 'TestPmmAcquirer', + 'retry.attempt1.acquirerAccount': 'TestPmmAcquirerAccount', + 'retry.attempt1.avsResultRaw': '5', + 'retry.attempt1.rawResponse': 'AUTHORISED', + 'retry.attempt1.responseCode': 'Approved', + 'retry.attempt1.scaExemptionRequested': 'lowValue', + 'scaExemptionRequested': 'lowValue' + }, + 'amount': { + 'currency': 'EUR', + 'value': 1000 + }, + 'eventCode': 'AUTHORISATION', + 'eventDate': '2023-01-10T13:42:29+01:00', + 'merchantAccountCode': 'AntoniStroinski', + 'merchantReference': '\\:/\\/slashes are fun', + 'operations': [ + 'CANCEL', + 'CAPTURE', + 'REFUND' + ], + 'paymentMethod': 'visa', + 'pspReference': 'ZVWN7D3WSMK2WN82', + 'reason': '052095:1111:03/2030', + 'success': 'true' + }"; + NotificationRequestItem notificationRequestItem = JsonConvert.DeserializeObject(jsonNotification); + bool isValidHmac = hmacValidator.IsValidHmac(notificationRequestItem, "74F490DD33F7327BAECC88B2947C011FC02D014A473AAA33A8EC93E4DC069174"); + Assert.IsTrue(isValidHmac); + } + + [TestMethod] + public void TestCardAcquisitionAdditionalResponse() + { + string jsonString = @"{ + ""additionalData"": { + ""PaymentAccountReference"": ""Yv6zs1234567890ASDFGHJKL"", + ""alias"": ""G12345678"", + ""aliasType"": ""Default"", + ""applicationLabel"": ""ASDFGHJKL"", + ""applicationPreferredName"": ""ASDFGHJKL"", + ""backendGiftcardIndicator"": ""false"", + ""cardBin"": ""4111111"", + ""cardHolderName"": ""John Smith"" + }, + ""message"": ""CARD_ACQ_COMPLETED"", + ""store"": ""NL"" + }"; + string responseBased64 = "ewoiYWRkaXRpb25hbERhdGEiOnsKICJQYXltZW50QWNjb3VudFJlZmVyZW5jZSI6Ill2NnpzMTIzNDU2Nzg5MEFTREZHSEpLTCIsCiJhbGlhcyI6IkcxMjM0NTY3OCIsCiJhbGlhc1R5cGUiOiJEZWZhdWx0IiwKImFwcGxpY2F0aW9uTGFiZWwiOiJBU0RGR0hKS0wiLAoiYXBwbGljYXRpb25QcmVmZXJyZWROYW1lIjoiQVNERkdISktMIiwKICJiYWNrZW5kR2lmdGNhcmRJbmRpY2F0b3IiOiJmYWxzZSIsCiJjYXJkQmluIjoiNDExMTExMSIsCiJjYXJkSG9sZGVyTmFtZSI6IkpvaG4gU21pdGgiCgp9LAoibWVzc2FnZSI6IkNBUkRfQUNRX0NPTVBMRVRFRCIsCiJzdG9yZSI6Ik5MIgp9"; + AdditionalResponse additionalResponse = CardAcquisitionUtil.AdditionalResponse(responseBased64); + Assert.AreEqual(additionalResponse.Message,"CARD_ACQ_COMPLETED"); + Assert.AreEqual(additionalResponse.Store,"NL"); + Assert.AreEqual(additionalResponse.AdditionalData["PaymentAccountReference"],"Yv6zs1234567890ASDFGHJKL"); + Assert.AreEqual(additionalResponse.AdditionalData["alias"],"G12345678"); + Assert.AreEqual(additionalResponse.AdditionalData["aliasType"],"Default"); + Assert.AreEqual(additionalResponse.AdditionalData["applicationLabel"],"ASDFGHJKL"); + Assert.AreEqual(additionalResponse.AdditionalData["applicationPreferredName"],"ASDFGHJKL"); + Assert.AreEqual(additionalResponse.AdditionalData["backendGiftcardIndicator"],"false"); + Assert.AreEqual(additionalResponse.AdditionalData["cardHolderName"],"John Smith"); + Assert.AreEqual(additionalResponse.AdditionalData["cardBin"],"4111111"); + + AdditionalResponse additionalResponseFromJson = CardAcquisitionUtil.AdditionalResponse(jsonString); + Assert.AreEqual(additionalResponseFromJson.Message,"CARD_ACQ_COMPLETED"); + Assert.AreEqual(additionalResponseFromJson.Store,"NL"); + Assert.AreEqual(additionalResponseFromJson.AdditionalData["PaymentAccountReference"],"Yv6zs1234567890ASDFGHJKL"); + Assert.AreEqual(additionalResponseFromJson.AdditionalData["alias"],"G12345678"); + Assert.AreEqual(additionalResponseFromJson.AdditionalData["aliasType"],"Default"); + Assert.AreEqual(additionalResponseFromJson.AdditionalData["applicationLabel"],"ASDFGHJKL"); + Assert.AreEqual(additionalResponseFromJson.AdditionalData["applicationPreferredName"],"ASDFGHJKL"); + Assert.AreEqual(additionalResponseFromJson.AdditionalData["backendGiftcardIndicator"],"false"); + Assert.AreEqual(additionalResponseFromJson.AdditionalData["cardHolderName"],"John Smith"); + Assert.AreEqual(additionalResponseFromJson.AdditionalData["cardBin"],"4111111"); + } + } +} diff --git a/Adyen.Test/Webhooks/ManagementWebhookHandlerTest.cs b/Adyen.Test/Webhooks/ManagementWebhookHandlerTest.cs new file mode 100644 index 000000000..0c891d80f --- /dev/null +++ b/Adyen.Test/Webhooks/ManagementWebhookHandlerTest.cs @@ -0,0 +1,124 @@ +// using System; +// using Adyen.Model.ManagementWebhooks; +// using Adyen.Webhooks; +// using Microsoft.VisualStudio.TestTools.UnitTesting; +// +// namespace Adyen.Test.WebhooksTests +// { +// [TestClass] +// public class ManagementWebhookHandlerTest : BaseTest +// { +// private readonly ManagementWebhookHandler _managementWebhookHandler = new ManagementWebhookHandler(); +// +// [TestMethod] +// public void Test_Management_Webhook_PaymentMethodCreated() +// { +// // Arrange +// const string jsonPayload = @" +// { +// 'createdAt': '2022-01-24T14:59:11+01:00', +// 'data': { +// 'id': 'PM3224R223224K5FH4M2K9B86', +// 'merchantId': 'MERCHANT_ACCOUNT', +// 'status': 'SUCCESS', +// 'storeId': 'ST322LJ223223K5F4SQNR9XL5', +// 'type': 'visa' +// }, +// 'environment': 'test', +// 'type': 'paymentMethod.created' +// }"; +// // Act +// _managementWebhookHandler.GetPaymentMethodCreatedNotificationRequest(jsonPayload, out PaymentMethodCreatedNotificationRequest paymentMethodCreatedWebhook); +// +// // Assert +// Assert.IsNotNull(paymentMethodCreatedWebhook); +// Assert.AreEqual(PaymentMethodCreatedNotificationRequest.TypeEnum.PaymentMethodCreated, paymentMethodCreatedWebhook.Type); +// Assert.AreEqual(paymentMethodCreatedWebhook.Data.Id, "PM3224R223224K5FH4M2K9B86"); +// Assert.AreEqual(paymentMethodCreatedWebhook.Data.MerchantId, "MERCHANT_ACCOUNT"); +// Assert.AreEqual(paymentMethodCreatedWebhook.Data.Status, MidServiceNotificationData.StatusEnum.Success); +// Assert.AreEqual(paymentMethodCreatedWebhook.Data.StoreId, "ST322LJ223223K5F4SQNR9XL5"); +// Assert.AreEqual(paymentMethodCreatedWebhook.Data.Type, "visa"); +// Assert.AreEqual(DateTime.Parse("2022-01-24T14:59:11+01:00"), paymentMethodCreatedWebhook.CreatedAt); +// Assert.AreEqual("test", paymentMethodCreatedWebhook.Environment); +// } +// +// [TestMethod] +// public void Test_Management_Webhook_MerchantUpdated() +// { +// // Arrange +// const string jsonPayload = @" +// { +// 'type': 'merchant.updated', +// 'environment': 'test', +// 'createdAt': '2022-09-20T13:42:31+02:00', +// 'data': { +// 'capabilities': { +// 'receivePayments': { +// 'allowed': true, +// 'requested': true, +// 'requestedLevel': 'notApplicable', +// 'verificationStatus': 'valid' +// } +// }, +// 'legalEntityId': 'LE322KH223222F5GNNW694PZN', +// 'merchantId': 'YOUR_MERCHANT_ID', +// 'status': 'PreActive' +// } +// }"; +// // Act +// _managementWebhookHandler.GetMerchantUpdatedNotificationRequest(jsonPayload, out MerchantUpdatedNotificationRequest target); +// +// // Assert +// Assert.IsNotNull(target); +// Assert.AreEqual(target.Type, MerchantUpdatedNotificationRequest.TypeEnum.MerchantUpdated); +// Assert.AreEqual("LE322KH223222F5GNNW694PZN", target.Data.LegalEntityId); +// Assert.AreEqual("YOUR_MERCHANT_ID", target.Data.MerchantId); +// Assert.AreEqual("PreActive", target.Data.Status); +// Assert.AreEqual(DateTime.Parse("2022-09-20T13:42:31+02:00"), target.CreatedAt); +// Assert.AreEqual("test", target.Environment); +// +// Assert.IsTrue(target.Data.Capabilities.TryGetValue("receivePayments", out AccountCapabilityData accountCapabilityData)); +// Assert.AreEqual(true, accountCapabilityData.Requested); +// Assert.AreEqual("notApplicable", accountCapabilityData.RequestedLevel); +// } +// +// [TestMethod] +// public void Test_Management_Webhook_MerchantCreated() +// { +// // Arrange +// const string jsonPayload = @" +// { +// 'type': 'merchant.created', +// 'environment': 'test', +// 'createdAt': '2022-08-12T10:50:01+02:00', +// 'data': { +// 'capabilities': { +// 'sendToTransferInstrument': { +// 'requested': true, +// 'requestedLevel': 'notApplicable' +// } +// }, +// 'companyId': 'YOUR_COMPANY_ID', +// 'merchantId': 'MC3224X22322535GH8D537TJR', +// 'status': 'PreActive' +// } +// }"; +// Assert.IsFalse(_managementWebhookHandler.GetMerchantUpdatedNotificationRequest(jsonPayload, out _)); +// +// // Act +// _managementWebhookHandler.GetMerchantCreatedNotificationRequest(jsonPayload, out MerchantCreatedNotificationRequest target); +// +// // Assert +// Assert.IsNotNull(target); +// Assert.AreEqual(MerchantCreatedNotificationRequest.TypeEnum.MerchantCreated, target.Type); +// Assert.AreEqual("test", target.Environment); +// Assert.AreEqual("YOUR_COMPANY_ID", target.Data.CompanyId); +// Assert.AreEqual("MC3224X22322535GH8D537TJR", target.Data.MerchantId); +// Assert.AreEqual("PreActive", target.Data.Status); +// Assert.AreEqual(DateTime.Parse("2022-08-12T10:50:01+02:00"), target.CreatedAt); +// Assert.IsTrue(target.Data.Capabilities.TryGetValue("sendToTransferInstrument", out AccountCapabilityData data)); +// Assert.AreEqual("notApplicable", data.RequestedLevel); +// Assert.AreEqual(true, data.Requested); +// } +// } +// } \ No newline at end of file diff --git a/Adyen.Test/Webhooks/WebhookHandlerTest.cs b/Adyen.Test/Webhooks/WebhookHandlerTest.cs new file mode 100644 index 000000000..ae4f1e202 --- /dev/null +++ b/Adyen.Test/Webhooks/WebhookHandlerTest.cs @@ -0,0 +1,173 @@ +// using System; +// using System.Linq; +// using Adyen.ApiSerialization; +// using Adyen.Model.TerminalApi; +// using Adyen.Webhooks; +// using Microsoft.VisualStudio.TestTools.UnitTesting; +// using Newtonsoft.Json; +// using Newtonsoft.Json.Linq; +// +// namespace Adyen.Test.WebhooksTests +// { +// [TestClass] +// public class WebhookHandlerTest : BaseTest +// { +// [TestMethod] +// public void TestAuthorisationSuccess() +// { +// var mockPath = GetMockFilePath("mocks/notification/authorisation-true.json"); +// var jsonRequest = MockFileToString(mockPath); +// var webhookHandler = new WebhookHandler(); +// var handleNotificationRequest = webhookHandler.HandleNotificationRequest(jsonRequest); +// var notificationRequestItemContainer = handleNotificationRequest.NotificationItemContainers.FirstOrDefault(); +// if (notificationRequestItemContainer == null) +// Assert.Fail("NotificationRequestItemContainer is null"); +// var notificationItem = notificationRequestItemContainer.NotificationItem; +// Assert.AreEqual("AUTHORISATION", notificationItem.EventCode); +// Assert.AreEqual("1234", notificationItem.AdditionalData["authCode"]); +// Assert.AreEqual("123456789", notificationItem.PspReference); +// } +// +// [TestMethod] +// public void TestMerchantWebhookPayload() +// { +// var mockPath = GetMockFilePath("mocks/notification/merchant-webhook-payload.json"); +// var jsonRequest = MockFileToString(mockPath); +// var webhookHandler = new WebhookHandler(); +// var handleNotificationRequest = webhookHandler.HandleNotificationRequest(jsonRequest); +// var json1 = JsonConvert.SerializeObject(handleNotificationRequest); +// var json2 = jsonRequest; +// Assert.IsTrue(JToken.DeepEquals(JToken.Parse(json1.ToLower()), JToken.Parse(json2.ToLower()))); +// } +// +// [TestMethod] +// public void TestCaptureSuccess() +// { +// var mockPath = GetMockFilePath("mocks/notification/capture-true.json"); +// var jsonRequest = MockFileToString(mockPath); +// var webhookHandler = new WebhookHandler(); +// var handleNotificationRequest = webhookHandler.HandleNotificationRequest(jsonRequest); +// var notificationRequestItemContainer = handleNotificationRequest.NotificationItemContainers.FirstOrDefault(); +// if (notificationRequestItemContainer == null) +// Assert.Fail("NotificationRequestItemContainer is null"); +// var notificationItem = notificationRequestItemContainer.NotificationItem; +// Assert.AreEqual("CAPTURE", notificationItem.EventCode); +// Assert.AreEqual("qvS6I3Gdi1jx+jSh7IopAgcHtMoxvXlNL7DYQ+j1hd0=", notificationItem.AdditionalData["hmacSignature"]); +// Assert.AreEqual("PSP_REFERENCE", notificationItem.PspReference); +// Assert.AreEqual(23623, notificationItem.Amount.Value); +// Assert.IsTrue(notificationItem.Success); +// } +// +// [TestMethod] +// public void TestCaptureFail() +// { +// var mockPath = GetMockFilePath("mocks/notification/capture-false.json"); +// var jsonRequest = MockFileToString(mockPath); +// var webhookHandler = new WebhookHandler(); +// var handleNotificationRequest = webhookHandler.HandleNotificationRequest(jsonRequest); +// var notificationRequestItemContainer = handleNotificationRequest.NotificationItemContainers.FirstOrDefault(); +// if (notificationRequestItemContainer == null) +// Assert.Fail("NotificationRequestItemContainer is null"); +// var notificationItem = notificationRequestItemContainer.NotificationItem; +// Assert.AreEqual("CAPTURE", notificationItem.EventCode); +// Assert.AreEqual("KujHNqpyCAMdGefj7lfQ8AeD0Jke9Zs2bVAqScQDWi4=", notificationItem.AdditionalData["hmacSignature"]); +// Assert.AreEqual("PSP_REFERENCE", notificationItem.PspReference); +// Assert.AreEqual(23623, notificationItem.Amount.Value); +// Assert.IsFalse(notificationItem.Success); +// Assert.AreEqual("Insufficient balance on payment", notificationItem.Reason); +// } +// +// [TestMethod] +// public void TestRefundSuccess() +// { +// var mockPath = GetMockFilePath("mocks/notification/refund-true.json"); +// var jsonRequest = MockFileToString(mockPath); +// var webhookHandler = new WebhookHandler(); +// var handleNotificationRequest = webhookHandler.HandleNotificationRequest(jsonRequest); +// var notificationRequestItemContainer = handleNotificationRequest.NotificationItemContainers.FirstOrDefault(); +// if (notificationRequestItemContainer == null) +// Assert.Fail("NotificationRequestItemContainer is null"); +// var notificationItem = notificationRequestItemContainer.NotificationItem; +// Assert.AreEqual("REFUND", notificationItem.EventCode); +// Assert.AreEqual("KJFhURWP8Pv9m8k+7NGHNJAupBj6X6J/VWAikFxeWhA=", notificationItem.AdditionalData["hmacSignature"]); +// Assert.AreEqual("PSP_REFERENCE", notificationItem.PspReference); +// Assert.AreEqual(1500, notificationItem.Amount.Value); +// Assert.AreEqual("MagentoMerchantTest2", notificationItem.MerchantAccountCode); +// Assert.IsTrue(notificationItem.Success); +// } +// +// [TestMethod] +// public void TestRefundFail() +// { +// var mockPath = GetMockFilePath("mocks/notification/refund-false.json"); +// var jsonRequest = MockFileToString(mockPath); +// var webhookHandler = new WebhookHandler(); +// var handleNotificationRequest = webhookHandler.HandleNotificationRequest(jsonRequest); +// var notificationRequestItemContainer = handleNotificationRequest.NotificationItemContainers.FirstOrDefault(); +// if (notificationRequestItemContainer == null) +// Assert.Fail("NotificationRequestItemContainer is null"); +// var notificationItem = notificationRequestItemContainer.NotificationItem; +// Assert.AreEqual("REFUND", notificationItem.EventCode); +// Assert.AreEqual("HZXziBYopfDIzDhk49iC//yCfxmy/z0xWuvvTxFNUSA=", notificationItem.AdditionalData["hmacSignature"]); +// Assert.AreEqual("PSP_REFERENCE", notificationItem.PspReference); +// Assert.AreEqual(1500, notificationItem.Amount.Value); +// Assert.IsFalse(notificationItem.Success); +// Assert.AreEqual("Insufficient balance on payment", notificationItem.Reason); +// } +// +// [TestMethod] +// public void TestPOSDisplayNotification() +// { +// var notification = +// "{\"SaleToPOIRequest\":{\"DisplayRequest\":{\"DisplayOutput\":[{\"Device\": \"CashierDisplay\",\"InfoQualify\": \"Status\",\"OutputContent\": {\"OutputFormat\": \"MessageRef\",\"PredefinedContent\": {\"ReferenceID\": \"TransactionID=oLkO001517998574000&TimeStamp=2018-02-07T10%3a16%3a14.000Z&event=TENDER_CREATED\"}},\"ResponseRequiredFlag\": false}]},\"MessageHeader\":{\"SaleID\":\"POSSystemID12345\",\"ProtocolVersion\":\"3.0\",\"MessageType\":\"Request\",\"POIID\":\"V400m-324688179\",\"ServiceID\":\"0207111617\",\"MessageClass\":\"Device\",\"MessageCategory\":\"Display\",\"DeviceID\":\"1517998562\"}}}"; +// var serializer = new SaleToPoiMessageSerializer(); +// var saleToPoiRequest = serializer.DeserializeNotification(notification); +// var displayRequest = (DisplayRequest)saleToPoiRequest.MessagePayload; +// Assert.AreEqual(displayRequest.DisplayOutput[0].OutputContent.OutputFormat, OutputFormatType.MessageRef); +// Assert.AreEqual(displayRequest.DisplayOutput[0].Device, DeviceType.CashierDisplay); +// } +// +// [TestMethod] +// public void TestPOSEventNotification() +// { +// var notification = @"{ +// 'SaleToPOIRequest':{ +// 'EventNotification':{ +// 'EventDetails':'newstate=IDLE&oldstate=START', +// 'EventToNotify':'Shutdown', +// 'TimeStamp':'2019-08-07T10:16:10.000Z' +// }, +// 'MessageHeader':{ +// 'SaleID':'POSSystemID12345', +// 'ProtocolVersion':'3.0', +// 'MessageType':'Notification', +// 'POIID':'V400m-324688179', +// 'MessageClass':'Event', +// 'MessageCategory':'Event', +// 'DeviceID':'1517998561' +// } +// } +// }"; +// var serializer = new SaleToPoiMessageSerializer(); +// var saleToPoiRequest = serializer.DeserializeNotification(notification); +// var eventNotification = (EventNotification) saleToPoiRequest.MessagePayload; +// Assert.AreEqual(eventNotification.EventDetails, "newstate=IDLE&oldstate=START"); +// Assert.AreEqual(eventNotification.TimeStamp, new DateTime(2019, 8, 7, 10, 16, 10)); +// } +// +// [TestMethod] +// public void TestClassicPlatformWebhookParser() +// { +// var mockPath = GetMockFilePath("mocks/notification/classic-platform-webhook.json"); +// var payload = MockFileToString(mockPath); +// +// var parser = new ClassicPlatformWebhookHandler(); +// Assert.IsTrue(parser.GetTransferFundsNotification(payload, out var webhook)); +// Assert.IsFalse(parser.GetAccountCloseNotification(payload, out _)); +// +// JObject expected = (JObject)JsonConvert.DeserializeObject(payload); +// JObject result = (JObject)JsonConvert.DeserializeObject(webhook.ToJson()); +// Assert.IsTrue(JToken.DeepEquals(expected, result)); +// } +// } +// } diff --git a/Adyen.Test/WebhooksTests/BalancePlatformWebhookHandlerTest.cs b/Adyen.Test/WebhooksTests/BalancePlatformWebhookHandlerTest.cs deleted file mode 100644 index 4f23c9517..000000000 --- a/Adyen.Test/WebhooksTests/BalancePlatformWebhookHandlerTest.cs +++ /dev/null @@ -1,551 +0,0 @@ -using System; -using Adyen.Model.AcsWebhooks; -using Adyen.Model.ConfigurationWebhooks; -using Adyen.Model.NegativeBalanceWarningWebhooks; -using Adyen.Model.ReportWebhooks; -using Adyen.Model.TransactionWebhooks; -using Adyen.Webhooks; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Newtonsoft.Json; -using CapabilityProblemEntity = Adyen.Model.ConfigurationWebhooks.CapabilityProblemEntity; - -namespace Adyen.Test.WebhooksTests -{ - [TestClass] - public class BalancePlatformWebhookHandlerTest : BaseTest - { - private readonly BalancePlatformWebhookHandler _balancePlatformWebhookHandler = new BalancePlatformWebhookHandler(); - - [TestMethod] - [DataRow("balancePlatform.accountHolder.created", AccountHolderNotificationRequest.TypeEnum.Created)] - [DataRow("balancePlatform.accountHolder.updated", AccountHolderNotificationRequest.TypeEnum.Updated)] - public void Given_AccountHolder_Webhook_When_Type_Is_Provided_Result_Should_Deserialize(string type, AccountHolderNotificationRequest.TypeEnum expected) - { - // Arrange - string jsonPayload = @" -{ - 'data': { - 'balancePlatform': 'YOUR_BALANCE_PLATFORM', - 'accountHolder': { - 'contactDetails': { - 'address': { - 'country': 'NL', - 'houseNumberOrName': '274', - 'postalCode': '1020CD', - 'street': 'Brannan Street' - }, - 'email': 's.hopper@example.com', - 'phone': { - 'number': '+315551231234', - 'type': 'Mobile' - } - }, - 'description': 'S.Hopper - Staff 123', - 'id': 'AH00000000000000000000001', - 'status': 'Active' - } - }, - 'environment': 'test', - 'type': '" + type + "'" + -"}"; - // Act - _balancePlatformWebhookHandler.GetAccountHolderNotificationRequest(jsonPayload, out AccountHolderNotificationRequest target); - - // Assert - Assert.IsNotNull(target); - Assert.AreEqual("YOUR_BALANCE_PLATFORM", target.Data.BalancePlatform); - Assert.AreEqual("NL", target.Data.AccountHolder.ContactDetails.Address.Country); - Assert.AreEqual("274", target.Data.AccountHolder.ContactDetails.Address.HouseNumberOrName); - Assert.AreEqual("1020CD", target.Data.AccountHolder.ContactDetails.Address.PostalCode); - Assert.AreEqual("Brannan Street", target.Data.AccountHolder.ContactDetails.Address.Street); - Assert.AreEqual("s.hopper@example.com", target.Data.AccountHolder.ContactDetails.Email); - Assert.AreEqual("+315551231234", target.Data.AccountHolder.ContactDetails.Phone.Number); - Assert.AreEqual(Phone.TypeEnum.Mobile, target.Data.AccountHolder.ContactDetails.Phone.Type); - Assert.AreEqual("S.Hopper - Staff 123", target.Data.AccountHolder.Description); - Assert.AreEqual("AH00000000000000000000001", target.Data.AccountHolder.Id); - Assert.AreEqual(AccountHolder.StatusEnum.Active, target.Data.AccountHolder.Status); - Assert.AreEqual("test", target.Environment); - Assert.AreEqual(expected, target.Type); - } - - [TestMethod] - public void Given_AccountHolder_Webhook_When_Type_Is_Unknown_Result_Should_Be_Null() - { - string jsonPayload = @"{ 'type': 'unknowntype' }"; - bool response = _balancePlatformWebhookHandler.GetAccountHolderNotificationRequest(jsonPayload, out AccountHolderNotificationRequest target); - Assert.IsFalse(response); - Assert.IsNull(target); - } - - [TestMethod] - public void Given_AccountHolder_Webhook_When_Invalid_Json_Result_Should_Throw_JsonReaderException() - { - string jsonPayload = "{ invalid,.json; }"; - Assert.ThrowsException(() => _balancePlatformWebhookHandler.GetAccountHolderNotificationRequest(jsonPayload, out var _)); - } - - [TestMethod] - - public void Test_BalancePlatform_AccountHolderUpdated_LEM_V3() - { - // Note: We're using double-quotes here as some descriptions in the JSON payload contain single quotes ' - // Assert - const string jsonPayload = @" -{ - ""data"": { - ""balancePlatform"": ""YOUR_BALANCE_PLATFORM"", - ""accountHolder"": { - ""legalEntityId"": ""LE00000000000000000001"", - ""reference"": ""YOUR_REFERENCE-2412C"", - ""capabilities"": { - ""sendToTransferInstrument"": { - ""enabled"": true, - ""requested"": true, - ""allowed"": false, - ""problems"": [ - { - ""entity"": { - ""id"": ""LE00000000000000000001"", - ""type"": ""LegalEntity"" - }, - ""verificationErrors"": [ - { - ""code"": ""2_902"", - ""message"": ""Terms Of Service forms are not accepted."", - ""remediatingActions"": [ - { - ""code"": ""2_902"", - ""message"": ""Accept TOS"" - } - ], - ""type"": ""invalidInput"" - } - ] - }, - { - ""entity"": { - ""id"": ""SE00000000000000000001"", - ""type"": ""BankAccount"" - }, - ""verificationErrors"": [ - { - ""code"": ""2_8037"", - ""message"": ""'bankStatement' was missing."", - ""remediatingActions"": [ - { - ""code"": ""1_703"", - ""message"": ""Upload a bank statement"" - } - ], - ""type"": ""dataMissing"" - } - ] - }, - { - ""entity"": { - ""id"": ""SE00000000000000000002"", - ""type"": ""BankAccount"" - }, - ""verificationErrors"": [ - { - ""code"": ""2_8037"", - ""message"": ""'bankStatement' was missing."", - ""remediatingActions"": [ - { - ""code"": ""1_703"", - ""message"": ""Upload a bank statement"" - } - ], - ""type"": ""dataMissing"" - } - ] - }, - { - ""entity"": { - ""id"": ""LE00000000000000000001"", - ""type"": ""LegalEntity"" - }, - ""verificationErrors"": [ - { - ""code"": ""2_8189"", - ""message"": ""'UBO through control' was missing."", - ""remediatingActions"": [ - { - ""code"": ""2_151"", - ""message"": ""Add 'organization.entityAssociations' of type 'uboThroughControl' to legal entity"" - } - ], - ""type"": ""dataMissing"" - }, - { - ""code"": ""1_50"", - ""message"": ""Organization details couldn't be verified"", - ""subErrors"": [ - { - ""code"": ""1_5016"", - ""message"": ""The tax ID number couldn't be verified"", - ""remediatingActions"": [ - { - ""code"": ""1_500"", - ""message"": ""Update organization details"" - }, - { - ""code"": ""1_501"", - ""message"": ""Upload a registration document"" - } - ], - ""type"": ""invalidInput"" - } - ], - ""type"": ""invalidInput"" - }, - { - ""code"": ""2_8067"", - ""message"": ""'Signatory' was missing."", - ""remediatingActions"": [ - { - ""code"": ""2_124"", - ""message"": ""Add 'organization.entityAssociations' of type 'signatory' to legal entity"" - } - ], - ""type"": ""dataMissing"" - } - ] - } - ], - ""transferInstruments"": [ - { - ""enabled"": true, - ""requested"": true, - ""allowed"": false, - ""id"": ""SE00000000000000000001"", - ""verificationStatus"": ""pending"" - }, - { - ""enabled"": true, - ""requested"": true, - ""allowed"": false, - ""id"": ""SE00000000000000000002"", - ""verificationStatus"": ""pending"" - } - ], - ""verificationStatus"": ""pending"" - } - }, - ""id"": ""AH00000000000000000001"", - ""status"": ""active"" - } - }, - ""environment"": ""live"", - ""timestamp"": ""2024-12-15T15:42:03+01:00"", - ""type"": ""balancePlatform.accountHolder.updated"" -}"; - - _balancePlatformWebhookHandler.GetAccountHolderNotificationRequest(jsonPayload, out AccountHolderNotificationRequest accountHolderUpdatedWebhook); - - Assert.IsNotNull(accountHolderUpdatedWebhook); - Assert.AreEqual(accountHolderUpdatedWebhook.Type, AccountHolderNotificationRequest.TypeEnum.Updated); - Assert.AreEqual("YOUR_BALANCE_PLATFORM", accountHolderUpdatedWebhook.Data.BalancePlatform); - Assert.AreEqual("AH00000000000000000001", accountHolderUpdatedWebhook.Data.AccountHolder.Id); - Assert.AreEqual("LE00000000000000000001", accountHolderUpdatedWebhook.Data.AccountHolder.LegalEntityId); - Assert.AreEqual("YOUR_REFERENCE-2412C", accountHolderUpdatedWebhook.Data.AccountHolder.Reference); - Assert.AreEqual(AccountHolder.StatusEnum.Active, accountHolderUpdatedWebhook.Data.AccountHolder.Status); - Assert.IsTrue(accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities.TryGetValue("sendToTransferInstrument", out AccountHolderCapability capabilitiesData)); - Assert.AreEqual(true, capabilitiesData.Enabled); - Assert.AreEqual(true, capabilitiesData.Requested); - Assert.AreEqual(false, capabilitiesData.Allowed); - Assert.AreEqual(AccountHolderCapability.VerificationStatusEnum.Pending, capabilitiesData.VerificationStatus); - Assert.AreEqual(4, accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems.Count); - Assert.AreEqual("LE00000000000000000001", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[0].Entity.Id); - Assert.AreEqual(CapabilityProblemEntity.TypeEnum.LegalEntity, accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[0].Entity.Type); - Assert.AreEqual("2_902", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[0].VerificationErrors[0].Code); - Assert.AreEqual("Terms Of Service forms are not accepted.", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[0].VerificationErrors[0].Message); - Assert.AreEqual("Accept TOS", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[0].VerificationErrors[0].RemediatingActions[0].Message); - Assert.AreEqual("SE00000000000000000001", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[1].Entity.Id); - Assert.AreEqual(CapabilityProblemEntity.TypeEnum.BankAccount, accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[1].Entity.Type); - Assert.AreEqual("2_8037", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[1].VerificationErrors[0].Code); - Assert.AreEqual("'bankStatement' was missing.", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[1].VerificationErrors[0].Message); - Assert.AreEqual("Upload a bank statement", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[1].VerificationErrors[0].RemediatingActions[0].Message); - Assert.AreEqual("SE00000000000000000002", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[2].Entity.Id); - Assert.AreEqual(CapabilityProblemEntity.TypeEnum.BankAccount, accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[2].Entity.Type); - Assert.AreEqual("2_8037", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[2].VerificationErrors[0].Code); - Assert.AreEqual("'bankStatement' was missing.", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[2].VerificationErrors[0].Message); - Assert.AreEqual("Upload a bank statement", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[2].VerificationErrors[0].RemediatingActions[0].Message); - Assert.AreEqual("LE00000000000000000001", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[3].Entity.Id); - Assert.AreEqual(CapabilityProblemEntity.TypeEnum.LegalEntity, accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[3].Entity.Type); - Assert.AreEqual("2_8189", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[3].VerificationErrors[0].Code); - Assert.AreEqual("'UBO through control' was missing.", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[3].VerificationErrors[0].Message); - Assert.AreEqual("Add 'organization.entityAssociations' of type 'uboThroughControl' to legal entity", accountHolderUpdatedWebhook.Data.AccountHolder.Capabilities["sendToTransferInstrument"].Problems[3].VerificationErrors[0].RemediatingActions[0].Message); - Assert.AreEqual("live", accountHolderUpdatedWebhook.Environment); - Assert.AreEqual(DateTime.Parse("2024-12-15T15:42:03+01:00"), accountHolderUpdatedWebhook.Timestamp); - } - - [TestMethod] - public void Test_POC_Integration() - { - string jsonPayload = @" -{ - 'data': { - 'balancePlatform': 'YOUR_BALANCE_PLATFORM', - 'accountHolder': { - 'contactDetails': { - 'address': { - 'country': 'NL', - 'houseNumberOrName': '274', - 'postalCode': '1020CD', - 'street': 'Brannan Street' - }, - 'email': 's.hopper@example.com', - 'phone': { - 'number': '+315551231234', - 'type': 'Mobile' - } - }, - 'description': 'S.Hopper - Staff 123', - 'id': 'AH00000000000000000000001', - 'status': 'Active' - } - }, - 'environment': 'test', - 'type': 'balancePlatform.accountHolder.created' -}"; - - var handler = new BalancePlatformWebhookHandler(); - var response = handler.GetGenericBalancePlatformWebhook(jsonPayload); - try - { - if (response.GetType() == typeof(AccountHolderNotificationRequest)) - { - var accountHolderNotificationRequest = (AccountHolderNotificationRequest)response; - Assert.AreEqual(accountHolderNotificationRequest.Environment, "test"); - } - - if (response.GetType() == typeof(BalanceAccountNotificationRequest)) - { - var balanceAccountNotificationRequest = (BalanceAccountNotificationRequest)response; - Assert.Fail(balanceAccountNotificationRequest.Data.BalancePlatform); - } - } - catch (System.Exception e) - { - Assert.Fail(); - throw new System.Exception(e.ToString()); - } - } - - [TestMethod] - public void Test_TransactionWebhook_V4() - { - // Arrange - const string jsonPayload = @" -{ - 'data': { - 'id': 'EVJN42272224222B5JB8BRC84N686ZEUR', - 'amount': { - 'value': 7000, - 'currency': 'EUR' - }, - 'status': 'booked', - 'transfer': { - 'id': 'JN4227222422265', - 'reference': 'Split_item_1', - }, - 'valueDate': '2023-03-01T00:00:00+02:00', - 'bookingDate': '2023-02-28T13:30:20+02:00', - 'creationDate': '2023-02-28T13:30:05+02:00', - 'accountHolder': { - 'id': 'AH00000000000000000000001', - 'description': 'Your description for the account holder', - 'reference': 'Your reference for the account holder' - }, - 'balanceAccount': { - 'id': 'BA00000000000000000000001', - 'description': 'Your description for the balance account', - 'reference': 'Your reference for the balance account' - }, - 'balancePlatform': 'YOUR_BALANCE_PLATFORM' - }, - 'type': 'balancePlatform.transaction.created', - 'environment': 'test' -}"; - Assert.IsFalse(_balancePlatformWebhookHandler.GetPaymentNotificationRequest(jsonPayload, out var _)); - - // Act - _balancePlatformWebhookHandler.GetTransactionNotificationRequestV4(jsonPayload, out TransactionNotificationRequestV4 target); - - // Assert - Assert.IsNotNull(target); - Assert.AreEqual(TransactionNotificationRequestV4.TypeEnum.BalancePlatformTransactionCreated, target.Type); - Assert.AreEqual(Transaction.StatusEnum.Booked, target.Data.Status); - Assert.AreEqual("BA00000000000000000000001", target.Data.BalanceAccount.Id); - Assert.IsTrue(target.Data.ValueDate.Equals(DateTime.Parse("2023-03-01T00:00:00+02:00"))); - Assert.AreEqual("YOUR_BALANCE_PLATFORM", target.Data.BalancePlatform); - Assert.AreEqual("AH00000000000000000000001", target.Data.AccountHolder.Id); - Assert.AreEqual("Your description for the account holder", target.Data.AccountHolder.Description); - Assert.AreEqual("Your reference for the account holder", target.Data.AccountHolder.Reference); - Assert.AreEqual("JN4227222422265", target.Data.Transfer.Id); - Assert.AreEqual("Split_item_1", target.Data.Transfer.Reference); - Assert.AreEqual(DateTime.Parse("2023-02-28T13:30:05+02:00"), target.Data.CreationDate); - Assert.AreEqual(DateTime.Parse("2023-02-28T13:30:20+02:00"), target.Data.BookingDate); - Assert.AreEqual(7000, target.Data.Amount.Value); - Assert.AreEqual("EUR", target.Data.Amount.Currency); - Assert.AreEqual("test", target.Environment); - } - - [TestMethod] - public void Test_ReportCreatedWebhook() - { - // Arrange - const string jsonPayload = @" -{ - 'data': { - 'balancePlatform': 'YOUR_BALANCE_PLATFORM', - 'creationDate': '2024-07-02T02:01:08+02:00', - 'id': 'balanceplatform_accounting_report_2024_07_01.csv', - 'downloadUrl': 'https://balanceplatform-test.adyen.com/balanceplatform/.../.../.../balanceplatform_accounting_report_2024_07_01.csv', - 'fileName': 'balanceplatform_accounting_report_2024_07_01.csv', - 'reportType': 'balanceplatform_accounting_report' - }, - 'environment': 'test', - 'timestamp': '2024-07-02T02:01:05+02:00', - 'type': 'balancePlatform.report.created' -}"; - - // Act - _balancePlatformWebhookHandler.GetReportNotificationRequest(jsonPayload, out ReportNotificationRequest target); - - // Assert - Assert.IsNotNull(target); - Assert.AreEqual(target.Type, ReportNotificationRequest.TypeEnum.BalancePlatformReportCreated); - Assert.AreEqual("YOUR_BALANCE_PLATFORM", target.Data.BalancePlatform); - Assert.AreEqual("balanceplatform_accounting_report_2024_07_01.csv", target.Data.Id); - Assert.AreEqual("balanceplatform_accounting_report_2024_07_01.csv", target.Data.FileName); - Assert.AreEqual("balanceplatform_accounting_report", target.Data.ReportType); - Assert.AreEqual(DateTime.Parse("2024-07-02T02:01:08+02:00"), target.Data.CreationDate); - Assert.AreEqual("https://balanceplatform-test.adyen.com/balanceplatform/.../.../.../balanceplatform_accounting_report_2024_07_01.csv", target.Data.DownloadUrl); - Assert.AreEqual("test", target.Environment); - Assert.AreEqual(DateTime.Parse("2024-07-02T02:01:05+02:00"), target.Timestamp); - } - - [TestMethod] - public void Test_AuthenticationCreatedWebhook() - { - // Arrange - const string jsonPayload = @" -{ - 'data': { - 'authentication': { - 'acsTransId': '6a4c1709-a42e-4c7f-96c7-1043adacfc97', - 'challenge': { - 'flow': 'OOB_TRIGGER_FL', - 'lastInteraction': '2022-12-22T15:49:03+01:00' - }, - 'challengeIndicator': '01', - 'createdAt': '2022-12-22T15:45:03+01:00', - 'deviceChannel': 'app', - 'dsTransID': 'a3b86754-444d-46ca-95a2-ada351d3f42c', - 'exemptionIndicator': 'lowValue', - 'inPSD2Scope': true, - 'messageCategory': 'payment', - 'messageVersion': '2.2.0', - 'riskScore': 0, - 'threeDSServerTransID': '6edcc246-23ee-4e94-ac5d-8ae620bea7d9', - 'transStatus': 'Y', - 'type': 'challenge' - }, - 'balancePlatform': 'YOUR_BALANCE_PLATFORM', - 'id': '497f6eca-6276-4993-bfeb-53cbbbba6f08', - 'paymentInstrumentId': 'PI3227C223222B5BPCMFXD2XG', - 'purchase': { - 'date': '2022-12-22T15:49:03+01:00', - 'merchantName': 'MyShop', - 'originalAmount': { - 'currency': 'EUR', - 'value': 1000 - } - }, - 'status': 'authenticated' - }, - 'environment': 'test', - 'timestamp': '2022-12-22T15:42:03+01:00', - 'type': 'balancePlatform.authentication.created' -}"; - - // Act - _balancePlatformWebhookHandler.GetAuthenticationNotificationRequest(jsonPayload, out AuthenticationNotificationRequest target); - - // Assert - Assert.IsNotNull(target); - Assert.AreEqual(target.Type, AuthenticationNotificationRequest.TypeEnum.BalancePlatformAuthenticationCreated); - Assert.AreEqual("YOUR_BALANCE_PLATFORM", target.Data.BalancePlatform); - Assert.AreEqual("497f6eca-6276-4993-bfeb-53cbbbba6f08", target.Data.Id); - Assert.AreEqual("PI3227C223222B5BPCMFXD2XG", target.Data.PaymentInstrumentId); - Assert.AreEqual(AuthenticationNotificationData.StatusEnum.Authenticated, target.Data.Status); - Assert.AreEqual("MyShop", target.Data.Purchase.MerchantName); - Assert.AreEqual("2022-12-22T15:49:03+01:00", target.Data.Purchase.Date); - Assert.AreEqual("EUR", target.Data.Purchase.OriginalAmount.Currency); - Assert.AreEqual(1000, target.Data.Purchase.OriginalAmount.Value); - Assert.AreEqual(DateTime.Parse("2022-12-22T15:45:03+01:00"), target.Data.Authentication.CreatedAt); - Assert.AreEqual(AuthenticationInfo.DeviceChannelEnum.App, target.Data.Authentication.DeviceChannel); - Assert.AreEqual(ChallengeInfo.FlowEnum.OOBTRIGGERFL, target.Data.Authentication.Challenge.Flow); - Assert.AreEqual(DateTime.Parse("2022-12-22T15:49:03+01:00"), target.Data.Authentication.Challenge.LastInteraction); - Assert.AreEqual(AuthenticationInfo.ChallengeIndicatorEnum._01, target.Data.Authentication.ChallengeIndicator); - Assert.AreEqual(AuthenticationInfo.ExemptionIndicatorEnum.LowValue, target.Data.Authentication.ExemptionIndicator); - Assert.AreEqual(true, target.Data.Authentication.InPSD2Scope); - Assert.AreEqual(AuthenticationInfo.MessageCategoryEnum.Payment, target.Data.Authentication.MessageCategory); - Assert.AreEqual("2.2.0", target.Data.Authentication.MessageVersion); - Assert.AreEqual(0, target.Data.Authentication.RiskScore); - Assert.AreEqual("6edcc246-23ee-4e94-ac5d-8ae620bea7d9", target.Data.Authentication.ThreeDSServerTransID); - Assert.AreEqual(AuthenticationInfo.TransStatusEnum.Y, target.Data.Authentication.TransStatus); - Assert.AreEqual("test", target.Environment); - Assert.AreEqual(DateTime.Parse("2022-12-22T15:42:03+01:00"), target.Timestamp); - } - - [TestMethod] - public void Test_NegativeBalanceCompensationWarningWebhook() - { - // Arrange - const string jsonPayload = @" -{ - 'data': { - 'balancePlatform': 'YOUR_BALANCE_PLATFORM', - 'creationDate': '2024-07-02T02:01:08+02:00', - 'id': 'BA00000000000000000001', - 'accountHolder': { - 'description': 'Description for the account holder.', - 'reference': 'YOUR_REFERENCE', - 'id': 'AH00000000000000000001' - }, - 'amount': { - 'currency': 'EUR', - 'value': -145050 - }, - 'liableBalanceAccountId': 'BA11111111111111111111', - 'negativeBalanceSince': '2024-10-19T00:33:13+02:00', - 'scheduledCompensationAt': '2024-12-01T01:00:00+01:00' - }, - 'environment': 'test', - 'timestamp': '2024-10-22T00:00:00+02:00', - 'type': 'balancePlatform.negativeBalanceCompensationWarning.scheduled' -}"; - - // Act - _balancePlatformWebhookHandler.GetNegativeBalanceCompensationWarningNotificationRequest(jsonPayload, out NegativeBalanceCompensationWarningNotificationRequest target); - - // Assert - Assert.IsNotNull(target); - Assert.AreEqual(NegativeBalanceCompensationWarningNotificationRequest.TypeEnum.BalancePlatformNegativeBalanceCompensationWarningScheduled, target.Type); - Assert.AreEqual("YOUR_BALANCE_PLATFORM", target.Data.BalancePlatform); - Assert.AreEqual(DateTime.Parse("2024-07-02T02:01:08+02:00"), target.Data.CreationDate); - Assert.AreEqual("BA00000000000000000001", target.Data.Id); - Assert.AreEqual("YOUR_REFERENCE", target.Data.AccountHolder.Reference); - Assert.AreEqual("EUR", target.Data.Amount.Currency); - Assert.AreEqual(-145050, target.Data.Amount.Value); - Assert.AreEqual("BA11111111111111111111", target.Data.LiableBalanceAccountId); - Assert.AreEqual(DateTime.Parse("2024-10-19T00:33:13+02:00"), target.Data.NegativeBalanceSince); - Assert.AreEqual(DateTime.Parse("2024-12-01T01:00:00+01:00"), target.Data.ScheduledCompensationAt); - Assert.AreEqual("test", target.Environment); - Assert.AreEqual(DateTime.Parse("2024-10-22T00:00:00+02:00"), target.Timestamp); - } - } -} \ No newline at end of file diff --git a/Adyen.Test/WebhooksTests/ManagementWebhookHandlerTest.cs b/Adyen.Test/WebhooksTests/ManagementWebhookHandlerTest.cs deleted file mode 100644 index 2e7d4d78b..000000000 --- a/Adyen.Test/WebhooksTests/ManagementWebhookHandlerTest.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System; -using Adyen.Model.ManagementWebhooks; -using Adyen.Webhooks; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Adyen.Test.WebhooksTests -{ - [TestClass] - public class ManagementWebhookHandlerTest : BaseTest - { - private readonly ManagementWebhookHandler _managementWebhookHandler = new ManagementWebhookHandler(); - - [TestMethod] - public void Test_Management_Webhook_PaymentMethodCreated() - { - // Arrange - const string jsonPayload = @" -{ - 'createdAt': '2022-01-24T14:59:11+01:00', - 'data': { - 'id': 'PM3224R223224K5FH4M2K9B86', - 'merchantId': 'MERCHANT_ACCOUNT', - 'status': 'SUCCESS', - 'storeId': 'ST322LJ223223K5F4SQNR9XL5', - 'type': 'visa' - }, - 'environment': 'test', - 'type': 'paymentMethod.created' -}"; - // Act - _managementWebhookHandler.GetPaymentMethodCreatedNotificationRequest(jsonPayload, out PaymentMethodCreatedNotificationRequest paymentMethodCreatedWebhook); - - // Assert - Assert.IsNotNull(paymentMethodCreatedWebhook); - Assert.AreEqual(PaymentMethodCreatedNotificationRequest.TypeEnum.PaymentMethodCreated, paymentMethodCreatedWebhook.Type); - Assert.AreEqual(paymentMethodCreatedWebhook.Data.Id, "PM3224R223224K5FH4M2K9B86"); - Assert.AreEqual(paymentMethodCreatedWebhook.Data.MerchantId, "MERCHANT_ACCOUNT"); - Assert.AreEqual(paymentMethodCreatedWebhook.Data.Status, MidServiceNotificationData.StatusEnum.Success); - Assert.AreEqual(paymentMethodCreatedWebhook.Data.StoreId, "ST322LJ223223K5F4SQNR9XL5"); - Assert.AreEqual(paymentMethodCreatedWebhook.Data.Type, "visa"); - Assert.AreEqual(DateTime.Parse("2022-01-24T14:59:11+01:00"), paymentMethodCreatedWebhook.CreatedAt); - Assert.AreEqual("test", paymentMethodCreatedWebhook.Environment); - } - - [TestMethod] - public void Test_Management_Webhook_MerchantUpdated() - { - // Arrange - const string jsonPayload = @" -{ - 'type': 'merchant.updated', - 'environment': 'test', - 'createdAt': '2022-09-20T13:42:31+02:00', - 'data': { - 'capabilities': { - 'receivePayments': { - 'allowed': true, - 'requested': true, - 'requestedLevel': 'notApplicable', - 'verificationStatus': 'valid' - } - }, - 'legalEntityId': 'LE322KH223222F5GNNW694PZN', - 'merchantId': 'YOUR_MERCHANT_ID', - 'status': 'PreActive' - } -}"; - // Act - _managementWebhookHandler.GetMerchantUpdatedNotificationRequest(jsonPayload, out MerchantUpdatedNotificationRequest target); - - // Assert - Assert.IsNotNull(target); - Assert.AreEqual(target.Type, MerchantUpdatedNotificationRequest.TypeEnum.MerchantUpdated); - Assert.AreEqual("LE322KH223222F5GNNW694PZN", target.Data.LegalEntityId); - Assert.AreEqual("YOUR_MERCHANT_ID", target.Data.MerchantId); - Assert.AreEqual("PreActive", target.Data.Status); - Assert.AreEqual(DateTime.Parse("2022-09-20T13:42:31+02:00"), target.CreatedAt); - Assert.AreEqual("test", target.Environment); - - Assert.IsTrue(target.Data.Capabilities.TryGetValue("receivePayments", out AccountCapabilityData accountCapabilityData)); - Assert.AreEqual(true, accountCapabilityData.Requested); - Assert.AreEqual("notApplicable", accountCapabilityData.RequestedLevel); - } - - [TestMethod] - public void Test_Management_Webhook_MerchantCreated() - { - // Arrange - const string jsonPayload = @" -{ - 'type': 'merchant.created', - 'environment': 'test', - 'createdAt': '2022-08-12T10:50:01+02:00', - 'data': { - 'capabilities': { - 'sendToTransferInstrument': { - 'requested': true, - 'requestedLevel': 'notApplicable' - } - }, - 'companyId': 'YOUR_COMPANY_ID', - 'merchantId': 'MC3224X22322535GH8D537TJR', - 'status': 'PreActive' - } -}"; - Assert.IsFalse(_managementWebhookHandler.GetMerchantUpdatedNotificationRequest(jsonPayload, out _)); - - // Act - _managementWebhookHandler.GetMerchantCreatedNotificationRequest(jsonPayload, out MerchantCreatedNotificationRequest target); - - // Assert - Assert.IsNotNull(target); - Assert.AreEqual(MerchantCreatedNotificationRequest.TypeEnum.MerchantCreated, target.Type); - Assert.AreEqual("test", target.Environment); - Assert.AreEqual("YOUR_COMPANY_ID", target.Data.CompanyId); - Assert.AreEqual("MC3224X22322535GH8D537TJR", target.Data.MerchantId); - Assert.AreEqual("PreActive", target.Data.Status); - Assert.AreEqual(DateTime.Parse("2022-08-12T10:50:01+02:00"), target.CreatedAt); - Assert.IsTrue(target.Data.Capabilities.TryGetValue("sendToTransferInstrument", out AccountCapabilityData data)); - Assert.AreEqual("notApplicable", data.RequestedLevel); - Assert.AreEqual(true, data.Requested); - } - } -} \ No newline at end of file diff --git a/Adyen.Test/WebhooksTests/WebhookHandlerTest.cs b/Adyen.Test/WebhooksTests/WebhookHandlerTest.cs deleted file mode 100644 index 0b60812e3..000000000 --- a/Adyen.Test/WebhooksTests/WebhookHandlerTest.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System; -using System.Linq; -using Adyen.ApiSerialization; -using Adyen.Model.TerminalApi; -using Adyen.Webhooks; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace Adyen.Test.WebhooksTests -{ - [TestClass] - public class WebhookHandlerTest : BaseTest - { - [TestMethod] - public void TestAuthorisationSuccess() - { - var mockPath = GetMockFilePath("mocks/notification/authorisation-true.json"); - var jsonRequest = MockFileToString(mockPath); - var webhookHandler = new WebhookHandler(); - var handleNotificationRequest = webhookHandler.HandleNotificationRequest(jsonRequest); - var notificationRequestItemContainer = handleNotificationRequest.NotificationItemContainers.FirstOrDefault(); - if (notificationRequestItemContainer == null) - Assert.Fail("NotificationRequestItemContainer is null"); - var notificationItem = notificationRequestItemContainer.NotificationItem; - Assert.AreEqual("AUTHORISATION", notificationItem.EventCode); - Assert.AreEqual("1234", notificationItem.AdditionalData["authCode"]); - Assert.AreEqual("123456789", notificationItem.PspReference); - } - - [TestMethod] - public void TestMerchantWebhookPayload() - { - var mockPath = GetMockFilePath("mocks/notification/merchant-webhook-payload.json"); - var jsonRequest = MockFileToString(mockPath); - var webhookHandler = new WebhookHandler(); - var handleNotificationRequest = webhookHandler.HandleNotificationRequest(jsonRequest); - var json1 = JsonConvert.SerializeObject(handleNotificationRequest); - var json2 = jsonRequest; - Assert.IsTrue(JToken.DeepEquals(JToken.Parse(json1.ToLower()), JToken.Parse(json2.ToLower()))); - } - - [TestMethod] - public void TestCaptureSuccess() - { - var mockPath = GetMockFilePath("mocks/notification/capture-true.json"); - var jsonRequest = MockFileToString(mockPath); - var webhookHandler = new WebhookHandler(); - var handleNotificationRequest = webhookHandler.HandleNotificationRequest(jsonRequest); - var notificationRequestItemContainer = handleNotificationRequest.NotificationItemContainers.FirstOrDefault(); - if (notificationRequestItemContainer == null) - Assert.Fail("NotificationRequestItemContainer is null"); - var notificationItem = notificationRequestItemContainer.NotificationItem; - Assert.AreEqual("CAPTURE", notificationItem.EventCode); - Assert.AreEqual("qvS6I3Gdi1jx+jSh7IopAgcHtMoxvXlNL7DYQ+j1hd0=", notificationItem.AdditionalData["hmacSignature"]); - Assert.AreEqual("PSP_REFERENCE", notificationItem.PspReference); - Assert.AreEqual(23623, notificationItem.Amount.Value); - Assert.IsTrue(notificationItem.Success); - } - - [TestMethod] - public void TestCaptureFail() - { - var mockPath = GetMockFilePath("mocks/notification/capture-false.json"); - var jsonRequest = MockFileToString(mockPath); - var webhookHandler = new WebhookHandler(); - var handleNotificationRequest = webhookHandler.HandleNotificationRequest(jsonRequest); - var notificationRequestItemContainer = handleNotificationRequest.NotificationItemContainers.FirstOrDefault(); - if (notificationRequestItemContainer == null) - Assert.Fail("NotificationRequestItemContainer is null"); - var notificationItem = notificationRequestItemContainer.NotificationItem; - Assert.AreEqual("CAPTURE", notificationItem.EventCode); - Assert.AreEqual("KujHNqpyCAMdGefj7lfQ8AeD0Jke9Zs2bVAqScQDWi4=", notificationItem.AdditionalData["hmacSignature"]); - Assert.AreEqual("PSP_REFERENCE", notificationItem.PspReference); - Assert.AreEqual(23623, notificationItem.Amount.Value); - Assert.IsFalse(notificationItem.Success); - Assert.AreEqual("Insufficient balance on payment", notificationItem.Reason); - } - - [TestMethod] - public void TestRefundSuccess() - { - var mockPath = GetMockFilePath("mocks/notification/refund-true.json"); - var jsonRequest = MockFileToString(mockPath); - var webhookHandler = new WebhookHandler(); - var handleNotificationRequest = webhookHandler.HandleNotificationRequest(jsonRequest); - var notificationRequestItemContainer = handleNotificationRequest.NotificationItemContainers.FirstOrDefault(); - if (notificationRequestItemContainer == null) - Assert.Fail("NotificationRequestItemContainer is null"); - var notificationItem = notificationRequestItemContainer.NotificationItem; - Assert.AreEqual("REFUND", notificationItem.EventCode); - Assert.AreEqual("KJFhURWP8Pv9m8k+7NGHNJAupBj6X6J/VWAikFxeWhA=", notificationItem.AdditionalData["hmacSignature"]); - Assert.AreEqual("PSP_REFERENCE", notificationItem.PspReference); - Assert.AreEqual(1500, notificationItem.Amount.Value); - Assert.AreEqual("MagentoMerchantTest2", notificationItem.MerchantAccountCode); - Assert.IsTrue(notificationItem.Success); - } - - [TestMethod] - public void TestRefundFail() - { - var mockPath = GetMockFilePath("mocks/notification/refund-false.json"); - var jsonRequest = MockFileToString(mockPath); - var webhookHandler = new WebhookHandler(); - var handleNotificationRequest = webhookHandler.HandleNotificationRequest(jsonRequest); - var notificationRequestItemContainer = handleNotificationRequest.NotificationItemContainers.FirstOrDefault(); - if (notificationRequestItemContainer == null) - Assert.Fail("NotificationRequestItemContainer is null"); - var notificationItem = notificationRequestItemContainer.NotificationItem; - Assert.AreEqual("REFUND", notificationItem.EventCode); - Assert.AreEqual("HZXziBYopfDIzDhk49iC//yCfxmy/z0xWuvvTxFNUSA=", notificationItem.AdditionalData["hmacSignature"]); - Assert.AreEqual("PSP_REFERENCE", notificationItem.PspReference); - Assert.AreEqual(1500, notificationItem.Amount.Value); - Assert.IsFalse(notificationItem.Success); - Assert.AreEqual("Insufficient balance on payment", notificationItem.Reason); - } - - [TestMethod] - public void TestPOSDisplayNotification() - { - var notification = - "{\"SaleToPOIRequest\":{\"DisplayRequest\":{\"DisplayOutput\":[{\"Device\": \"CashierDisplay\",\"InfoQualify\": \"Status\",\"OutputContent\": {\"OutputFormat\": \"MessageRef\",\"PredefinedContent\": {\"ReferenceID\": \"TransactionID=oLkO001517998574000&TimeStamp=2018-02-07T10%3a16%3a14.000Z&event=TENDER_CREATED\"}},\"ResponseRequiredFlag\": false}]},\"MessageHeader\":{\"SaleID\":\"POSSystemID12345\",\"ProtocolVersion\":\"3.0\",\"MessageType\":\"Request\",\"POIID\":\"V400m-324688179\",\"ServiceID\":\"0207111617\",\"MessageClass\":\"Device\",\"MessageCategory\":\"Display\",\"DeviceID\":\"1517998562\"}}}"; - var serializer = new SaleToPoiMessageSerializer(); - var saleToPoiRequest = serializer.DeserializeNotification(notification); - var displayRequest = (DisplayRequest)saleToPoiRequest.MessagePayload; - Assert.AreEqual(displayRequest.DisplayOutput[0].OutputContent.OutputFormat, OutputFormatType.MessageRef); - Assert.AreEqual(displayRequest.DisplayOutput[0].Device, DeviceType.CashierDisplay); - } - - [TestMethod] - public void TestPOSEventNotification() - { - var notification = @"{ - 'SaleToPOIRequest':{ - 'EventNotification':{ - 'EventDetails':'newstate=IDLE&oldstate=START', - 'EventToNotify':'Shutdown', - 'TimeStamp':'2019-08-07T10:16:10.000Z' - }, - 'MessageHeader':{ - 'SaleID':'POSSystemID12345', - 'ProtocolVersion':'3.0', - 'MessageType':'Notification', - 'POIID':'V400m-324688179', - 'MessageClass':'Event', - 'MessageCategory':'Event', - 'DeviceID':'1517998561' - } - } - }"; - var serializer = new SaleToPoiMessageSerializer(); - var saleToPoiRequest = serializer.DeserializeNotification(notification); - var eventNotification = (EventNotification) saleToPoiRequest.MessagePayload; - Assert.AreEqual(eventNotification.EventDetails, "newstate=IDLE&oldstate=START"); - Assert.AreEqual(eventNotification.TimeStamp, new DateTime(2019, 8, 7, 10, 16, 10)); - } - - [TestMethod] - public void TestClassicPlatformWebhookParser() - { - var mockPath = GetMockFilePath("mocks/notification/classic-platform-webhook.json"); - var payload = MockFileToString(mockPath); - - var parser = new ClassicPlatformWebhookHandler(); - Assert.IsTrue(parser.GetTransferFundsNotification(payload, out var webhook)); - Assert.IsFalse(parser.GetAccountCloseNotification(payload, out _)); - - JObject expected = (JObject)JsonConvert.DeserializeObject(payload); - JObject result = (JObject)JsonConvert.DeserializeObject(webhook.ToJson()); - Assert.IsTrue(JToken.DeepEquals(expected, result)); - } - } -} diff --git a/Adyen.Test/mockHttpClient.cs b/Adyen.Test/mockHttpClient.cs deleted file mode 100644 index 978a21c89..000000000 --- a/Adyen.Test/mockHttpClient.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Net; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - -public class MockHttpMessageHandler : HttpMessageHandler -{ - private readonly string _response; - private readonly HttpStatusCode _statusCode; - - public string Input { get; private set; } - public int NumberOfCalls { get; private set; } - - public MockHttpMessageHandler(string response, HttpStatusCode statusCode) - { - _response = response; - _statusCode = statusCode; - } - - protected override async Task SendAsync(HttpRequestMessage request, - CancellationToken cancellationToken) - { - NumberOfCalls++; - if (request.Content != null) // Could be a GET-request without a body - { - Input = await request.Content.ReadAsStringAsync(); - } - return new HttpResponseMessage - { - StatusCode = _statusCode, - Content = new StringContent(_response) - }; - } -} \ No newline at end of file diff --git a/Adyen.Test/mocks/balanceplatform/AccountHolder.json b/Adyen.Test/mocks/balanceplatform/AccountHolder.json index 8c75be5a9..4f8c64960 100644 --- a/Adyen.Test/mocks/balanceplatform/AccountHolder.json +++ b/Adyen.Test/mocks/balanceplatform/AccountHolder.json @@ -11,9 +11,10 @@ "email": "s.hopper@example.com", "phone": { "number": "+315551231234", - "type": "Mobile" + "type": "mobile" } }, + "legalEntityId": "LE322JV223222D5GG42KN6869", "description": "S.Hopper - Staff 123", "id": "AH32272223222B5CM4MWJ892H", "status": "active" diff --git a/Adyen.Test/mocks/balanceplatform/AccountHolderWithUnknownEnum.json b/Adyen.Test/mocks/balanceplatform/AccountHolderWithUnknownEnum.json new file mode 100644 index 000000000..a16376ddc --- /dev/null +++ b/Adyen.Test/mocks/balanceplatform/AccountHolderWithUnknownEnum.json @@ -0,0 +1,21 @@ +{ + "balancePlatform": "BalandePlatformExample", + "contactDetails": { + "address": { + "city": "Amsterdam", + "country": "NL", + "houseNumberOrName": "274", + "postalCode": "1020CD", + "street": "Brannan Street" + }, + "email": "s.hopper@example.com", + "phone": { + "number": "+315551231234", + "type": "mobile" + } + }, + "legalEntityId": "LE322JV223222D5GG42KN6869", + "description": "S.Hopper - Staff 123", + "id": "AH32272223222B5CM4MWJ892H", + "status": "unknown-enum" +} \ No newline at end of file diff --git a/Adyen.Test/mocks/balanceplatform/BalanceAccount.json b/Adyen.Test/mocks/balanceplatform/BalanceAccount.json index 5e7d9ba9e..f36f0a1bb 100644 --- a/Adyen.Test/mocks/balanceplatform/BalanceAccount.json +++ b/Adyen.Test/mocks/balanceplatform/BalanceAccount.json @@ -1,6 +1,8 @@ { - "accountHolderId": "AH32272223222B59K6RTQBFNZ", + "accountHolderId": "AH32272223222C5GXTD343TKP", "defaultCurrencyCode": "EUR", + "description": "S.Hopper - Main balance account", + "timeZone": "Europe/Amsterdam", "balances": [ { "available": 0, @@ -9,6 +11,6 @@ "reserved": 0 } ], - "id": "BA3227C223222B5BLP6JQC3FD", - "status": "Active" + "id": "BA3227C223222H5J4DCGQ9V9L", + "status": "active" } \ No newline at end of file diff --git a/Adyen.Test/mocks/balanceplatform/PaginatedAccountHoldersResponse.json b/Adyen.Test/mocks/balanceplatform/PaginatedAccountHoldersResponse.json index 4b65c66f5..be1201a7c 100644 --- a/Adyen.Test/mocks/balanceplatform/PaginatedAccountHoldersResponse.json +++ b/Adyen.Test/mocks/balanceplatform/PaginatedAccountHoldersResponse.json @@ -1,34 +1,27 @@ { "accountHolders": [ { - "contactDetails": { - "address": { - "city": "Amsterdam", - "country": "NL", - "houseNumberOrName": "6", - "postalCode": "12336750", - "street": "Simon Carmiggeltstraat" - } - }, - "description": "J. Doe", - "id": "AH32272223222B59DDWSCCMP7", - "status": "Active" + "description": "Test-305", + "legalEntityId": "LE3227C223222D5D8S5S33M4M", + "reference": "LegalEntity internal error test", + "id": "AH32272223222B5GFSNSXFFL9", + "status": "active" }, { - "contactDetails": { - "address": { - "city": "Amsterdam", - "country": "NL", - "houseNumberOrName": "11", - "postalCode": "12336750", - "street": "Simon Carmiggeltstraat" - } - }, - "description": "S. Hopper", - "id": "AH32272223222B59DJ7QBCMPN", - "status": "Active" + "description": "Test-751", + "legalEntityId": "LE3227C223222D5D8S5TT3SRX", + "reference": "LegalEntity internal error test", + "id": "AH32272223222B5GFSNVGFFM7", + "status": "active" + }, + { + "description": "Explorer Holder", + "legalEntityId": "LE3227C223222D5D8S5TT3SRX", + "reference": "Account from the Explorer Holder", + "id": "AH32272223222B5GFWNRFFVR6", + "status": "active" } ], - "hasNext": "true", - "hasPrevious": "false" + "hasNext": true, + "hasPrevious": true } \ No newline at end of file diff --git a/Adyen.Test/mocks/balanceplatform/PaginatedBalanceAccountsResponse.json b/Adyen.Test/mocks/balanceplatform/PaginatedBalanceAccountsResponse.json index 58706682f..fb1bc8842 100644 --- a/Adyen.Test/mocks/balanceplatform/PaginatedBalanceAccountsResponse.json +++ b/Adyen.Test/mocks/balanceplatform/PaginatedBalanceAccountsResponse.json @@ -1,22 +1,31 @@ { "balanceAccounts": [ { - "accountHolderId": "AH32272223222B59K6ZKBBFNQ", + "accountHolderId": "AH32272223222B5CTBMZT6W2V", "defaultCurrencyCode": "EUR", - "id": "BA32272223222B59K6ZXHBFN6", - "status": "Active" + "description": "S. Hopper - Main Account", + "reference": "YOUR_REFERENCE-X173L", + "timeZone": "Europe/Amsterdam", + "id": "BA32272223222B5CTDNB66W2Z", + "status": "active" }, { - "accountHolderId": "AH32272223222B59K6ZKBBFNQ", + "accountHolderId": "AH32272223222B5CTBMZT6W2V", "defaultCurrencyCode": "EUR", - "id": "BA32272223222B59K72CKBFNJ", - "status": "closed" + "description": "S. Hopper - Main Account", + "reference": "YOUR_REFERENCE-X173L", + "timeZone": "Europe/Amsterdam", + "id": "BA32272223222B5CTDQPM6W2H", + "status": "active" }, { - "accountHolderId": "AH32272223222B59K6ZKBBFNQ", + "accountHolderId": "AH32272223222B5CTBMZT6W2V", "defaultCurrencyCode": "EUR", - "id": "BA32272223222B5BRR27B2M7G", - "status": "Active" + "description": "S. Hopper - Main Account", + "reference": "YOUR_REFERENCE-X173L", + "timeZone": "Europe/Amsterdam", + "id": "BA32272223222B5CVF5J63LMW", + "status": "active" } ], "hasNext": true, diff --git a/Adyen.Test/mocks/balanceplatform/PaginatedPaymentInstrumentsResponse.json b/Adyen.Test/mocks/balanceplatform/PaginatedPaymentInstrumentsResponse.json index 23bf0add2..585cfdc6d 100644 --- a/Adyen.Test/mocks/balanceplatform/PaginatedPaymentInstrumentsResponse.json +++ b/Adyen.Test/mocks/balanceplatform/PaginatedPaymentInstrumentsResponse.json @@ -1,13 +1,14 @@ { - "hasNext": "true", - "hasPrevious": "false", + "hasNext": true, + "hasPrevious": false, "paymentInstruments": [ { "balanceAccountId": "BA32272223222B59CZ3T52DKZ", "issuingCountryCode": "GB", - "status": "Active", + "status": "active", "type": "card", "card": { + "brand": "mc", "brandVariant": "mc", "cardholderName": "name", "formFactor": "virtual", @@ -24,9 +25,10 @@ { "balanceAccountId": "BA32272223222B59CZ3T52DKZ", "issuingCountryCode": "GB", - "status": "Active", + "status": "active", "type": "card", "card": { + "brand": "mc", "brandVariant": "mc", "cardholderName": "name", "formFactor": "virtual", diff --git a/Adyen.Test/mocks/balanceplatform/PaymentInstrument.json b/Adyen.Test/mocks/balanceplatform/PaymentInstrument.json index ab6c7493e..48984ca79 100644 --- a/Adyen.Test/mocks/balanceplatform/PaymentInstrument.json +++ b/Adyen.Test/mocks/balanceplatform/PaymentInstrument.json @@ -1,6 +1,13 @@ { + "balanceAccountId": "BA3227C223222B5CTBLR8BWJB", + "issuingCountryCode": "NL", + "status": "active", "type": "bankAccount", "description": "YOUR_DESCRIPTION", - "balanceAccountId": "BA3227C223222B5CTBLR8BWJB", - "issuingCountryCode": "NL" + "bankAccount": { + "formFactor": "physical", + "type": "iban", + "iban": "NL20ADYB2017000035" + }, + "id": "PI322LJ223222B5DJS7CD9LWL" } \ No newline at end of file diff --git a/Adyen.Test/mocks/balanceplatform/SweepConfiguration.json b/Adyen.Test/mocks/balanceplatform/SweepConfiguration.json index 719d05edc..f27b44936 100644 --- a/Adyen.Test/mocks/balanceplatform/SweepConfiguration.json +++ b/Adyen.Test/mocks/balanceplatform/SweepConfiguration.json @@ -1,4 +1,5 @@ { + "id": "SWPC4227C224555B5FTD2NT2JV4WN5", "counterparty": { "merchantAccount": "YOUR_MERCHANT_ACCOUNT" }, diff --git a/Adyen.Test/mocks/balanceplatform/TransactionRuleResponse.json b/Adyen.Test/mocks/balanceplatform/TransactionRuleResponse.json index 8477e3717..fa6007f21 100644 --- a/Adyen.Test/mocks/balanceplatform/TransactionRuleResponse.json +++ b/Adyen.Test/mocks/balanceplatform/TransactionRuleResponse.json @@ -1,15 +1,28 @@ { "transactionRule": { - "description": "Allow 5 transactions per month", + "description": "Only allow point-of-sale transactions", + "entityKey": { + "entityReference": "PI3227C223222B5FN65FN5NS9", + "entityType": "paymentInstrument" + }, "interval": { - "type": "monthly" + "timeZone": "UTC", + "type": "perTransaction" + }, + "outcomeType": "hardBlock", + "reference": "YOUR_REFERENCE_4F7346", + "requestType": "authorization", + "ruleRestrictions": { + "processingTypes": { + "operation": "noneMatch", + "value": [ + "pos" + ] + } }, - "maxTransactions": 5, - "paymentInstrumentId": "PI3227C223222B59KGTXP884R", - "reference": "myRule12345", - "startDate": "2021-01-25T12:46:35", + "startDate": "2022-08-02T16:07:00.851374+02:00", "status": "active", - "type": "velocity", - "id": "TR32272223222B5CMD3V73HXG" + "type": "blockList", + "id": "TR32272223222B5GFSGFLFCHM" } } \ No newline at end of file diff --git a/Adyen.Test/mocks/balanceplatform/TransactionRulesResponse.json b/Adyen.Test/mocks/balanceplatform/TransactionRulesResponse.json index 89b977c5f..57b77005a 100644 --- a/Adyen.Test/mocks/balanceplatform/TransactionRulesResponse.json +++ b/Adyen.Test/mocks/balanceplatform/TransactionRulesResponse.json @@ -1,33 +1,66 @@ { "transactionRules": [ { - "description": "Allow 5 transactions per month", + "aggregationLevel": "paymentInstrument", + "description": "Up to 1000 EUR per card for the last 12 hours", + "entityKey": { + "entityReference": "PG3227C223222C5GXR3M5592Q", + "entityType": "paymentInstrumentGroup" + }, "interval": { - "type": "monthly" + "duration": { + "unit": "hours", + "value": 12 + }, + "timeZone": "UTC", + "type": "sliding" }, - "maxTransactions": 5, - "paymentInstrumentGroupId": "PG3227C223222B5CMD3FJFKGZ", - "reference": "myRule12345", - "startDate": "2021-01-25T12:46:35", - "status": "active", + "outcomeType": "hardBlock", + "reference": "YOUR_REFERENCE_2918A", + "requestType": "authorization", + "ruleRestrictions": { + "totalAmount": { + "operation": "greaterThan", + "value": { + "currency": "EUR", + "value": 100000 + } + } + }, + "status": "inactive", "type": "velocity", - "id": "TR32272223222B5CMDGMC9F4F" + "id": "TR3227C223222C5GXR3XP596N" }, { - "amount": { - "currency": "EUR", - "value": 10000 + "aggregationLevel": "paymentInstrument", + "description": "NL only", + "entityKey": { + "entityReference": "PG3227C223222C5GXR3M5592Q", + "entityType": "paymentInstrumentGroup" }, - "description": "Allow up to 100 EUR per month", "interval": { - "type": "monthly" + "duration": { + "unit": "hours", + "value": 12 + }, + "timeZone": "UTC", + "type": "sliding" + }, + "outcomeType": "hardBlock", + "reference": "myRule12345", + "requestType": "authorization", + "ruleRestrictions": { + "totalAmount": { + "operation": "greaterThan", + "value": { + "currency": "EUR", + "value": 100000 + } + } }, - "paymentInstrumentGroupId": "PG3227C223222B5CMD3FJFKGZ", - "reference": "myRule16378", - "startDate": "2021-01-25T12:46:35", - "status": "active", + "status": "inactive", "type": "velocity", - "id": "TR32272223222B5CMDGT89F4F" + "id": "TR3227C223222C5GXR3WC595H" } ] } \ No newline at end of file diff --git a/Adyen.Test/mocks/checkout/card-details-success.json b/Adyen.Test/mocks/checkout/card-details-success.json index 5945d09d7..697b8abfa 100644 --- a/Adyen.Test/mocks/checkout/card-details-success.json +++ b/Adyen.Test/mocks/checkout/card-details-success.json @@ -2,11 +2,14 @@ "brands": [ { "type": "visa", - "supported": "true" + "supported": true }, { "type": "cartebancaire", - "supported": "true" + "supported": false } - ] + ], + "fundingSource": "CREDIT", + "isCardCommercial": false, + "issuingCountryCode": "FR" } \ No newline at end of file diff --git a/Adyen.Test/mocks/checkout/payment-links-success.json b/Adyen.Test/mocks/checkout/payment-links-success.json index 2787a6788..ebf4be12c 100644 --- a/Adyen.Test/mocks/checkout/payment-links-success.json +++ b/Adyen.Test/mocks/checkout/payment-links-success.json @@ -1,4 +1,6 @@ { + "id": "your-id", + "status": "active", "amount": { "currency": "BRL", "value": 1250 diff --git a/Adyen.Test/mocks/checkout/payment-methods-response.json b/Adyen.Test/mocks/checkout/payment-methods-response.json new file mode 100644 index 000000000..693ce5251 --- /dev/null +++ b/Adyen.Test/mocks/checkout/payment-methods-response.json @@ -0,0 +1,151 @@ +{ + "paymentMethods": [ + { + "name": "AliPay", + "type": "alipay" + }, + { + "brands": [ + "cartebancaire", + "amex", + "mc", + "visa" + ], + "configuration": { + "mcDpaId": "6d41d4d6-45b1-42c3-a5d0-a28c0e69d4b1_dpa2", + "visaSrcInitiatorId": "B9SECVKIQX2SOBQ6J9X721dVBBKHhJJl1nxxVbemHGn5oB6S8", + "mcSrcClientId": "6d41d4d6-45b1-42c3-a5d0-a28c0e69d4b1", + "visaSrciDpaId": "8e6e347c-254e-863f-0e6a-196bf2d9df02" + }, + "name": "Cards", + "type": "scheme" + }, + { + "configuration": { + "merchantId": "000000000202326", + "merchantName": "TestMerchantAccount" + }, + "name": "Apple Pay", + "type": "applepay" + }, + { + "name": "Payconiq by Bancontact", + "type": "bcmc_mobile" + }, + { + "name": "Boleto Bancario", + "type": "boletobancario" + }, + { + "name": "Online bank transfer.", + "type": "directEbanking" + }, + { + "name": "DOKU", + "type": "doku" + }, + { + "name": "DOKU wallet", + "type": "doku_wallet" + }, + { + "brand": "***************", + "name": "Generic GiftCard", + "type": "giftcard" + }, + { + "brand": "*****", + "name": "Givex", + "type": "giftcard" + }, + { + "name": "GoPay Wallet", + "type": "gopay_wallet" + }, + { + "name": "GrabPay", + "type": "grabpay_SG" + }, + { + "issuers": [ + { + "id": "************", + "name": "*****" + } + ], + "name": "iDEAL", + "type": "ideal" + }, + { + "name": "Korea–issued cards", + "type": "kcp_creditcard" + }, + { + "name": "Pay later with Klarna.", + "type": "klarna" + }, + { + "name": "Pay over time with Klarna.", + "type": "klarna_account" + }, + { + "name": "Pay now with Klarna.", + "type": "klarna_paynow" + }, + { + "name": "MB WAY", + "type": "mbway" + }, + { + "name": "MobilePay", + "type": "mobilepay" + }, + { + "configuration": { + "merchantId": "50", + "gatewayMerchantId": "TestMerchantAccount" + }, + "name": "Google Pay", + "type": "paywithgoogle" + }, + { + "name": "pix", + "type": "pix" + }, + { + "name": "SEPA Direct Debit", + "type": "sepadirectdebit" + }, + { + "brand": "***", + "name": "SVS", + "type": "giftcard" + }, + { + "name": "UPI Collect", + "type": "upi_collect" + }, + { + "name": "UPI Intent", + "type": "upi_intent" + }, + { + "name": "UPI QR", + "type": "upi_qr" + }, + { + "brand": "*********", + "name": "Valuelink", + "type": "giftcard" + }, + { + "name": "Vipps", + "type": "vipps" + }, + { + "brand": "***********", + "name": "VVV Giftcard", + "type": "giftcard" + } + ] +} diff --git a/Adyen.Test/mocks/checkout/paymentlinks-recurring-payment-success.json b/Adyen.Test/mocks/checkout/paymentlinks-recurring-payment-success.json deleted file mode 100644 index 628896bea..000000000 --- a/Adyen.Test/mocks/checkout/paymentlinks-recurring-payment-success.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "amount": { - "currency": "EUR", - "value": 100 - }, - "expiresAt": "2020-10-28T12:00:00Z", - "reference": "REFERENCE_NUMBER", - "url": "https://checkoutshopper-test.adyen.com/checkoutshopper/payByLink.shtml?d=YW1vdW50TWlub3JW...JRA", - "merchantAccount": "YOUR_MERCHANT_ACCOUNT" -} \ No newline at end of file diff --git a/Adyen.Test/mocks/checkout/paymentmethods-error-forbidden-403.json b/Adyen.Test/mocks/checkout/paymentmethods-error-forbidden-403.json deleted file mode 100644 index 28afc9887..000000000 --- a/Adyen.Test/mocks/checkout/paymentmethods-error-forbidden-403.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "status": 403, - "errorCode": "901", - "message": "Invalid Merchant Account", - "errorType": "security" -} \ No newline at end of file diff --git a/Adyen.Test/mocks/checkout/payments-success.json b/Adyen.Test/mocks/checkout/payments-success.json index 0deff7d1a..25b34868f 100644 --- a/Adyen.Test/mocks/checkout/payments-success.json +++ b/Adyen.Test/mocks/checkout/payments-success.json @@ -14,81 +14,34 @@ "accountScore": 25, "results": [ { - "FraudCheckResult": { - "accountScore": 0, - "checkId": 2, - "name": "CardChunkUsage" - } + "accountScore": 0, + "checkId": 2, + "name": "CardChunkUsage" }, { - "FraudCheckResult": { - "accountScore": 0, - "checkId": 3, - "name": "PaymentDetailUsage" - } + "accountScore": 0, + "checkId": 3, + "name": "PaymentDetailUsage" }, { - "FraudCheckResult": { - "accountScore": 0, - "checkId": 4, - "name": "HolderNameUsage" - } + "accountScore": 0, + "checkId": 4, + "name": "HolderNameUsage" }, { - "FraudCheckResult": { - "accountScore": 0, - "checkId": 1, - "name": "PaymentDetailRefCheck" - } + "accountScore": 0, + "checkId": 1, + "name": "PaymentDetailRefCheck" }, { - "FraudCheckResult": { - "accountScore": 0, - "checkId": 13, - "name": "IssuerRefCheck" - } + "accountScore": 0, + "checkId": 13, + "name": "IssuerRefCheck" }, { - "FraudCheckResult": { - "accountScore": 0, - "checkId": 15, - "name": "IssuingCountryReferral" - } - }, - { - "FraudCheckResult": { - "accountScore": 0, - "checkId": 27, - "name": "PmOwnerRefCheck" - } - }, - { - "FraudCheckResult": { - "accountScore": 0, - "checkId": 10, - "name": "HolderNameContainsNumber" - } - }, - { - "FraudCheckResult": { - "accountScore": 0, - "checkId": 11, - "name": "HolderNameIsOneWord" - } - }, - { - "FraudCheckResult": { - "accountScore": 0, - "checkId": 82, - "name": "CustomFieldCheck" - } - }, - { - "FraudCheckResult": { - "accountScore": 0, - "checkId": 25, - "name": "CVCAuthResultCheck" - } + "accountScore": 0, + "checkId": 15, + "name": "IssuingCountryReferral" } ] }, diff --git a/Adyen.Test/mocks/checkout/paymentsdetails-error-invalid-data-422.json b/Adyen.Test/mocks/checkout/paymentsdetails-error-invalid-data-422.json deleted file mode 100644 index ce814f1ef..000000000 --- a/Adyen.Test/mocks/checkout/paymentsdetails-error-invalid-data-422.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "status": 422, - "errorCode": "101", - "message": "Invalid card number", - "errorType": "validation" -} \ No newline at end of file diff --git a/Adyen.Test/mocks/checkout/paymentsresult-multibanco-success.json b/Adyen.Test/mocks/checkout/paymentsresult-multibanco-success.json deleted file mode 100644 index 5e2c9d09c..000000000 --- a/Adyen.Test/mocks/checkout/paymentsresult-multibanco-success.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "resultCode": "PresentToShopper", - "action": { - "paymentMethodType": "multibanco", - "type": "voucher", - "entity": "12101", - "expiresAt": "2020-01-12T09:37:49", - "initialAmount": { - "currency": "EUR", - "value": 1000 - }, - "merchantName": "YOUR_MERCHANT", - "merchantReference": "YOUR_ORDER_NUMBER", - "reference": "501 422 944", - "totalAmount": { - "currency": "EUR", - "value": 1000 - }, - "action": "voucher" - } -} \ No newline at end of file diff --git a/Adyen.Test/mocks/disputes/accept-disputes.json b/Adyen.Test/mocks/disputes/accept-dispute.json similarity index 100% rename from Adyen.Test/mocks/disputes/accept-disputes.json rename to Adyen.Test/mocks/disputes/accept-dispute.json diff --git a/Adyen.Test/mocks/disputes/retrieve-appicable-defense-reasons.json b/Adyen.Test/mocks/disputes/retrieve-applicable-defense-reasons.json similarity index 100% rename from Adyen.Test/mocks/disputes/retrieve-appicable-defense-reasons.json rename to Adyen.Test/mocks/disputes/retrieve-applicable-defense-reasons.json diff --git a/Adyen.Test/mocks/management/list-terminals.json b/Adyen.Test/mocks/management/list-terminals.json index 9f50ff321..3625f6f40 100644 --- a/Adyen.Test/mocks/management/list-terminals.json +++ b/Adyen.Test/mocks/management/list-terminals.json @@ -1,4 +1,20 @@ { + "_links": { + "first": { + "href": "https://management-test.adyen.com/v3/terminals?pageNumber=1&pageSize=20" + }, + "last": { + "href": "https://management-test.adyen.com/v3/terminals?pageNumber=1&pageSize=20" + }, + "next": { + "href": "https://management-test.adyen.com/v3/terminals?pageNumber=1&pageSize=20" + }, + "self": { + "href": "https://management-test.adyen.com/v3/terminals?pageNumber=0&pageSize=20" + } + }, + "itemsTotal": 1, + "pagesTotal": 1, "data": [ { "id": "S1F2-000150183300032", diff --git a/Adyen.Test/mocks/pos-terminal-management/assing-terminals-success.json b/Adyen.Test/mocks/pos-terminal-management/assigning-terminals-success.json similarity index 100% rename from Adyen.Test/mocks/pos-terminal-management/assing-terminals-success.json rename to Adyen.Test/mocks/pos-terminal-management/assigning-terminals-success.json diff --git a/Adyen.Test/mocks/pos-terminal-management/get-terminals-details-success.json b/Adyen.Test/mocks/pos-terminal-management/get-terminal-details-success.json similarity index 100% rename from Adyen.Test/mocks/pos-terminal-management/get-terminals-details-success.json rename to Adyen.Test/mocks/pos-terminal-management/get-terminal-details-success.json diff --git a/Adyen.Test/mocks/transfers/get-all-transactions.json b/Adyen.Test/mocks/transfers/get-all-transactions.json index b55e6c2e7..c253e2bed 100644 --- a/Adyen.Test/mocks/transfers/get-all-transactions.json +++ b/Adyen.Test/mocks/transfers/get-all-transactions.json @@ -1,72 +1,109 @@ { "data": [ { - "accountHolderId": "AHA1B2C3D4E5F6G7H8I9J0", + "balancePlatform": "YOUR_BALANCE_PLATFORM", + "creationDate": "2023-08-10T14:51:20+02:00", + "id": "EVJN42272224222B5JB8BRC84N686ZEUR", + "accountHolder": { + "description": "Your description for the account holder", + "id": "AH00000000000000000000001" + }, "amount": { - "currency": "EUR", - "value": -9 + "currency": "USD", + "value": -1000 }, - "balanceAccountId": "BAB8B2C3D4E5F6G7H8D9J6GD4", - "balancePlatform": "YOUR_BALANCE_PLATFORM", - "bookingDate": "2022-03-11T11:21:24+01:00", - "category": "internal", - "createdAt": "2022-03-11T11:21:24+01:00", - "id": "1VVF0D5U66PIUIVP", - "instructedAmount": { - "currency": "EUR", - "value": -9 - }, - "reference": "REFERENCE_46e8c40e", + "balanceAccount": { + "description": "Your description for the account holder", + "id": "BA00000000000000000000001" + }, + "bookingDate": "2023-08-10T14:51:33+02:00", + "eventId": "EVJN42272224222B5JB8BRC84N686Z", "status": "booked", - "transferId": "1VVF0D5U66PIUIVP", - "type": "fee", - "valueDate": "2022-03-11T11:21:24+01:00" + "transfer": { + "id": "3JNC3O5ZVFLLGV4B", + "reference": "Your internal reference for the transfer" + }, + "valueDate": "2023-08-10T14:51:20+02:00" }, { - "accountHolderId": "AHA1B2C3D4E5F6G7H8I9J0", + "balancePlatform": "YOUR_BALANCE_PLATFORM", + "creationDate": "2023-08-10T15:34:31+02:00", + "id": "EVJN4227C224222B5JB8G3Q89N2NB6EUR", + "accountHolder": { + "description": "Your description for the account holder", + "id": "AH00000000000000000000001" + }, "amount": { - "currency": "EUR", - "value": -46 + "currency": "USD", + "value": 123 }, - "balanceAccountId": "BAB8B2C3D4E5F6G7H8D9J6GD4", - "balancePlatform": "YOUR_BALANCE_PLATFORM", - "bookingDate": "2022-03-12T14:22:52+01:00", - "category": "internal", - "createdAt": "2022-03-12T14:22:52+01:00", - "id": "1WEPGD5U6MS1CFK3", - "instructedAmount": { - "currency": "EUR", - "value": -46 - }, - "reference": "YOUR_REFERENCE", + "balanceAccount": { + "description": "Your description for the account holder", + "id": "BA00000000000000000000001" + }, + "bookingDate": "2023-08-10T15:34:40+02:00", + "eventId": "EVJN4227C224222B5JB8G3Q89N2NB6", "status": "booked", - "transferId": "1WEPGD5U6MS1CFK3", - "type": "fee", - "valueDate": "2022-03-12T14:22:52+01:00" + "transfer": { + "id": "48POO45ZVG11166J", + "reference": "Your internal reference for the transfer" + }, + "valueDate": "2023-08-10T15:34:31+02:00" }, { - "accountHolderId": "AHA1B2C3D4E5F6G7H8I9J0", + "balancePlatform": "YOUR_BALANCE_PLATFORM", + "creationDate": "2023-08-11T13:45:46+02:00", + "id": "EVJN4227C224222B5JBD3XHF8P3L8GUSD", + "accountHolder": { + "description": "Your description for the account holder", + "id": "AH00000000000000000000001" + }, "amount": { - "currency": "EUR", - "value": -8 + "currency": "USD", + "value": -10000 + }, + "balanceAccount": { + "description": "Your description for the account holder", + "id": "BA00000000000000000000001" }, - "balanceAccountId": "BAB8B2C3D4E5F6G7H8D9J6GD4", + "bookingDate": "2023-08-11T13:45:57+02:00", + "eventId": "EVJN4227C224222B5JBD3XHF8P3L8G", + "status": "booked", + "transfer": { + "id": "48RTTW5ZVT8KU9DV", + "reference": "my-reference" + }, + "valueDate": "2023-08-11T13:45:46+02:00" + }, + { "balancePlatform": "YOUR_BALANCE_PLATFORM", - "bookingDate": "2022-03-14T21:00:48+01:00", - "createdAt": "2022-03-14T15:00:00+01:00", - "description": "YOUR_DESCRIPTION_2", - "id": "2QP32A5U7IWC5WKG", - "instructedAmount": { - "currency": "EUR", - "value": -8 + "creationDate": "2023-08-11T13:45:51+02:00", + "id": "EVJN42272224222B5JBD3XJGHF4J26USD", + "accountHolder": { + "description": "Your description for the account holder", + "id": "AH00000000000000000000001" + }, + "amount": { + "currency": "USD", + "value": 1000 }, + "balanceAccount": { + "description": "Your description for the account holder", + "id": "BA00000000000000000000001" + }, + "bookingDate": "2023-08-11T13:45:58+02:00", + "eventId": "EVJN42272224222B5JBD3XJGHF4J26", "status": "booked", - "valueDate": "2022-03-14T21:00:48+01:00" + "transfer": { + "id": "48TYZO5ZVT8M1K47", + "reference": "my-reference" + }, + "valueDate": "2023-08-11T13:45:51+02:00" } ], "_links": { "next": { - "href": "https://balanceplatform-api-test.adyen.com/btl/v2/transactions?balancePlatform=Bastronaut&createdUntil=2022-03-21T00%3A00%3A00Z&createdSince=2022-03-11T00%3A00%3A00Z&limit=3&cursor=S2B-TSAjOkIrYlIlbjdqe0RreHRyM32lKRSxubXBHRkhHL2E32XitQQz5SfzpucD5HbHwpM1p6NDR1eXVQLFF6MmY33J32sobDxQYT90MHIud1hwLnd6JitcX32xJ" + "href": "https://balanceplatform-api-test.adyen.com/btl/v4/transactions?balancePlatform=TestBalancePlatform&createdUntil=2023-08-20T13%3A07%3A40Z&createdSince=2023-08-10T10%3A50%3A40Z&cursor=S2B-c0p1N0tdN0l6RGhYK1YpM0lgOTUyMDlLXElyKE9LMCtyaFEuMj1NMHgidCsrJi1ZNnhqXCtqVi5JPGpRK1F2fCFqWzU33JTojSVNJc1J1VXhncS10QDd6JX9FQFl5Zn0uNyUvSXJNQTo" } } } \ No newline at end of file diff --git a/Adyen/Adyen.csproj b/Adyen/Adyen.csproj index fa180e07d..f7f7a4088 100644 --- a/Adyen/Adyen.csproj +++ b/Adyen/Adyen.csproj @@ -1,7 +1,7 @@ - + - net8.0;net6.0;net462;netstandard2.0 + net8.0 12 enable disable @@ -36,9 +36,12 @@ - + + + - + + @@ -54,4 +57,7 @@ + + + diff --git a/Adyen/Constants/ClientConfig.cs b/Adyen/Constants/ClientConfig.cs deleted file mode 100644 index 8e6ce0e55..000000000 --- a/Adyen/Constants/ClientConfig.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Adyen.Constants -{ - public class ClientConfig - { - //Test cloud api endpoints - public const string CloudApiEndPointTest = "https://terminal-api-test.adyen.com"; - - //Live cloud api endpoints - public const string CloudApiEndPointEULive = "https://terminal-api-live.adyen.com"; - public const string CloudApiEndPointAULive = "https://terminal-api-live-au.adyen.com"; - public const string CloudApiEndPointUSLive = "https://terminal-api-live-us.adyen.com"; - public const string CloudApiEndPointAPSELive = "https://terminal-api-live-apse.adyen.com"; - - public const string UserAgentSuffix = "adyen-dotnet-api-library/"; - public const string NexoProtocolVersion = "3.0"; - - public const string LibName = "adyen-dotnet-api-library"; - public const string LibVersion = "32.0.0"; - } -} diff --git a/Adyen/Core/Auth/TokenBase.cs b/Adyen/Core/Auth/TokenBase.cs new file mode 100644 index 000000000..a0afd072c --- /dev/null +++ b/Adyen/Core/Auth/TokenBase.cs @@ -0,0 +1,20 @@ +#nullable enable + +using System; + +namespace Adyen.Core.Auth +{ + /// + /// The base class for all auth tokens. + /// + public abstract class TokenBase + { + /// + /// The constructor for the TokenBase object, used by . + /// + protected TokenBase() + { + + } + } +} \ No newline at end of file diff --git a/Adyen/Core/Auth/TokenProvider.cs b/Adyen/Core/Auth/TokenProvider.cs new file mode 100644 index 000000000..745cca691 --- /dev/null +++ b/Adyen/Core/Auth/TokenProvider.cs @@ -0,0 +1,42 @@ +namespace Adyen.Core.Auth +{ + /// + /// An interface for providing tokens in a generic way. + /// + /// + public interface ITokenProvider where TTokenBase : TokenBase + { + /// + /// Retrieves the stored token. + /// + /// + TTokenBase Get(); + } + + /// + /// A class which will provide tokens from type . + /// + /// + public class TokenProvider : ITokenProvider where TTokenBase : TokenBase + { + private readonly TTokenBase _token; + + /// + /// Initializes a token with type . + /// + /// + public TokenProvider(TTokenBase token) + { + _token = token; + } + + /// + /// Retrieves the stored token. + /// + /// + public TTokenBase Get() + { + return _token; + } + } +} \ No newline at end of file diff --git a/Adyen/Core/Client/ApiException.cs b/Adyen/Core/Client/ApiException.cs new file mode 100644 index 000000000..23a732bc9 --- /dev/null +++ b/Adyen/Core/Client/ApiException.cs @@ -0,0 +1,40 @@ +#nullable enable + +using System; + +namespace Adyen.Core.Client +{ + /// + /// API Exception + /// + public class ApiException : Exception + { + /// + /// The reason the api request failed + /// + public string? ReasonPhrase { get; } + + /// + /// The HttpStatusCode + /// + public System.Net.HttpStatusCode StatusCode { get; } + + /// + /// The raw data returned by the API + /// + public string RawContent { get; } + + /// + /// Construct the ApiException from parts of the response + /// + /// Reason for ApiException + /// + /// Raw content + public ApiException(string? reasonPhrase, System.Net.HttpStatusCode statusCode, string rawContent) : base(reasonPhrase ?? rawContent) + { + ReasonPhrase = reasonPhrase; + StatusCode = statusCode; + RawContent = rawContent; + } + } +} diff --git a/Adyen/Core/Client/ApiFactory.cs b/Adyen/Core/Client/ApiFactory.cs new file mode 100644 index 000000000..2c073ba34 --- /dev/null +++ b/Adyen/Core/Client/ApiFactory.cs @@ -0,0 +1,48 @@ +using System; +using Microsoft.Extensions.DependencyInjection; + +namespace Adyen.Core.Client +{ + /// + /// The factory interface for creating the services that can communicate with the Adyen APIs. + /// + public interface IApiFactory + { + /// + /// A method to create an IApi of type IResult + /// + /// + /// + IResult Create() where IResult : IAdyenApiService; + } + + /// + /// The implementation of . + /// + public class ApiFactory : IApiFactory + { + /// + /// The service provider + /// + public IServiceProvider Services { get; } + + /// + /// Initializes a new instance of the class. + /// + /// + public ApiFactory(IServiceProvider services) + { + Services = services; + } + + /// + /// A method to create an IApi of type IResult + /// + /// + /// + public IResult Create() where IResult : IAdyenApiService + { + return Services.GetRequiredService(); + } + } +} \ No newline at end of file diff --git a/Adyen/Core/Client/ApiResponse.cs b/Adyen/Core/Client/ApiResponse.cs new file mode 100644 index 000000000..1eb24e24b --- /dev/null +++ b/Adyen/Core/Client/ApiResponse.cs @@ -0,0 +1,371 @@ +#nullable enable +using System; +using System.Diagnostics.CodeAnalysis; +using System.Net; + +namespace Adyen.Core.Client +{ + /// + /// Provides a non-generic contract for the ApiResponse wrapper. + /// + public partial interface IApiResponse + { + /// + /// The IsSuccessStatusCode from the API response. + /// + bool IsSuccessStatusCode { get; } + + /// + /// Gets the status code (). + /// + /// The status code. + HttpStatusCode StatusCode { get; } + + /// + /// The raw content of this response. + /// + string RawContent { get; } + + /// + /// The raw binary stream (only set for binary responses). + /// + System.IO.Stream? ContentStream { get; } + + /// + /// The DateTime when the request was retrieved. + /// + DateTime DownloadedAt { get; } + + /// + /// The headers contained in the API response. + /// + System.Net.Http.Headers.HttpResponseHeaders Headers { get; } + + /// + /// The path used when making the request. + /// + string Path { get; } + + /// + /// The reason phrase contained in the API response. + /// + string? ReasonPhrase { get; } + + /// + /// The DateTime when the request was sent. + /// + DateTime RequestedAt { get; } + + /// + /// The Uri used when making the request. + /// + Uri? RequestUri { get; } + } + + /// + /// API Response + /// + public partial class ApiResponse : IApiResponse + { + /// + /// Gets the status code (HTTP status code). + /// + /// The status code. + public HttpStatusCode StatusCode { get; } + + /// + /// The raw data. + /// + public string RawContent { get; protected set; } + + /// + /// The raw binary stream (only set for binary responses). + /// + public System.IO.Stream? ContentStream { get; protected set; } + + /// + /// The IsSuccessStatusCode from the API response. + /// + public bool IsSuccessStatusCode { get; } + + /// + /// The reason phrase contained in the API response. + /// + public string? ReasonPhrase { get; } + + /// + /// The headers contained in the API response. + /// + public System.Net.Http.Headers.HttpResponseHeaders Headers { get; } + + /// + /// The DateTime (default: UtcNow) when the request was retrieved. + /// + public DateTime DownloadedAt { get; } = DateTime.UtcNow; + + /// + /// The DateTime when the request was sent. + /// + public DateTime RequestedAt { get; } + + /// + /// The path used when making the request. + /// + public string Path { get; } + + /// + /// The used when making the request. + /// + public Uri? RequestUri { get; } + + /// + /// The . + /// + protected System.Text.Json.JsonSerializerOptions _jsonSerializerOptions; + + /// + /// Construct the response using an HttpResponseMessage. + /// + /// . + /// + /// The raw data. + /// The path used when making the request. + /// The when the request was sent. + /// The . + public ApiResponse(global::System.Net.Http.HttpRequestMessage httpRequestMessage, System.Net.Http.HttpResponseMessage httpResponseMessage, string rawContent, string path, DateTime requestedAt, System.Text.Json.JsonSerializerOptions jsonSerializerOptions) + { + StatusCode = httpResponseMessage.StatusCode; + Headers = httpResponseMessage.Headers; + IsSuccessStatusCode = httpResponseMessage.IsSuccessStatusCode; + ReasonPhrase = httpResponseMessage.ReasonPhrase; + RawContent = rawContent; + Path = path; + RequestUri = httpRequestMessage.RequestUri; + RequestedAt = requestedAt; + _jsonSerializerOptions = jsonSerializerOptions; + } + + /// + /// Construct the response using the . + /// + /// . + /// . + /// The raw binary stream (only set for binary responses). + /// The path used when making the request. + /// The DateTime.UtcNow when the request was sent. + /// The . + public ApiResponse(global::System.Net.Http.HttpRequestMessage httpRequestMessage, System.Net.Http.HttpResponseMessage httpResponseMessage, System.IO.Stream contentStream, string path, DateTime requestedAt, System.Text.Json.JsonSerializerOptions jsonSerializerOptions) + { + StatusCode = httpResponseMessage.StatusCode; + Headers = httpResponseMessage.Headers; + IsSuccessStatusCode = httpResponseMessage.IsSuccessStatusCode; + ReasonPhrase = httpResponseMessage.ReasonPhrase; + ContentStream = contentStream; + RawContent = string.Empty; + Path = path; + RequestUri = httpRequestMessage.RequestUri; + RequestedAt = requestedAt; + _jsonSerializerOptions = jsonSerializerOptions; + } + } + + /// + /// An interface for responses of type BadRequest. + /// + /// + public interface IBadRequest : IApiResponse + { + /// + /// Deserializes the response if the response is BadRequest. + /// + /// + TType BadRequest(); + + /// + /// Returns true if the response is BadRequest and the deserialized response is not null. + /// + /// + /// + bool TryDeserializeBadRequestResponse([NotNullWhen(true)]out TType? result); + } + + /// + /// An interface for responses of type TooManyRequests. + /// + /// + public interface ITooManyRequests : IApiResponse + { + /// + /// Deserializes the response if the response is TooManyRequests. + /// + /// + TType TooManyRequests(); + + /// + /// Returns true if the response is TooManyRequests and the deserialized response is not null. + /// + /// + /// + bool TryDeserializeTooManyRequestsResponse([NotNullWhen(true)]out TType? result); + } + + /// + /// An interface for responses of type Unauthorized. + /// + /// + public interface IUnauthorized : IApiResponse + { + /// + /// Deserializes the response if the response is Unauthorized. + /// + /// + TType Unauthorized(); + + /// + /// Returns true if the response is Unauthorized and the deserialized response is not null. + /// + /// + /// + bool TryDeserializeUnauthorizedResponse([NotNullWhen(true)]out TType? result); + } + + /// + /// An interface for responses of type Forbidden. + /// + /// + public interface IForbidden : IApiResponse + { + /// + /// Deserializes the response if the response is Forbidden. + /// + /// + TType Forbidden(); + + /// + /// Returns true if the response is Forbidden and the deserialized response is not null. + /// + /// + /// + bool TryDeserializeForbiddenResponse([NotNullWhen(true)]out TType? result); + } + + /// + /// An interface for responses of type Ok. + /// + /// + public interface IOk : IApiResponse + { + /// + /// Deserializes the response if the response is Ok. + /// + /// + TType Ok(); + + /// + /// Returns true if the response is Ok and the deserialized response is not null. + /// + /// + /// + bool TryDeserializeOkResponse([NotNullWhen(true)]out TType? result); + } + + /// + /// An interface for responses of type UnprocessableContent. + /// + /// + public interface IUnprocessableContent : IApiResponse + { + /// + /// Deserializes the response if the response is UnprocessableContent. + /// + /// + TType UnprocessableContent(); + + /// + /// Returns true if the response is UnprocessableContent and the deserialized response is not null. + /// + /// + /// + bool TryDeserializeUnprocessableContentResponse([NotNullWhen(true)]out TType? result); + } + + /// + /// An interface for responses of type InternalServerError. + /// + /// + public interface IInternalServerError : IApiResponse + { + /// + /// Deserializes the response if the response is InternalServerError. + /// + /// + TType InternalServerError(); + + /// + /// Returns true if the response is InternalServerError and the deserialized response is not null. + /// + /// + /// + bool TryDeserializeInternalServerErrorResponse([NotNullWhen(true)]out TType? result); + } + + /// + /// An interface for responses of type Created. + /// + /// + public interface ICreated : IApiResponse + { + /// + /// Deserializes the response if the response is Created. + /// + /// + TType Created(); + + /// + /// Returns true if the response is Created and the deserialized response is not null. + /// + /// + /// + bool TryDeserializeCreatedResponse([NotNullWhen(true)]out TType? result); + } + + /// + /// An interface for responses of type Accepted. + /// + /// + public interface IAccepted : IApiResponse + { + /// + /// Deserializes the response if the response is Accepted. + /// + /// + TType Accepted(); + + /// + /// Returns true if the response is Accepted and the deserialized response is not null. + /// + /// + /// + bool TryDeserializeAcceptedResponse([NotNullWhen(true)]out TType? result); + } + + /// + /// An interface for responses of type NotFound. + /// + /// + public interface INotFound : IApiResponse + { + /// + /// Deserializes the response if the response is NotFound. + /// + /// + TType NotFound(); + + /// + /// Returns true if the response is NotFound and the deserialized response is not null. + /// + /// + /// + bool TryDeserializeNotFoundResponse([NotNullWhen(true)]out TType? result); + } +} diff --git a/Adyen/Core/Client/ApiResponseEventArgs.cs b/Adyen/Core/Client/ApiResponseEventArgs.cs new file mode 100644 index 000000000..7f6d48f23 --- /dev/null +++ b/Adyen/Core/Client/ApiResponseEventArgs.cs @@ -0,0 +1,24 @@ +using System; + +namespace Adyen.Core.Client +{ + /// + /// This class is used for wrapping the . + /// + public class ApiResponseEventArgs : EventArgs + { + /// + /// The . + /// + public ApiResponse ApiResponse { get; } + + /// + /// The constructed from the Adyen . + /// + /// . + public ApiResponseEventArgs(ApiResponse apiResponse) + { + ApiResponse = apiResponse; + } + } +} diff --git a/Adyen/Core/Client/ExceptionEventArgs.cs b/Adyen/Core/Client/ExceptionEventArgs.cs new file mode 100644 index 000000000..1cbfc25fa --- /dev/null +++ b/Adyen/Core/Client/ExceptionEventArgs.cs @@ -0,0 +1,24 @@ +using System; + +namespace Adyen.Core.Client +{ + /// + /// Useful for tracking server health + /// + public class ExceptionEventArgs : EventArgs + { + /// + /// The ApiResponse exception + /// + public Exception Exception { get; } + + /// + /// The ExceptionEventArgs + /// + /// + public ExceptionEventArgs(Exception exception) + { + Exception = exception; + } + } +} diff --git a/Adyen/Core/Client/Extensions/HttpClientBuilderExtensions.cs b/Adyen/Core/Client/Extensions/HttpClientBuilderExtensions.cs new file mode 100644 index 000000000..bc5187d30 --- /dev/null +++ b/Adyen/Core/Client/Extensions/HttpClientBuilderExtensions.cs @@ -0,0 +1,68 @@ +#nullable enable + +using System; +using System.Net.Http; +using Microsoft.Extensions.DependencyInjection; +using Polly.Timeout; +using Polly.Extensions.Http; +using Polly; + +namespace Adyen.Core.Client.Extensions +{ + /// + /// Extension methods for IHttpClientBuilder + /// + public static class HttpClientBuilderExtensions + { + /// + /// Adds a Polly retry policy to your clients. + /// + /// . + /// The number of retries. + /// . + public static IHttpClientBuilder AddRetryPolicy(this IHttpClientBuilder httpClient, int numberOfRetries) + { + httpClient.AddPolicyHandler(RetryPolicy(numberOfRetries)); + return httpClient; + } + + private static Polly.Retry.AsyncRetryPolicy RetryPolicy(int numberOfRetries) + => HttpPolicyExtensions + .HandleTransientHttpError() + .Or() + .RetryAsync(numberOfRetries); + + /// + /// Adds a Polly timeout policy to your clients. + /// Use this when you need resilient policies (using the ) and want to combine this with a retry & circuit breaker. + /// + /// . + /// . + /// . + public static IHttpClientBuilder AddTimeoutPolicy(this IHttpClientBuilder httpClient, TimeSpan timeout) + { + httpClient.AddPolicyHandler(TimeoutPolicy(timeout)); + return httpClient; + } + + private static AsyncTimeoutPolicy TimeoutPolicy(TimeSpan timeout) + => Policy.TimeoutAsync(timeout); + + /// + /// Adds a Polly circuit breaker to your clients. + /// + /// . + /// Example: if set to 3 - if 3 consecutive request fail, Polly will 'open' the circuit for the duration of and fail all incoming requests. After that, the circuit will be 'half-open'. + /// . + /// . + public static IHttpClientBuilder AddCircuitBreakerPolicy(this IHttpClientBuilder httpClient, int numberOfEventsAllowedBeforeBreaking, TimeSpan durationOfBreak) + { + httpClient.AddTransientHttpErrorPolicy(policyBuilder => CircuitBreakerPolicy(policyBuilder, numberOfEventsAllowedBeforeBreaking, durationOfBreak)); + return httpClient; + } + + private static Polly.CircuitBreaker.AsyncCircuitBreakerPolicy CircuitBreakerPolicy( + PolicyBuilder policyBuilder, int numberOfEventsAllowedBeforeBreaking, TimeSpan durationOfBreak) + => policyBuilder.CircuitBreakerAsync(numberOfEventsAllowedBeforeBreaking, durationOfBreak); + } +} diff --git a/Adyen/Core/Client/Extensions/HttpRequestMessageExtensions.cs b/Adyen/Core/Client/Extensions/HttpRequestMessageExtensions.cs new file mode 100644 index 000000000..077af8da6 --- /dev/null +++ b/Adyen/Core/Client/Extensions/HttpRequestMessageExtensions.cs @@ -0,0 +1,63 @@ +namespace Adyen.Core.Client.Extensions +{ + /// + /// Extension function that adds custom headers and UserAgent to the . + /// + public static class HttpRequestMessageExtensions + { + /// + /// The name of the application. + /// + public static string ApplicationName { get; set; } + + /// + /// Name of this library. This will be sent as a part of the headers. + /// + public const string AdyenLibraryName = "adyen-dotnet-api-library"; + + /// + /// Version of this library. + /// + public const string AdyenLibraryVersion = "32.2.1"; // TODO replace this with the actual version. + + /// + /// Adds the UserAgent to the headers of the object. + /// + /// . + /// . + public static HttpRequestMessage AddUserAgentToHeaders(this HttpRequestMessage httpRequestMessage) + { + // Add application name if set. + if (!string.IsNullOrWhiteSpace(ApplicationName)) + { + httpRequestMessage.Headers.Add("UserAgent", $"{ApplicationName} {AdyenLibraryName}/{AdyenLibraryVersion}"); + return httpRequestMessage; + } + + httpRequestMessage.Headers.Add("UserAgent", $"{AdyenLibraryName}/{AdyenLibraryVersion}"); + return httpRequestMessage; + } + + /// + /// Adds the to the headers of the object. + /// + /// . + /// . + public static HttpRequestMessage AddLibraryNameToHeader(this HttpRequestMessage httpRequestMessage) + { + httpRequestMessage.Headers.Add("adyen-library-name", AdyenLibraryName); + return httpRequestMessage; + } + + /// + /// Adds the to the headers of the object. + /// + /// . + /// . + public static HttpRequestMessage AddLibraryVersionToHeader(this HttpRequestMessage httpRequestMessage) + { + httpRequestMessage.Headers.Add("adyen-library-version", AdyenLibraryVersion); + return httpRequestMessage; + } + } +} \ No newline at end of file diff --git a/Adyen/Core/Client/IAdyenApiService.cs b/Adyen/Core/Client/IAdyenApiService.cs new file mode 100644 index 000000000..ca6e164d6 --- /dev/null +++ b/Adyen/Core/Client/IAdyenApiService.cs @@ -0,0 +1,13 @@ +namespace Adyen.Core.Client +{ + /// + /// Interface for interacting with any Adyen API using . + /// + public interface IAdyenApiService + { + /// + /// The object, best practice: instantiate and manage object using the . + /// + System.Net.Http.HttpClient HttpClient { get; } + } +} \ No newline at end of file diff --git a/Adyen/Core/Client/UrlBuilderExtensions.cs b/Adyen/Core/Client/UrlBuilderExtensions.cs new file mode 100644 index 000000000..37885f81a --- /dev/null +++ b/Adyen/Core/Client/UrlBuilderExtensions.cs @@ -0,0 +1,80 @@ +using Adyen.Core.Options; + +namespace Adyen.Core.Client +{ + /// + /// Helper utility functions to construct the Adyen live-urls for our APIs. + /// + public static class UrlBuilderExtensions + { + /// + /// Constructs the Host URL based on the selected , used to populate the `HttpClient.BaseAddress`. + /// + /// . + /// The base URL of the API. + /// String containing the Host URL. + /// + public static string ConstructHostUrl(AdyenOptions adyenOptions, string baseUrl) + { + if (adyenOptions.Environment == AdyenEnvironment.Live) + return ConstructLiveUrl(adyenOptions.LiveEndpointUrlPrefix, baseUrl); + + // Some Adyen OpenApi Specifications use the live-url, instead of the test-url, this line replaces "-live" with "-test". + // If you need to override this URL, you can do so by replacing the BASE_URL in ClientUtils.cs. + if (adyenOptions.Environment == AdyenEnvironment.Test) + return baseUrl.Replace("-live", "-test"); + + throw new ArgumentOutOfRangeException(adyenOptions.Environment.ToString()); + } + + /// + /// Construct LIVE BaseUrl, add the liveEndpointUrlPrefix it's the Checkout API or the Classic Payment API. + /// This helper function can be removed when all URLs (test & live) are included in the Adyen OpenApi Specifications: https://github.com/Adyen/adyen-openapi. + /// + /// The Live endpoint url prefix. + /// The base URL of the API. + /// String containing the LIVE URL. + /// + public static string ConstructLiveUrl(string liveEndpointUrlPrefix, string url) + { + // Change base url for Live environment + if (url.Contains("pal-")) // Payment API prefix + { + if (liveEndpointUrlPrefix == null) + { + throw new InvalidOperationException("LiveEndpointUrlPrefix is null - please configure your AdyenOptions.LiveEndpointUrlPrefix"); + } + + url = url.Replace("https://pal-test.adyen.com/pal/servlet/", + "https://" + liveEndpointUrlPrefix + "-pal-live.adyenpayments.com/pal/servlet/"); + } + else if (url.Contains("checkout-")) // Checkout API prefix + { + if (liveEndpointUrlPrefix == null) + { + throw new InvalidOperationException("LiveEndpointUrlPrefix is null - please configure your AdyenOptions.LiveEndpointUrlPrefix"); + } + + if (url.Contains("possdk")) + { + url = url.Replace("https://checkout-test.adyen.com/", + "https://" + liveEndpointUrlPrefix + "-checkout-live.adyenpayments.com/"); + } + else + { + url = url.Replace("https://checkout-test.adyen.com/", + "https://" + liveEndpointUrlPrefix + "-checkout-live.adyenpayments.com/checkout/"); + } + } + else if (url.Contains("https://test.adyen.com/authe/api/")) // SessionAuthentication + { + url = url.Replace("https://test.adyen.com/authe/api/", + "https://authe-live.adyen.com/authe/api/"); + } + + // If no prefix is required, we replace "test" -> "live" + url = url.Replace("-test", "-live"); + return url; + } + } +} \ No newline at end of file diff --git a/Adyen/Core/Converters/ByteArrayConverter.cs b/Adyen/Core/Converters/ByteArrayConverter.cs new file mode 100644 index 000000000..e222288a8 --- /dev/null +++ b/Adyen/Core/Converters/ByteArrayConverter.cs @@ -0,0 +1,29 @@ +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Adyen.Core.Converters +{ + public class ByteArrayConverter : JsonConverter + { + public override byte[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + return null; + + string value = reader.GetString(); + return Encoding.UTF8.GetBytes(value); + } + + public override void Write(Utf8JsonWriter writer, byte[] value, JsonSerializerOptions options) + { + if (value == null) + { + writer.WriteNullValue(); + return; + } + + writer.WriteStringValue(Encoding.UTF8.GetString(value)); + } + } +} \ No newline at end of file diff --git a/Adyen/Core/Converters/DateOnlyJsonConverter.cs b/Adyen/Core/Converters/DateOnlyJsonConverter.cs new file mode 100644 index 000000000..80c34bd8d --- /dev/null +++ b/Adyen/Core/Converters/DateOnlyJsonConverter.cs @@ -0,0 +1,52 @@ +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Adyen.Core.Converters +{ + /// + /// Formatter for 'date' openapi formats ss defined by full-date - RFC3339 + /// see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#data-types + /// + public class DateOnlyJsonConverter : JsonConverter + { + /// + /// The formats used to deserialize the date + /// + public static string[] Formats { get; } = { + "yyyy'-'MM'-'dd", + "yyyyMMdd" + + }; + + /// + /// Returns a DateOnly from the Json object + /// + /// + /// + /// + /// + public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + if (reader.TokenType == JsonTokenType.Null) + throw new NotSupportedException(); + + string value = reader.GetString()!; + + foreach(string format in Formats) + if (DateOnly.TryParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateOnly result)) + return result; + + throw new NotSupportedException(); + } + + /// + /// Writes the DateOnly to the json writer + /// + /// + /// + /// + public override void Write(Utf8JsonWriter writer, DateOnly dateOnlyValue, JsonSerializerOptions options) => + writer.WriteStringValue(dateOnlyValue.ToString("yyyy'-'MM'-'dd", CultureInfo.InvariantCulture)); + } +} diff --git a/Adyen/Core/Converters/DateOnlyNullableJsonConverter.cs b/Adyen/Core/Converters/DateOnlyNullableJsonConverter.cs new file mode 100644 index 000000000..c0e1f236a --- /dev/null +++ b/Adyen/Core/Converters/DateOnlyNullableJsonConverter.cs @@ -0,0 +1,57 @@ +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Adyen.Core.Converters +{ + /// + /// Formatter for 'date' openapi formats ss defined by full-date - RFC3339 + /// see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#data-types + /// + public class DateOnlyNullableJsonConverter : JsonConverter + { + /// + /// The formats used to deserialize the date + /// + public static string[] Formats { get; } = { + "yyyy'-'MM'-'dd", + "yyyyMMdd" + + }; + + /// + /// Returns a DateOnly from the Json object + /// + /// + /// + /// + /// + public override DateOnly? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + if (reader.TokenType == JsonTokenType.Null) + return null; + + string value = reader.GetString()!; + + foreach(string format in Formats) + if (DateOnly.TryParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateOnly result)) + return result; + + throw new NotSupportedException(); + } + + /// + /// Writes the DateOnly to the json writer + /// + /// + /// + /// + public override void Write(Utf8JsonWriter writer, DateOnly? dateOnlyValue, JsonSerializerOptions options) + { + if (dateOnlyValue == null) + writer.WriteNullValue(); + else + writer.WriteStringValue(dateOnlyValue.Value.ToString("yyyy'-'MM'-'dd", CultureInfo.InvariantCulture)); + } + } +} diff --git a/Adyen/Core/Converters/DateTimeJsonConverter.cs b/Adyen/Core/Converters/DateTimeJsonConverter.cs new file mode 100644 index 000000000..cbf1f4dde --- /dev/null +++ b/Adyen/Core/Converters/DateTimeJsonConverter.cs @@ -0,0 +1,66 @@ +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Adyen.Core.Converters +{ + /// + /// Formatter for 'date-time' openapi formats ss defined by full-date - RFC3339 + /// see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#data-types + /// + public class DateTimeJsonConverter : JsonConverter + { + /// + /// The formats used to deserialize the date + /// + public static string[] Formats { get; } = { + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffffffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ssK", + "yyyyMMddTHHmmss.fffffffK", + "yyyyMMddTHHmmss.ffffffK", + "yyyyMMddTHHmmss.fffffK", + "yyyyMMddTHHmmss.ffffK", + "yyyyMMddTHHmmss.fffK", + "yyyyMMddTHHmmss.ffK", + "yyyyMMddTHHmmss.fK", + "yyyyMMddTHHmmssK", + + }; + + /// + /// Returns a DateTime from the Json object + /// + /// + /// + /// + /// + public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + if (reader.TokenType == JsonTokenType.Null) + throw new NotSupportedException(); + + string value = reader.GetString()!; + + foreach(string format in Formats) + if (DateTime.TryParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out DateTime result)) + return result; + + throw new NotSupportedException(); + } + + /// + /// Writes the DateTime to the json writer + /// + /// + /// + /// + public override void Write(Utf8JsonWriter writer, DateTime dateTimeValue, JsonSerializerOptions options) => + writer.WriteStringValue(dateTimeValue.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK", CultureInfo.InvariantCulture)); + } +} diff --git a/Adyen/Core/Converters/DateTimeNullableJsonConverter.cs b/Adyen/Core/Converters/DateTimeNullableJsonConverter.cs new file mode 100644 index 000000000..f828a2ce0 --- /dev/null +++ b/Adyen/Core/Converters/DateTimeNullableJsonConverter.cs @@ -0,0 +1,71 @@ +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Adyen.Core.Converters +{ + /// + /// Formatter for 'date-time' openapi formats ss defined by full-date - RFC3339 + /// see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#data-types + /// + public class DateTimeNullableJsonConverter : JsonConverter + { + /// + /// The formats used to deserialize the date + /// + public static string[] Formats { get; } = { + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffffffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ssK", + "yyyyMMddTHHmmss.fffffffK", + "yyyyMMddTHHmmss.ffffffK", + "yyyyMMddTHHmmss.fffffK", + "yyyyMMddTHHmmss.ffffK", + "yyyyMMddTHHmmss.fffK", + "yyyyMMddTHHmmss.ffK", + "yyyyMMddTHHmmss.fK", + "yyyyMMddTHHmmssK", + + }; + + /// + /// Returns a DateTime from the Json object + /// + /// + /// + /// + /// + public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + if (reader.TokenType == JsonTokenType.Null) + return null; + + string value = reader.GetString()!; + + foreach(string format in Formats) + if (DateTime.TryParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out DateTime result)) + return result; + + return null; + } + + /// + /// Writes the DateTime to the json writer + /// + /// + /// + /// + public override void Write(Utf8JsonWriter writer, DateTime? dateTimeValue, JsonSerializerOptions options) + { + if (dateTimeValue == null) + writer.WriteNullValue(); + else + writer.WriteStringValue(dateTimeValue.Value.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK", CultureInfo.InvariantCulture)); + } + } +} diff --git a/Adyen/Core/IEnum.cs b/Adyen/Core/IEnum.cs new file mode 100644 index 000000000..11055cb53 --- /dev/null +++ b/Adyen/Core/IEnum.cs @@ -0,0 +1,11 @@ +namespace Adyen.Core +{ + /// + /// Interface for defining enums. + /// This interface is used to make enums forward-compatible. + /// + public interface IEnum + { + string? Value { get; set; } + } +} \ No newline at end of file diff --git a/Adyen/Core/Option.cs b/Adyen/Core/Option.cs new file mode 100644 index 000000000..407cd4532 --- /dev/null +++ b/Adyen/Core/Option.cs @@ -0,0 +1,48 @@ +#nullable enable + + +namespace Adyen.Core +{ + /// + /// A wrapper for operation parameters which are not required. + /// + public struct Option + { + /// + /// The value to send to the server. + /// + public TType Value { get; } + + /// + /// When true the value will be sent to the server. + /// + internal bool IsSet { get; } + + /// + /// A wrapper for operation parameters which are not required. + /// + /// + public Option(TType value) + { + IsSet = true; + Value = value; + } + + /// + /// Implicitly converts this option to the contained type. + /// + /// + /// + /// . + public static implicit operator TType(Option option) => option.Value; + + /// + /// Implicitly converts the provided value to an Option. + /// + /// . + /// + /// Option of . + public static implicit operator Option(TType value) => new Option(value); + + } +} \ No newline at end of file diff --git a/Adyen/Core/Options/AdyenEnvironment.cs b/Adyen/Core/Options/AdyenEnvironment.cs new file mode 100644 index 000000000..6d98e4fe0 --- /dev/null +++ b/Adyen/Core/Options/AdyenEnvironment.cs @@ -0,0 +1,12 @@ +namespace Adyen.Core.Options +{ + /// + /// The Adyen Environment. + /// Changing this value affects the ClientUtils.BASE_URL where your API requests are sent. + /// + public enum AdyenEnvironment + { + Test, + Live + } +} \ No newline at end of file diff --git a/Adyen/Core/Options/AdyenOptions.cs b/Adyen/Core/Options/AdyenOptions.cs new file mode 100644 index 000000000..3da6480b9 --- /dev/null +++ b/Adyen/Core/Options/AdyenOptions.cs @@ -0,0 +1,30 @@ +namespace Adyen.Core.Options +{ + public class AdyenOptions + { + /// + /// The Adyen Environment. + /// + public AdyenEnvironment Environment { get; set; } = AdyenEnvironment.Test; + + /// + /// Used in the LIVE environment only. + /// This prefix is appended to HttpClient.BaseAddress when is set to `AdyenEnvironment.Live` + /// See: https://docs.adyen.com/development-resources/live-endpoints/ + /// + public string LiveEndpointUrlPrefix { get; set; } + + /// + /// The `ADYEN_API_KEY` is an Adyen API authentication token that must be included in the HTTP request header and allows your application to securely communicate with the Adyen APIs. + /// Guide on how to obtain the `ADYEN_API_KEY` + /// 1. For Digital/ECOM & In-Person Payments, visit: https://docs.adyen.com/development-resources/api-credentials/#generate-api-key to get your API Key. + /// 2. For Platforms & Financial Services, visit: https://docs.adyen.com/adyen-for-platforms-model to get started. + /// + public string AdyenApiKey { get; set; } + + /// + /// If this is set to true, we log every + /// + public bool IsLogInformationServices { get; set; } = false; + } +} \ No newline at end of file diff --git a/Adyen/Model/AcsWebhooks/AuthenticationNotificationData.cs b/Adyen/Model/AcsWebhooks/AuthenticationNotificationData.cs index 82418b422..dbb25c08a 100644 --- a/Adyen/Model/AcsWebhooks/AuthenticationNotificationData.cs +++ b/Adyen/Model/AcsWebhooks/AuthenticationNotificationData.cs @@ -76,8 +76,8 @@ protected AuthenticationNotificationData() { } /// /// authentication (required). /// The unique identifier of the balance platform.. - /// Unique identifier of the authentication. (required). - /// Unique identifier of the payment instrument that was used for the authentication. (required). + /// The unique identifier of the authentication. (required). + /// The unique identifier of the payment instrument that was used for the authentication. (required). /// purchase (required). /// Outcome of the authentication. Allowed values: * authenticated * rejected * error (required). public AuthenticationNotificationData(AuthenticationInfo authentication = default(AuthenticationInfo), string balancePlatform = default(string), string id = default(string), string paymentInstrumentId = default(string), PurchaseInfo purchase = default(PurchaseInfo), StatusEnum status = default(StatusEnum)) @@ -104,16 +104,16 @@ protected AuthenticationNotificationData() { } public string BalancePlatform { get; set; } /// - /// Unique identifier of the authentication. + /// The unique identifier of the authentication. /// - /// Unique identifier of the authentication. + /// The unique identifier of the authentication. [DataMember(Name = "id", IsRequired = false, EmitDefaultValue = false)] public string Id { get; set; } /// - /// Unique identifier of the payment instrument that was used for the authentication. + /// The unique identifier of the payment instrument that was used for the authentication. /// - /// Unique identifier of the payment instrument that was used for the authentication. + /// The unique identifier of the payment instrument that was used for the authentication. [DataMember(Name = "paymentInstrumentId", IsRequired = false, EmitDefaultValue = false)] public string PaymentInstrumentId { get; set; } diff --git a/Adyen/Model/AcsWebhooks/BalancePlatformNotificationResponse.cs b/Adyen/Model/AcsWebhooks/BalancePlatformNotificationResponse.cs index ecbae9104..1d24000b9 100644 --- a/Adyen/Model/AcsWebhooks/BalancePlatformNotificationResponse.cs +++ b/Adyen/Model/AcsWebhooks/BalancePlatformNotificationResponse.cs @@ -35,16 +35,16 @@ public partial class BalancePlatformNotificationResponse : IEquatable /// Initializes a new instance of the class. /// - /// Respond with any **2xx** HTTP status code to [accept the webhook](https://docs.adyen.com/development-resources/webhooks#accept-notifications).. + /// Respond with any **2xx** HTTP status code to [accept the webhook](https://docs.adyen.com/development-resources/webhooks/#accept-webhooks).. public BalancePlatformNotificationResponse(string notificationResponse = default(string)) { this.NotificationResponse = notificationResponse; } /// - /// Respond with any **2xx** HTTP status code to [accept the webhook](https://docs.adyen.com/development-resources/webhooks#accept-notifications). + /// Respond with any **2xx** HTTP status code to [accept the webhook](https://docs.adyen.com/development-resources/webhooks/#accept-webhooks). /// - /// Respond with any **2xx** HTTP status code to [accept the webhook](https://docs.adyen.com/development-resources/webhooks#accept-notifications). + /// Respond with any **2xx** HTTP status code to [accept the webhook](https://docs.adyen.com/development-resources/webhooks/#accept-webhooks). [DataMember(Name = "notificationResponse", EmitDefaultValue = false)] public string NotificationResponse { get; set; } diff --git a/Adyen/Model/AcsWebhooks/PurchaseInfo.cs b/Adyen/Model/AcsWebhooks/PurchaseInfo.cs index 511b1a6ab..29200bd8f 100644 --- a/Adyen/Model/AcsWebhooks/PurchaseInfo.cs +++ b/Adyen/Model/AcsWebhooks/PurchaseInfo.cs @@ -40,8 +40,8 @@ protected PurchaseInfo() { } /// /// Initializes a new instance of the class. /// - /// Date of the purchase. (required). - /// Name of the merchant. (required). + /// The date of the purchase. (required). + /// The name of the business that the cardholder purchased from. (required). /// originalAmount (required). public PurchaseInfo(string date = default(string), string merchantName = default(string), Amount originalAmount = default(Amount)) { @@ -51,16 +51,16 @@ protected PurchaseInfo() { } } /// - /// Date of the purchase. + /// The date of the purchase. /// - /// Date of the purchase. + /// The date of the purchase. [DataMember(Name = "date", IsRequired = false, EmitDefaultValue = false)] public string Date { get; set; } /// - /// Name of the merchant. + /// The name of the business that the cardholder purchased from. /// - /// Name of the merchant. + /// The name of the business that the cardholder purchased from. [DataMember(Name = "merchantName", IsRequired = false, EmitDefaultValue = false)] public string MerchantName { get; set; } diff --git a/Adyen/Model/AcsWebhooks/RelayedAuthenticationRequest.cs b/Adyen/Model/AcsWebhooks/RelayedAuthenticationRequest.cs index 0dd6d2895..8c190ddc1 100644 --- a/Adyen/Model/AcsWebhooks/RelayedAuthenticationRequest.cs +++ b/Adyen/Model/AcsWebhooks/RelayedAuthenticationRequest.cs @@ -32,6 +32,28 @@ namespace Adyen.Model.AcsWebhooks [DataContract(Name = "RelayedAuthenticationRequest")] public partial class RelayedAuthenticationRequest : IEquatable, IValidatableObject { + /// + /// Type of notification. + /// + /// Type of notification. + [JsonConverter(typeof(StringEnumConverter))] + public enum TypeEnum + { + /// + /// Enum BalancePlatformAuthenticationRelayed for value: balancePlatform.authentication.relayed + /// + [EnumMember(Value = "balancePlatform.authentication.relayed")] + BalancePlatformAuthenticationRelayed = 1 + + } + + + /// + /// Type of notification. + /// + /// Type of notification. + [DataMember(Name = "type", IsRequired = false, EmitDefaultValue = false)] + public TypeEnum Type { get; set; } /// /// Initializes a new instance of the class. /// @@ -40,16 +62,31 @@ protected RelayedAuthenticationRequest() { } /// /// Initializes a new instance of the class. /// + /// The environment from which the webhook originated. Possible values: **test**, **live**. (required). /// The unique identifier of the challenge. (required). /// The unique identifier of the [payment instrument](https://docs.adyen.com/api-explorer/balanceplatform/latest/get/paymentInstruments/_id_) used for the purchase. (required). /// purchase (required). - public RelayedAuthenticationRequest(string id = default(string), string paymentInstrumentId = default(string), Purchase purchase = default(Purchase)) + /// URL for auto-switching to the threeDS Requestor App. If not present, the threeDS Requestor App doesn't support auto-switching.. + /// When the event was queued.. + /// Type of notification. (required). + public RelayedAuthenticationRequest(string environment = default(string), string id = default(string), string paymentInstrumentId = default(string), Purchase purchase = default(Purchase), string threeDSRequestorAppURL = default(string), DateTime timestamp = default(DateTime), TypeEnum type = default(TypeEnum)) { + this.Environment = environment; this.Id = id; this.PaymentInstrumentId = paymentInstrumentId; this.Purchase = purchase; + this.Type = type; + this.ThreeDSRequestorAppURL = threeDSRequestorAppURL; + this.Timestamp = timestamp; } + /// + /// The environment from which the webhook originated. Possible values: **test**, **live**. + /// + /// The environment from which the webhook originated. Possible values: **test**, **live**. + [DataMember(Name = "environment", IsRequired = false, EmitDefaultValue = false)] + public string Environment { get; set; } + /// /// The unique identifier of the challenge. /// @@ -70,6 +107,20 @@ protected RelayedAuthenticationRequest() { } [DataMember(Name = "purchase", IsRequired = false, EmitDefaultValue = false)] public Purchase Purchase { get; set; } + /// + /// URL for auto-switching to the threeDS Requestor App. If not present, the threeDS Requestor App doesn't support auto-switching. + /// + /// URL for auto-switching to the threeDS Requestor App. If not present, the threeDS Requestor App doesn't support auto-switching. + [DataMember(Name = "threeDSRequestorAppURL", EmitDefaultValue = false)] + public string ThreeDSRequestorAppURL { get; set; } + + /// + /// When the event was queued. + /// + /// When the event was queued. + [DataMember(Name = "timestamp", EmitDefaultValue = false)] + public DateTime Timestamp { get; set; } + /// /// Returns the string presentation of the object /// @@ -78,9 +129,13 @@ public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append("class RelayedAuthenticationRequest {\n"); + sb.Append(" Environment: ").Append(Environment).Append("\n"); sb.Append(" Id: ").Append(Id).Append("\n"); sb.Append(" PaymentInstrumentId: ").Append(PaymentInstrumentId).Append("\n"); sb.Append(" Purchase: ").Append(Purchase).Append("\n"); + sb.Append(" ThreeDSRequestorAppURL: ").Append(ThreeDSRequestorAppURL).Append("\n"); + sb.Append(" Timestamp: ").Append(Timestamp).Append("\n"); + sb.Append(" Type: ").Append(Type).Append("\n"); sb.Append("}\n"); return sb.ToString(); } @@ -116,6 +171,11 @@ public bool Equals(RelayedAuthenticationRequest input) return false; } return + ( + this.Environment == input.Environment || + (this.Environment != null && + this.Environment.Equals(input.Environment)) + ) && ( this.Id == input.Id || (this.Id != null && @@ -130,6 +190,20 @@ public bool Equals(RelayedAuthenticationRequest input) this.Purchase == input.Purchase || (this.Purchase != null && this.Purchase.Equals(input.Purchase)) + ) && + ( + this.ThreeDSRequestorAppURL == input.ThreeDSRequestorAppURL || + (this.ThreeDSRequestorAppURL != null && + this.ThreeDSRequestorAppURL.Equals(input.ThreeDSRequestorAppURL)) + ) && + ( + this.Timestamp == input.Timestamp || + (this.Timestamp != null && + this.Timestamp.Equals(input.Timestamp)) + ) && + ( + this.Type == input.Type || + this.Type.Equals(input.Type) ); } @@ -142,6 +216,10 @@ public override int GetHashCode() unchecked // Overflow is fine, just wrap { int hashCode = 41; + if (this.Environment != null) + { + hashCode = (hashCode * 59) + this.Environment.GetHashCode(); + } if (this.Id != null) { hashCode = (hashCode * 59) + this.Id.GetHashCode(); @@ -154,6 +232,15 @@ public override int GetHashCode() { hashCode = (hashCode * 59) + this.Purchase.GetHashCode(); } + if (this.ThreeDSRequestorAppURL != null) + { + hashCode = (hashCode * 59) + this.ThreeDSRequestorAppURL.GetHashCode(); + } + if (this.Timestamp != null) + { + hashCode = (hashCode * 59) + this.Timestamp.GetHashCode(); + } + hashCode = (hashCode * 59) + this.Type.GetHashCode(); return hashCode; } } diff --git a/Adyen/Model/AcsWebhooks/Resource.cs b/Adyen/Model/AcsWebhooks/Resource.cs index 9dcd4a3d0..b77e9d028 100644 --- a/Adyen/Model/AcsWebhooks/Resource.cs +++ b/Adyen/Model/AcsWebhooks/Resource.cs @@ -36,7 +36,7 @@ public partial class Resource : IEquatable, IValidatableObject /// Initializes a new instance of the class. /// /// The unique identifier of the balance platform.. - /// The date and time when the event was triggered, in ISO 8601 extended format. For example, **2020-12-18T10:15:30+01:00**.. + /// The date and time when the event was triggered, in ISO 8601 extended format. For example, **2025-03-19T10:15:30+01:00**.. /// The ID of the resource.. public Resource(string balancePlatform = default(string), DateTime creationDate = default(DateTime), string id = default(string)) { @@ -53,9 +53,9 @@ public partial class Resource : IEquatable, IValidatableObject public string BalancePlatform { get; set; } /// - /// The date and time when the event was triggered, in ISO 8601 extended format. For example, **2020-12-18T10:15:30+01:00**. + /// The date and time when the event was triggered, in ISO 8601 extended format. For example, **2025-03-19T10:15:30+01:00**. /// - /// The date and time when the event was triggered, in ISO 8601 extended format. For example, **2020-12-18T10:15:30+01:00**. + /// The date and time when the event was triggered, in ISO 8601 extended format. For example, **2025-03-19T10:15:30+01:00**. [DataMember(Name = "creationDate", EmitDefaultValue = false)] public DateTime CreationDate { get; set; } diff --git a/Adyen/Model/ApiError.cs b/Adyen/Model/ApiError.cs deleted file mode 100644 index 6a544f85e..000000000 --- a/Adyen/Model/ApiError.cs +++ /dev/null @@ -1,141 +0,0 @@ -#region License -// /* -// * ###### -// * ###### -// * ############ ####( ###### #####. ###### ############ ############ -// * ############# #####( ###### #####. ###### ############# ############# -// * ###### #####( ###### #####. ###### ##### ###### ##### ###### -// * ###### ###### #####( ###### #####. ###### ##### ##### ##### ###### -// * ###### ###### #####( ###### #####. ###### ##### ##### ###### -// * ############# ############# ############# ############# ##### ###### -// * ############ ############ ############# ############ ##### ###### -// * ###### -// * ############# -// * ############ -// * -// * Adyen Dotnet API Library -// * -// * Copyright (c) 2020 Adyen B.V. -// * This file is open source and available under the MIT license. -// * See the LICENSE file for more info. -// */ -#endregion - -using Adyen.Model.Recurring; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Text; - -namespace Adyen.Model -{ - public class ApiError : IEquatable, IValidatableObject - { - public int Status { get; set; } - public string ErrorCode { get; set; } - public string Message { get; set; } - public string ErrorType { get; set; } - public string PspReference { get; set; } - - - /// - /// Returns the string presentation of the object - /// - /// String presentation of the object - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append("class ApiError {\n"); - sb.Append(" ErrorCode: ").Append(ErrorCode).Append("\n"); - sb.Append(" Message: ").Append(Message).Append("\n"); - sb.Append(" ErrorType: ").Append(ErrorType).Append("\n"); - sb.Append(" PspReference: ").Append(PspReference).Append("\n"); - sb.Append("}\n"); - return sb.ToString(); - } - - /// - /// Returns true if objects are equal - /// - /// Object to be compared - /// Boolean - public override bool Equals(object obj) - { - // credit: http://stackoverflow.com/a/10454552/677735 - return this.Equals(obj as RecurringDetailsResult); - } - - /// - /// Returns true if RecurringDetailsResult instances are equal - /// - /// Instance of RecurringDetailsResult to be compared - /// Boolean - public bool Equals(ApiError other) - { - // credit: http://stackoverflow.com/a/10454552/677735 - if (other == null) - return false; - - return - ( - this.Status == other.Status || - - this.Status.Equals(other.Status) - ) && - ( - this.ErrorCode == other.ErrorCode || - this.ErrorCode != null && - this.ErrorCode.Equals(other.ErrorCode) - ) && - ( - this.Message == other.Message || - this.Message != null && - this.Message.Equals(other.Message) - ) && - ( - this.ErrorType == other.ErrorType || - this.ErrorType != null && - this.ErrorType.Equals(other.ErrorType) - ) && - ( - this.PspReference == other.PspReference || - this.PspReference != null && - this.PspReference.Equals(other.PspReference) - ); - } - - /// - /// Gets the hash code - /// - /// Hash code - public override int GetHashCode() - { - // credit: http://stackoverflow.com/a/263416/677735 - unchecked // Overflow is fine, just wrap - { - int hash = 41; - // Suitable nullity checks etc, of course :) - hash = hash * 59 + this.Status.GetHashCode(); - if (this.ErrorCode != null) - hash = hash * 59 + this.ErrorCode.GetHashCode(); - if (this.Message != null) - hash = hash * 59 + this.Message.GetHashCode(); - if (this.ErrorType != null) - hash = hash * 59 + this.ErrorType.GetHashCode(); - if (this.PspReference != null) - hash = hash * 59 + this.PspReference.GetHashCode(); - return hash; - } - } - - /// - /// To validate all properties of the instance - /// - /// Validation context - /// Validation Result - IEnumerable IValidatableObject.Validate(ValidationContext validationContext) - { - yield break; - } - } -} diff --git a/Adyen/Model/BalancePlatform/VerificationError.cs b/Adyen/Model/BalancePlatform/VerificationError.cs index 64777db77..ab623fc06 100644 --- a/Adyen/Model/BalancePlatform/VerificationError.cs +++ b/Adyen/Model/BalancePlatform/VerificationError.cs @@ -401,15 +401,20 @@ public enum TypeEnum /// Enum PendingStatus for value: pendingStatus /// [EnumMember(Value = "pendingStatus")] - PendingStatus = 3 - + PendingStatus = 3, + + /// + /// Enum DataReview for value: dataReview + /// + [EnumMember(Value = "dataReview")] + DataReview = 4 } /// - /// The type of error. Possible values: **invalidInput**, **dataMissing**. + /// The type of error. Possible values: * **invalidInput** * **dataMissing** * **pendingStatus** * **dataReview** /// - /// The type of error. Possible values: **invalidInput**, **dataMissing**. + /// The type of error. Possible values: * **invalidInput** * **dataMissing** * **pendingStatus** * **dataReview** [DataMember(Name = "type", EmitDefaultValue = false)] public TypeEnum? Type { get; set; } /// @@ -420,7 +425,7 @@ public enum TypeEnum /// A description of the error.. /// Contains the actions that you can take to resolve the verification error.. /// Contains more granular information about the verification error.. - /// The type of error. Possible values: **invalidInput**, **dataMissing**.. + /// The type of error. Possible values: **invalidInput**, **dataMissing**, **pendingStatus**, **dataReview** . public VerificationError(List capabilities = default(List), string code = default(string), string message = default(string), List remediatingActions = default(List), List subErrors = default(List), TypeEnum? type = default(TypeEnum?)) { this.Capabilities = capabilities; diff --git a/Adyen/Model/BalancePlatform/VerificationErrorRecursive.cs b/Adyen/Model/BalancePlatform/VerificationErrorRecursive.cs index eb3720572..9bf8551f9 100644 --- a/Adyen/Model/BalancePlatform/VerificationErrorRecursive.cs +++ b/Adyen/Model/BalancePlatform/VerificationErrorRecursive.cs @@ -379,9 +379,9 @@ public enum CapabilitiesEnum [DataMember(Name = "capabilities", EmitDefaultValue = false)] public List Capabilities { get; set; } /// - /// The type of error. Possible values: **invalidInput**, **dataMissing**. + /// The type of error. Possible values: * **invalidInput** * **dataMissing** * **pendingStatus** * **dataReview** /// - /// The type of error. Possible values: **invalidInput**, **dataMissing**. + /// The type of error. Possible values: * **invalidInput** * **dataMissing** * **pendingStatus** * **dataReview** [JsonConverter(typeof(StringEnumConverter))] public enum TypeEnum { @@ -401,15 +401,21 @@ public enum TypeEnum /// Enum PendingStatus for value: pendingStatus /// [EnumMember(Value = "pendingStatus")] - PendingStatus = 3 + PendingStatus = 3, + + /// + /// Enum DataReview for value: dataReview + /// + [EnumMember(Value = "dataReview")] + DataReview = 4 } /// - /// The type of error. Possible values: **invalidInput**, **dataMissing**. + /// The type of error. Possible values: * **invalidInput** * **dataMissing** * **pendingStatus** * **dataReview** /// - /// The type of error. Possible values: **invalidInput**, **dataMissing**. + /// The type of error. Possible values: * **invalidInput** * **dataMissing** * **pendingStatus** * **dataReview** [DataMember(Name = "type", EmitDefaultValue = false)] public TypeEnum? Type { get; set; } /// diff --git a/Adyen/Model/Checkout/AdditionalDataSubMerchant.cs b/Adyen/Model/Checkout/AdditionalDataSubMerchant.cs index 883cfbb5f..bfbcea0a1 100644 --- a/Adyen/Model/Checkout/AdditionalDataSubMerchant.cs +++ b/Adyen/Model/Checkout/AdditionalDataSubMerchant.cs @@ -41,7 +41,7 @@ public partial class AdditionalDataSubMerchant : IEquatableRequired for transactions performed by registered payment facilitators. The email address of the sub-merchant. * Format: Alphanumeric * Maximum length: 40 characters. /// Required for transactions performed by registered payment facilitators. A unique identifier that you create for the sub-merchant, used by schemes to identify the sub-merchant. * Format: Alphanumeric * Maximum length: 15 characters. /// Required for transactions performed by registered payment facilitators. The sub-merchant's 4-digit Merchant Category Code (MCC). * Format: Numeric * Fixed length: 4 digits. - /// Required for transactions performed by registered payment facilitators. The name of the sub-merchant. Based on scheme specifications, this value will overwrite the shopper statement that will appear in the card statement. * Format: Alphanumeric * Maximum length: 22 characters. + /// Required for transactions performed by registered payment facilitators. The name of the sub-merchant. Based on scheme specifications, this value will overwrite the shopper statement that will appear in the card statement. Exception: for acquirers in Brazil, this value does not overwrite the shopper statement. * Format: Alphanumeric * Maximum length: 22 characters. /// Required for transactions performed by registered payment facilitators. The phone number of the sub-merchant.* Format: Alphanumeric * Maximum length: 20 characters. /// Required for transactions performed by registered payment facilitators. The postal code of the sub-merchant's address, without dashes. * Format: Numeric * Fixed length: 8 digits. /// Required for transactions performed by registered payment facilitators. The state code of the sub-merchant's address, if applicable to the country. * Format: Alphanumeric * Maximum length: 2 characters. @@ -106,9 +106,9 @@ public partial class AdditionalDataSubMerchant : IEquatable - /// Required for transactions performed by registered payment facilitators. The name of the sub-merchant. Based on scheme specifications, this value will overwrite the shopper statement that will appear in the card statement. * Format: Alphanumeric * Maximum length: 22 characters + /// Required for transactions performed by registered payment facilitators. The name of the sub-merchant. Based on scheme specifications, this value will overwrite the shopper statement that will appear in the card statement. Exception: for acquirers in Brazil, this value does not overwrite the shopper statement. * Format: Alphanumeric * Maximum length: 22 characters /// - /// Required for transactions performed by registered payment facilitators. The name of the sub-merchant. Based on scheme specifications, this value will overwrite the shopper statement that will appear in the card statement. * Format: Alphanumeric * Maximum length: 22 characters + /// Required for transactions performed by registered payment facilitators. The name of the sub-merchant. Based on scheme specifications, this value will overwrite the shopper statement that will appear in the card statement. Exception: for acquirers in Brazil, this value does not overwrite the shopper statement. * Format: Alphanumeric * Maximum length: 22 characters [DataMember(Name = "subMerchant.subSeller[subSellerNr].name", EmitDefaultValue = false)] public string SubMerchantSubSellerSubSellerNrName { get; set; } diff --git a/Adyen/Model/Checkout/AuthenticationData.cs b/Adyen/Model/Checkout/AuthenticationData.cs index 991c56b3c..974b35995 100644 --- a/Adyen/Model/Checkout/AuthenticationData.cs +++ b/Adyen/Model/Checkout/AuthenticationData.cs @@ -64,7 +64,7 @@ public enum AttemptAuthenticationEnum /// Initializes a new instance of the class. /// /// Indicates when 3D Secure authentication should be attempted. This overrides all other rules, including [Dynamic 3D Secure settings](https://docs.adyen.com/risk-management/dynamic-3d-secure). Possible values: * **always**: Perform 3D Secure authentication. * **never**: Don't perform 3D Secure authentication. If PSD2 SCA or other national regulations require authentication, the transaction gets declined.. - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. Default: **false**. (default to false). + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorization. Default: **false**. (default to false). /// threeDSRequestData. public AuthenticationData(AttemptAuthenticationEnum? attemptAuthentication = default(AttemptAuthenticationEnum?), bool? authenticationOnly = false, ThreeDSRequestData threeDSRequestData = default(ThreeDSRequestData)) { @@ -74,9 +74,9 @@ public enum AttemptAuthenticationEnum } /// - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. Default: **false**. + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorization. Default: **false**. /// - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. Default: **false**. + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorization. Default: **false**. [DataMember(Name = "authenticationOnly", EmitDefaultValue = false)] public bool? AuthenticationOnly { get; set; } diff --git a/Adyen/Model/Checkout/BalanceCheckRequest.cs b/Adyen/Model/Checkout/BalanceCheckRequest.cs index e15c595f7..50f294353 100644 --- a/Adyen/Model/Checkout/BalanceCheckRequest.cs +++ b/Adyen/Model/Checkout/BalanceCheckRequest.cs @@ -143,8 +143,8 @@ protected BalanceCheckRequest() { } /// Some payment methods require defining a value for this field to specify how to process the transaction. For the Bancontact payment method, it can be set to: * `maestro` (default), to be processed like a Maestro card, or * `bcmc`, to be processed like a Bancontact card.. /// The `recurringDetailReference` you want to use for this payment. The value `LATEST` can be used to select the most recently stored recurring detail.. /// A session ID used to identify a payment session.. - /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > For 3D Secure 2 transactions, schemes require `shopperEmail` for all browser-based and mobile implementations.. - /// The shopper's IP address. In general, we recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks). > For 3D Secure 2 transactions, schemes require `shopperIP` for all browser-based implementations. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new).. + /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > Required for Visa and JCB transactions that require 3D Secure 2 authentication if you did not include the `telephoneNumber`.. + /// The shopper's IP address. We recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks).> Required for Visa and JCB transactions that require 3D Secure 2 authentication for all web and mobile integrations, if you did not include the `shopperEmail`. For native mobile integrations, the field is required to support cases where authentication is routed to the redirect flow. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new).. /// Specifies the sales channel, through which the shopper gives their card details, and whether the shopper is a returning customer. For the web service API, Adyen assumes Ecommerce shopper interaction by default. This field has the following possible values: * `Ecommerce` - Online transactions where the cardholder is present (online). For better authorisation rates, we recommend sending the card security code (CSC) along with the request. * `ContAuth` - Card on file and/or subscription transactions, where the cardholder is known to the merchant (returning customer). If the shopper is present (online), you can supply also the CSC to improve authorisation (one-click payment). * `Moto` - Mail-order and telephone-order transactions where the shopper is in contact with the merchant via email or telephone. * `POS` - Point-of-sale transactions where the shopper is physically present to make a payment using a secure payment terminal.. /// The combination of a language code and a country code to specify the language to be used in the payment.. /// shopperName. @@ -153,9 +153,9 @@ protected BalanceCheckRequest() { } /// The shopper's social security number.. /// An array of objects specifying how the payment should be split when using either Adyen for Platforms for [marketplaces](https://docs.adyen.com/marketplaces/split-payments) or [platforms](https://docs.adyen.com/platforms/split-payments), or standalone [Issuing](https://docs.adyen.com/issuing/add-manage-funds#split).. /// Required for Adyen for Platforms integrations if you are a platform model. This is your [reference](https://docs.adyen.com/api-explorer/Management/3/post/merchants/(merchantId)/stores#request-reference) (on [balance platform](https://docs.adyen.com/platforms)) or the [storeReference](https://docs.adyen.com/api-explorer/Account/latest/post/updateAccountHolder#request-accountHolderDetails-storeDetails-storeReference) (in the [classic integration](https://docs.adyen.com/classic-platforms/processing-payments/route-payment-to-store/#route-a-payment-to-a-store)) for the ecommerce or point-of-sale store that is processing the payment.. - /// The shopper's telephone number.. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication.. /// threeDS2RequestData. - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. (default to false). + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorization.Default: **false**. (default to false). /// The reference value to aggregate sales totals in reporting. When not specified, the store field is used (if available).. /// Set to true if the payment should be routed to a trusted MID.. public BalanceCheckRequest(AccountInfo accountInfo = default(AccountInfo), Amount additionalAmount = default(Amount), Dictionary additionalData = default(Dictionary), Amount amount = default(Amount), ApplicationInfo applicationInfo = default(ApplicationInfo), Address billingAddress = default(Address), BrowserInfo browserInfo = default(BrowserInfo), int? captureDelayHours = default(int?), DateTime dateOfBirth = default(DateTime), ForexQuote dccQuote = default(ForexQuote), Address deliveryAddress = default(Address), DateTime deliveryDate = default(DateTime), string deviceFingerprint = default(string), int? fraudOffset = default(int?), Installments installments = default(Installments), Dictionary localizedShopperStatement = default(Dictionary), string mcc = default(string), string merchantAccount = default(string), string merchantOrderReference = default(string), MerchantRiskIndicator merchantRiskIndicator = default(MerchantRiskIndicator), Dictionary metadata = default(Dictionary), string orderReference = default(string), Dictionary paymentMethod = default(Dictionary), Recurring recurring = default(Recurring), RecurringProcessingModelEnum? recurringProcessingModel = default(RecurringProcessingModelEnum?), string reference = default(string), string selectedBrand = default(string), string selectedRecurringDetailReference = default(string), string sessionId = default(string), string shopperEmail = default(string), string shopperIP = default(string), ShopperInteractionEnum? shopperInteraction = default(ShopperInteractionEnum?), string shopperLocale = default(string), Name shopperName = default(Name), string shopperReference = default(string), string shopperStatement = default(string), string socialSecurityNumber = default(string), List splits = default(List), string store = default(string), string telephoneNumber = default(string), ThreeDS2RequestData threeDS2RequestData = default(ThreeDS2RequestData), bool? threeDSAuthenticationOnly = false, string totalsGroup = default(string), bool? trustedShopper = default(bool?)) @@ -393,16 +393,16 @@ protected BalanceCheckRequest() { } public string SessionId { get; set; } /// - /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > For 3D Secure 2 transactions, schemes require `shopperEmail` for all browser-based and mobile implementations. + /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > Required for Visa and JCB transactions that require 3D Secure 2 authentication if you did not include the `telephoneNumber`. /// - /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > For 3D Secure 2 transactions, schemes require `shopperEmail` for all browser-based and mobile implementations. + /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > Required for Visa and JCB transactions that require 3D Secure 2 authentication if you did not include the `telephoneNumber`. [DataMember(Name = "shopperEmail", EmitDefaultValue = false)] public string ShopperEmail { get; set; } /// - /// The shopper's IP address. In general, we recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks). > For 3D Secure 2 transactions, schemes require `shopperIP` for all browser-based implementations. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). + /// The shopper's IP address. We recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks).> Required for Visa and JCB transactions that require 3D Secure 2 authentication for all web and mobile integrations, if you did not include the `shopperEmail`. For native mobile integrations, the field is required to support cases where authentication is routed to the redirect flow. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). /// - /// The shopper's IP address. In general, we recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks). > For 3D Secure 2 transactions, schemes require `shopperIP` for all browser-based implementations. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). + /// The shopper's IP address. We recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks).> Required for Visa and JCB transactions that require 3D Secure 2 authentication for all web and mobile integrations, if you did not include the `shopperEmail`. For native mobile integrations, the field is required to support cases where authentication is routed to the redirect flow. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). [DataMember(Name = "shopperIP", EmitDefaultValue = false)] public string ShopperIP { get; set; } @@ -455,9 +455,9 @@ protected BalanceCheckRequest() { } public string Store { get; set; } /// - /// The shopper's telephone number. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication. /// - /// The shopper's telephone number. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication. [DataMember(Name = "telephoneNumber", EmitDefaultValue = false)] public string TelephoneNumber { get; set; } @@ -468,9 +468,9 @@ protected BalanceCheckRequest() { } public ThreeDS2RequestData ThreeDS2RequestData { get; set; } /// - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorization.Default: **false**. /// - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorization.Default: **false**. [DataMember(Name = "threeDSAuthenticationOnly", EmitDefaultValue = false)] [Obsolete("Deprecated since Adyen Checkout API v69. Use `authenticationData.authenticationOnly` instead.")] public bool? ThreeDSAuthenticationOnly { get; set; } diff --git a/Adyen/Model/Checkout/CardDetails.cs b/Adyen/Model/Checkout/CardDetails.cs index 1d87040d4..70db52490 100644 --- a/Adyen/Model/Checkout/CardDetails.cs +++ b/Adyen/Model/Checkout/CardDetails.cs @@ -123,13 +123,14 @@ public enum TypeEnum /// The encrypted card number.. /// The encrypted card expiry month.. /// The encrypted card expiry year.. + /// This field contains an encrypted, one-time password or an authentication code provided by the cardholder.. /// The encrypted card verification code.. /// The card expiry month. Only collect raw card data if you are [fully PCI compliant](https://docs.adyen.com/development-resources/pci-dss-compliance-guide).. /// The card expiry year. Only collect raw card data if you are [fully PCI compliant](https://docs.adyen.com/development-resources/pci-dss-compliance-guide).. /// The encoded fastlane data blob. /// The funding source that should be used when multiple sources are available. For Brazilian combo cards, by default the funding source is credit. To use debit, set this value to **debit**.. /// The name of the card holder.. - /// The transaction identifier from card schemes. This is the [`networkTxReference`](https://docs.adyen.com/api-explorer/#/CheckoutService/latest/post/payments__resParam_additionalData-ResponseAdditionalDataCommon-networkTxReference) from the response to the first payment.. + /// The transaction identifier from card schemes. This is the [`networkTxReference`](https://docs.adyen.com/api-explorer/Checkout/latest/post/payments#responses-200-additionalData-ResponseAdditionalDataCommon-networkTxReference) from the response to the first payment.. /// The card number. Only collect raw card data if you are [fully PCI compliant](https://docs.adyen.com/development-resources/pci-dss-compliance-guide).. /// This is the `recurringDetailReference` returned in the response when you created the token.. /// The `shopperNotificationReference` returned in the response when you requested to notify the shopper. Used only for recurring payments in India.. @@ -140,7 +141,7 @@ public enum TypeEnum /// This is the `recurringDetailReference` returned in the response when you created the token.. /// Required for mobile integrations. Version of the 3D Secure 2 mobile SDK.. /// Default payment method details. Common for scheme payment methods, and for simple payment method details. (default to TypeEnum.Scheme). - public CardDetails(string brand = default(string), string checkoutAttemptId = default(string), string cupsecureplusSmscode = default(string), string cvc = default(string), string encryptedCard = default(string), string encryptedCardNumber = default(string), string encryptedExpiryMonth = default(string), string encryptedExpiryYear = default(string), string encryptedSecurityCode = default(string), string expiryMonth = default(string), string expiryYear = default(string), string fastlaneData = default(string), FundingSourceEnum? fundingSource = default(FundingSourceEnum?), string holderName = default(string), string networkPaymentReference = default(string), string number = default(string), string recurringDetailReference = default(string), string shopperNotificationReference = default(string), string srcCorrelationId = default(string), string srcDigitalCardId = default(string), string srcScheme = default(string), string srcTokenReference = default(string), string storedPaymentMethodId = default(string), string threeDS2SdkVersion = default(string), TypeEnum? type = TypeEnum.Scheme) + public CardDetails(string brand = default(string), string checkoutAttemptId = default(string), string cupsecureplusSmscode = default(string), string cvc = default(string), string encryptedCard = default(string), string encryptedCardNumber = default(string), string encryptedExpiryMonth = default(string), string encryptedExpiryYear = default(string), string encryptedPassword = default(string), string encryptedSecurityCode = default(string), string expiryMonth = default(string), string expiryYear = default(string), string fastlaneData = default(string), FundingSourceEnum? fundingSource = default(FundingSourceEnum?), string holderName = default(string), string networkPaymentReference = default(string), string number = default(string), string recurringDetailReference = default(string), string shopperNotificationReference = default(string), string srcCorrelationId = default(string), string srcDigitalCardId = default(string), string srcScheme = default(string), string srcTokenReference = default(string), string storedPaymentMethodId = default(string), string threeDS2SdkVersion = default(string), TypeEnum? type = TypeEnum.Scheme) { this.Brand = brand; this.CheckoutAttemptId = checkoutAttemptId; @@ -150,6 +151,7 @@ public enum TypeEnum this.EncryptedCardNumber = encryptedCardNumber; this.EncryptedExpiryMonth = encryptedExpiryMonth; this.EncryptedExpiryYear = encryptedExpiryYear; + this.EncryptedPassword = encryptedPassword; this.EncryptedSecurityCode = encryptedSecurityCode; this.ExpiryMonth = expiryMonth; this.ExpiryYear = expiryYear; @@ -225,6 +227,13 @@ public enum TypeEnum [DataMember(Name = "encryptedExpiryYear", EmitDefaultValue = false)] public string EncryptedExpiryYear { get; set; } + /// + /// This field contains an encrypted, one-time password or an authentication code provided by the cardholder. + /// + /// This field contains an encrypted, one-time password or an authentication code provided by the cardholder. + [DataMember(Name = "encryptedPassword", EmitDefaultValue = false)] + public string EncryptedPassword { get; set; } + /// /// The encrypted card verification code. /// @@ -261,9 +270,9 @@ public enum TypeEnum public string HolderName { get; set; } /// - /// The transaction identifier from card schemes. This is the [`networkTxReference`](https://docs.adyen.com/api-explorer/#/CheckoutService/latest/post/payments__resParam_additionalData-ResponseAdditionalDataCommon-networkTxReference) from the response to the first payment. + /// The transaction identifier from card schemes. This is the [`networkTxReference`](https://docs.adyen.com/api-explorer/Checkout/latest/post/payments#responses-200-additionalData-ResponseAdditionalDataCommon-networkTxReference) from the response to the first payment. /// - /// The transaction identifier from card schemes. This is the [`networkTxReference`](https://docs.adyen.com/api-explorer/#/CheckoutService/latest/post/payments__resParam_additionalData-ResponseAdditionalDataCommon-networkTxReference) from the response to the first payment. + /// The transaction identifier from card schemes. This is the [`networkTxReference`](https://docs.adyen.com/api-explorer/Checkout/latest/post/payments#responses-200-additionalData-ResponseAdditionalDataCommon-networkTxReference) from the response to the first payment. [DataMember(Name = "networkPaymentReference", EmitDefaultValue = false)] public string NetworkPaymentReference { get; set; } @@ -347,6 +356,7 @@ public override string ToString() sb.Append(" EncryptedCardNumber: ").Append(EncryptedCardNumber).Append("\n"); sb.Append(" EncryptedExpiryMonth: ").Append(EncryptedExpiryMonth).Append("\n"); sb.Append(" EncryptedExpiryYear: ").Append(EncryptedExpiryYear).Append("\n"); + sb.Append(" EncryptedPassword: ").Append(EncryptedPassword).Append("\n"); sb.Append(" EncryptedSecurityCode: ").Append(EncryptedSecurityCode).Append("\n"); sb.Append(" ExpiryMonth: ").Append(ExpiryMonth).Append("\n"); sb.Append(" ExpiryYear: ").Append(ExpiryYear).Append("\n"); @@ -439,6 +449,11 @@ public bool Equals(CardDetails input) (this.EncryptedExpiryYear != null && this.EncryptedExpiryYear.Equals(input.EncryptedExpiryYear)) ) && + ( + this.EncryptedPassword == input.EncryptedPassword || + (this.EncryptedPassword != null && + this.EncryptedPassword.Equals(input.EncryptedPassword)) + ) && ( this.EncryptedSecurityCode == input.EncryptedSecurityCode || (this.EncryptedSecurityCode != null && @@ -565,6 +580,10 @@ public override int GetHashCode() { hashCode = (hashCode * 59) + this.EncryptedExpiryYear.GetHashCode(); } + if (this.EncryptedPassword != null) + { + hashCode = (hashCode * 59) + this.EncryptedPassword.GetHashCode(); + } if (this.EncryptedSecurityCode != null) { hashCode = (hashCode * 59) + this.EncryptedSecurityCode.GetHashCode(); diff --git a/Adyen/Model/Checkout/CardDonations.cs b/Adyen/Model/Checkout/CardDonations.cs index 13a88d4f8..08cb712f9 100644 --- a/Adyen/Model/Checkout/CardDonations.cs +++ b/Adyen/Model/Checkout/CardDonations.cs @@ -123,13 +123,14 @@ public enum TypeEnum /// The encrypted card number.. /// The encrypted card expiry month.. /// The encrypted card expiry year.. + /// This field contains an encrypted, one-time password or an authentication code provided by the cardholder.. /// The encrypted card verification code.. /// The card expiry month. Only collect raw card data if you are [fully PCI compliant](https://docs.adyen.com/development-resources/pci-dss-compliance-guide).. /// The card expiry year. Only collect raw card data if you are [fully PCI compliant](https://docs.adyen.com/development-resources/pci-dss-compliance-guide).. /// The encoded fastlane data blob. /// The funding source that should be used when multiple sources are available. For Brazilian combo cards, by default the funding source is credit. To use debit, set this value to **debit**.. /// The name of the card holder.. - /// The transaction identifier from card schemes. This is the [`networkTxReference`](https://docs.adyen.com/api-explorer/#/CheckoutService/latest/post/payments__resParam_additionalData-ResponseAdditionalDataCommon-networkTxReference) from the response to the first payment.. + /// The transaction identifier from card schemes. This is the [`networkTxReference`](https://docs.adyen.com/api-explorer/Checkout/latest/post/payments#responses-200-additionalData-ResponseAdditionalDataCommon-networkTxReference) from the response to the first payment.. /// The card number. Only collect raw card data if you are [fully PCI compliant](https://docs.adyen.com/development-resources/pci-dss-compliance-guide).. /// This is the `recurringDetailReference` returned in the response when you created the token.. /// The `shopperNotificationReference` returned in the response when you requested to notify the shopper. Used only for recurring payments in India.. @@ -140,7 +141,7 @@ public enum TypeEnum /// This is the `recurringDetailReference` returned in the response when you created the token.. /// Required for mobile integrations. Version of the 3D Secure 2 mobile SDK.. /// Default payment method details. Common for scheme payment methods, and for simple payment method details. (default to TypeEnum.Scheme). - public CardDonations(string brand = default(string), string checkoutAttemptId = default(string), string cupsecureplusSmscode = default(string), string cvc = default(string), string encryptedCard = default(string), string encryptedCardNumber = default(string), string encryptedExpiryMonth = default(string), string encryptedExpiryYear = default(string), string encryptedSecurityCode = default(string), string expiryMonth = default(string), string expiryYear = default(string), string fastlaneData = default(string), FundingSourceEnum? fundingSource = default(FundingSourceEnum?), string holderName = default(string), string networkPaymentReference = default(string), string number = default(string), string recurringDetailReference = default(string), string shopperNotificationReference = default(string), string srcCorrelationId = default(string), string srcDigitalCardId = default(string), string srcScheme = default(string), string srcTokenReference = default(string), string storedPaymentMethodId = default(string), string threeDS2SdkVersion = default(string), TypeEnum? type = TypeEnum.Scheme) + public CardDonations(string brand = default(string), string checkoutAttemptId = default(string), string cupsecureplusSmscode = default(string), string cvc = default(string), string encryptedCard = default(string), string encryptedCardNumber = default(string), string encryptedExpiryMonth = default(string), string encryptedExpiryYear = default(string), string encryptedPassword = default(string), string encryptedSecurityCode = default(string), string expiryMonth = default(string), string expiryYear = default(string), string fastlaneData = default(string), FundingSourceEnum? fundingSource = default(FundingSourceEnum?), string holderName = default(string), string networkPaymentReference = default(string), string number = default(string), string recurringDetailReference = default(string), string shopperNotificationReference = default(string), string srcCorrelationId = default(string), string srcDigitalCardId = default(string), string srcScheme = default(string), string srcTokenReference = default(string), string storedPaymentMethodId = default(string), string threeDS2SdkVersion = default(string), TypeEnum? type = TypeEnum.Scheme) { this.Brand = brand; this.CheckoutAttemptId = checkoutAttemptId; @@ -150,6 +151,7 @@ public enum TypeEnum this.EncryptedCardNumber = encryptedCardNumber; this.EncryptedExpiryMonth = encryptedExpiryMonth; this.EncryptedExpiryYear = encryptedExpiryYear; + this.EncryptedPassword = encryptedPassword; this.EncryptedSecurityCode = encryptedSecurityCode; this.ExpiryMonth = expiryMonth; this.ExpiryYear = expiryYear; @@ -225,6 +227,13 @@ public enum TypeEnum [DataMember(Name = "encryptedExpiryYear", EmitDefaultValue = false)] public string EncryptedExpiryYear { get; set; } + /// + /// This field contains an encrypted, one-time password or an authentication code provided by the cardholder. + /// + /// This field contains an encrypted, one-time password or an authentication code provided by the cardholder. + [DataMember(Name = "encryptedPassword", EmitDefaultValue = false)] + public string EncryptedPassword { get; set; } + /// /// The encrypted card verification code. /// @@ -261,9 +270,9 @@ public enum TypeEnum public string HolderName { get; set; } /// - /// The transaction identifier from card schemes. This is the [`networkTxReference`](https://docs.adyen.com/api-explorer/#/CheckoutService/latest/post/payments__resParam_additionalData-ResponseAdditionalDataCommon-networkTxReference) from the response to the first payment. + /// The transaction identifier from card schemes. This is the [`networkTxReference`](https://docs.adyen.com/api-explorer/Checkout/latest/post/payments#responses-200-additionalData-ResponseAdditionalDataCommon-networkTxReference) from the response to the first payment. /// - /// The transaction identifier from card schemes. This is the [`networkTxReference`](https://docs.adyen.com/api-explorer/#/CheckoutService/latest/post/payments__resParam_additionalData-ResponseAdditionalDataCommon-networkTxReference) from the response to the first payment. + /// The transaction identifier from card schemes. This is the [`networkTxReference`](https://docs.adyen.com/api-explorer/Checkout/latest/post/payments#responses-200-additionalData-ResponseAdditionalDataCommon-networkTxReference) from the response to the first payment. [DataMember(Name = "networkPaymentReference", EmitDefaultValue = false)] public string NetworkPaymentReference { get; set; } @@ -347,6 +356,7 @@ public override string ToString() sb.Append(" EncryptedCardNumber: ").Append(EncryptedCardNumber).Append("\n"); sb.Append(" EncryptedExpiryMonth: ").Append(EncryptedExpiryMonth).Append("\n"); sb.Append(" EncryptedExpiryYear: ").Append(EncryptedExpiryYear).Append("\n"); + sb.Append(" EncryptedPassword: ").Append(EncryptedPassword).Append("\n"); sb.Append(" EncryptedSecurityCode: ").Append(EncryptedSecurityCode).Append("\n"); sb.Append(" ExpiryMonth: ").Append(ExpiryMonth).Append("\n"); sb.Append(" ExpiryYear: ").Append(ExpiryYear).Append("\n"); @@ -439,6 +449,11 @@ public bool Equals(CardDonations input) (this.EncryptedExpiryYear != null && this.EncryptedExpiryYear.Equals(input.EncryptedExpiryYear)) ) && + ( + this.EncryptedPassword == input.EncryptedPassword || + (this.EncryptedPassword != null && + this.EncryptedPassword.Equals(input.EncryptedPassword)) + ) && ( this.EncryptedSecurityCode == input.EncryptedSecurityCode || (this.EncryptedSecurityCode != null && @@ -565,6 +580,10 @@ public override int GetHashCode() { hashCode = (hashCode * 59) + this.EncryptedExpiryYear.GetHashCode(); } + if (this.EncryptedPassword != null) + { + hashCode = (hashCode * 59) + this.EncryptedPassword.GetHashCode(); + } if (this.EncryptedSecurityCode != null) { hashCode = (hashCode * 59) + this.EncryptedSecurityCode.GetHashCode(); diff --git a/Adyen/Model/Checkout/CreateCheckoutSessionRequest.cs b/Adyen/Model/Checkout/CreateCheckoutSessionRequest.cs index 8ea96dc09..6cecc7153 100644 --- a/Adyen/Model/Checkout/CreateCheckoutSessionRequest.cs +++ b/Adyen/Model/Checkout/CreateCheckoutSessionRequest.cs @@ -282,10 +282,10 @@ protected CreateCheckoutSessionRequest() { } /// Specifies the redirect method (GET or POST) when redirecting back from the issuer.. /// Specifies the redirect method (GET or POST) when redirecting to the issuer.. /// The reference to uniquely identify a payment. (required). - /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. > The URL must not include personally identifiable information (PII), for example name or email address. (required). + /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. We strongly recommend that you use a maximum of 1024 characters. > The URL must not include personally identifiable information (PII), for example name or email address. (required). /// riskData. /// The shopper's email address.. - /// The shopper's IP address. In general, we recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks). > For 3D Secure 2 transactions, schemes require `shopperIP` for all browser-based implementations. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new).. + /// The shopper's IP address. We recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks).> Required for Visa and JCB transactions that require 3D Secure 2 authentication for all web and mobile integrations, if you did not include the `shopperEmail`. For native mobile integrations, the field is required to support cases where authentication is routed to the redirect flow. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new).. /// Specifies the sales channel, through which the shopper gives their card details, and whether the shopper is a returning customer. For the web service API, Adyen assumes Ecommerce shopper interaction by default. This field has the following possible values: * `Ecommerce` - Online transactions where the cardholder is present (online). For better authorisation rates, we recommend sending the card security code (CSC) along with the request. * `ContAuth` - Card on file and/or subscription transactions, where the cardholder is known to the merchant (returning customer). If the shopper is present (online), you can supply also the CSC to improve authorisation (one-click payment). * `Moto` - Mail-order and telephone-order transactions where the shopper is in contact with the merchant via email or telephone. * `POS` - Point-of-sale transactions where the shopper is physically present to make a payment using a secure payment terminal.. /// The combination of a language code and a country code to specify the language to be used in the payment.. /// shopperName. @@ -300,10 +300,10 @@ protected CreateCheckoutSessionRequest() { } /// Specifies how payment methods should be filtered based on the 'store' parameter: - 'exclusive': Only payment methods belonging to the specified 'store' are returned. - 'inclusive': Payment methods from the 'store' and those not associated with any other store are returned.. /// When true and `shopperReference` is provided, the payment details will be stored for future [recurring payments](https://docs.adyen.com/online-payments/tokenization/#recurring-payment-types).. /// Indicates if the details of the payment method will be stored for the shopper. Possible values: * **disabled** – No details will be stored (default). * **askForConsent** – If the `shopperReference` is provided, the UI lets the shopper choose if they want their payment details to be stored. * **enabled** – If the `shopperReference` is provided, the details will be stored without asking the shopper for consent.. - /// The shopper's telephone number.. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication.. /// Sets a custom theme for [Hosted Checkout](https://docs.adyen.com/online-payments/build-your-integration/?platform=Web&integration=Hosted+Checkout). The value can be any of the **Theme ID** values from your Customer Area.. /// threeDS2RequestData. - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. (default to false). + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorization.Default: **false**. (default to false). /// Set to true if the payment should be routed to a trusted MID.. public CreateCheckoutSessionRequest(AccountInfo accountInfo = default(AccountInfo), Amount additionalAmount = default(Amount), Dictionary additionalData = default(Dictionary), List allowedPaymentMethods = default(List), Amount amount = default(Amount), ApplicationInfo applicationInfo = default(ApplicationInfo), AuthenticationData authenticationData = default(AuthenticationData), BillingAddress billingAddress = default(BillingAddress), List blockedPaymentMethods = default(List), int? captureDelayHours = default(int?), ChannelEnum? channel = default(ChannelEnum?), Company company = default(Company), string countryCode = default(string), DateTime dateOfBirth = default(DateTime), DateTime deliverAt = default(DateTime), DeliveryAddress deliveryAddress = default(DeliveryAddress), bool? enableOneClick = default(bool?), bool? enablePayOut = default(bool?), bool? enableRecurring = default(bool?), DateTime expiresAt = default(DateTime), FundOrigin fundOrigin = default(FundOrigin), FundRecipient fundRecipient = default(FundRecipient), Dictionary installmentOptions = default(Dictionary), List lineItems = default(List), Mandate mandate = default(Mandate), string mcc = default(string), string merchantAccount = default(string), string merchantOrderReference = default(string), Dictionary metadata = default(Dictionary), ModeEnum? mode = ModeEnum.Embedded, ThreeDSecureData mpiData = default(ThreeDSecureData), PlatformChargebackLogic platformChargebackLogic = default(PlatformChargebackLogic), string recurringExpiry = default(string), string recurringFrequency = default(string), RecurringProcessingModelEnum? recurringProcessingModel = default(RecurringProcessingModelEnum?), string redirectFromIssuerMethod = default(string), string redirectToIssuerMethod = default(string), string reference = default(string), string returnUrl = default(string), RiskData riskData = default(RiskData), string shopperEmail = default(string), string shopperIP = default(string), ShopperInteractionEnum? shopperInteraction = default(ShopperInteractionEnum?), string shopperLocale = default(string), Name shopperName = default(Name), string shopperReference = default(string), string shopperStatement = default(string), bool? showInstallmentAmount = default(bool?), bool? showRemovePaymentMethodButton = default(bool?), string socialSecurityNumber = default(string), bool? splitCardFundingSources = false, List splits = default(List), string store = default(string), StoreFiltrationModeEnum? storeFiltrationMode = default(StoreFiltrationModeEnum?), bool? storePaymentMethod = default(bool?), StorePaymentMethodModeEnum? storePaymentMethodMode = default(StorePaymentMethodModeEnum?), string telephoneNumber = default(string), string themeId = default(string), CheckoutSessionThreeDS2RequestData threeDS2RequestData = default(CheckoutSessionThreeDS2RequestData), bool? threeDSAuthenticationOnly = false, bool? trustedShopper = default(bool?)) { @@ -604,9 +604,9 @@ protected CreateCheckoutSessionRequest() { } public string Reference { get; set; } /// - /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. > The URL must not include personally identifiable information (PII), for example name or email address. + /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. We strongly recommend that you use a maximum of 1024 characters. > The URL must not include personally identifiable information (PII), for example name or email address. /// - /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. > The URL must not include personally identifiable information (PII), for example name or email address. + /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. We strongly recommend that you use a maximum of 1024 characters. > The URL must not include personally identifiable information (PII), for example name or email address. [DataMember(Name = "returnUrl", IsRequired = false, EmitDefaultValue = false)] public string ReturnUrl { get; set; } @@ -624,9 +624,9 @@ protected CreateCheckoutSessionRequest() { } public string ShopperEmail { get; set; } /// - /// The shopper's IP address. In general, we recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks). > For 3D Secure 2 transactions, schemes require `shopperIP` for all browser-based implementations. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). + /// The shopper's IP address. We recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks).> Required for Visa and JCB transactions that require 3D Secure 2 authentication for all web and mobile integrations, if you did not include the `shopperEmail`. For native mobile integrations, the field is required to support cases where authentication is routed to the redirect flow. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). /// - /// The shopper's IP address. In general, we recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks). > For 3D Secure 2 transactions, schemes require `shopperIP` for all browser-based implementations. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). + /// The shopper's IP address. We recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks).> Required for Visa and JCB transactions that require 3D Secure 2 authentication for all web and mobile integrations, if you did not include the `shopperEmail`. For native mobile integrations, the field is required to support cases where authentication is routed to the redirect flow. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). [DataMember(Name = "shopperIP", EmitDefaultValue = false)] public string ShopperIP { get; set; } @@ -707,9 +707,9 @@ protected CreateCheckoutSessionRequest() { } public bool? StorePaymentMethod { get; set; } /// - /// The shopper's telephone number. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication. /// - /// The shopper's telephone number. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication. [DataMember(Name = "telephoneNumber", EmitDefaultValue = false)] public string TelephoneNumber { get; set; } @@ -727,9 +727,9 @@ protected CreateCheckoutSessionRequest() { } public CheckoutSessionThreeDS2RequestData ThreeDS2RequestData { get; set; } /// - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorization.Default: **false**. /// - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorization.Default: **false**. [DataMember(Name = "threeDSAuthenticationOnly", EmitDefaultValue = false)] [Obsolete("Deprecated since Adyen Checkout API v69. Use `authenticationData.authenticationOnly` instead.")] public bool? ThreeDSAuthenticationOnly { get; set; } diff --git a/Adyen/Model/Checkout/CreateCheckoutSessionResponse.cs b/Adyen/Model/Checkout/CreateCheckoutSessionResponse.cs index b1db134a1..2e2d58a66 100644 --- a/Adyen/Model/Checkout/CreateCheckoutSessionResponse.cs +++ b/Adyen/Model/Checkout/CreateCheckoutSessionResponse.cs @@ -282,11 +282,11 @@ protected CreateCheckoutSessionResponse() { } /// Specifies the redirect method (GET or POST) when redirecting back from the issuer.. /// Specifies the redirect method (GET or POST) when redirecting to the issuer.. /// The reference to uniquely identify a payment. (required). - /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. > The URL must not include personally identifiable information (PII), for example name or email address. (required). + /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. We strongly recommend that you use a maximum of 1024 characters. > The URL must not include personally identifiable information (PII), for example name or email address. (required). /// riskData. /// The payment session data you need to pass to your front end.. /// The shopper's email address.. - /// The shopper's IP address. In general, we recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks). > For 3D Secure 2 transactions, schemes require `shopperIP` for all browser-based implementations. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new).. + /// The shopper's IP address. We recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks).> Required for Visa and JCB transactions that require 3D Secure 2 authentication for all web and mobile integrations, if you did not include the `shopperEmail`. For native mobile integrations, the field is required to support cases where authentication is routed to the redirect flow. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new).. /// Specifies the sales channel, through which the shopper gives their card details, and whether the shopper is a returning customer. For the web service API, Adyen assumes Ecommerce shopper interaction by default. This field has the following possible values: * `Ecommerce` - Online transactions where the cardholder is present (online). For better authorisation rates, we recommend sending the card security code (CSC) along with the request. * `ContAuth` - Card on file and/or subscription transactions, where the cardholder is known to the merchant (returning customer). If the shopper is present (online), you can supply also the CSC to improve authorisation (one-click payment). * `Moto` - Mail-order and telephone-order transactions where the shopper is in contact with the merchant via email or telephone. * `POS` - Point-of-sale transactions where the shopper is physically present to make a payment using a secure payment terminal.. /// The combination of a language code and a country code to specify the language to be used in the payment.. /// shopperName. @@ -301,10 +301,10 @@ protected CreateCheckoutSessionResponse() { } /// Specifies how payment methods should be filtered based on the 'store' parameter: - 'exclusive': Only payment methods belonging to the specified 'store' are returned. - 'inclusive': Payment methods from the 'store' and those not associated with any other store are returned.. /// When true and `shopperReference` is provided, the payment details will be stored for future [recurring payments](https://docs.adyen.com/online-payments/tokenization/#recurring-payment-types).. /// Indicates if the details of the payment method will be stored for the shopper. Possible values: * **disabled** – No details will be stored (default). * **askForConsent** – If the `shopperReference` is provided, the UI lets the shopper choose if they want their payment details to be stored. * **enabled** – If the `shopperReference` is provided, the details will be stored without asking the shopper for consent.. - /// The shopper's telephone number.. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication.. /// Sets a custom theme for [Hosted Checkout](https://docs.adyen.com/online-payments/build-your-integration/?platform=Web&integration=Hosted+Checkout). The value can be any of the **Theme ID** values from your Customer Area.. /// threeDS2RequestData. - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. (default to false). + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorization.Default: **false**. (default to false). /// Set to true if the payment should be routed to a trusted MID.. /// The URL for the Hosted Checkout page. Redirect the shopper to this URL so they can make the payment.. public CreateCheckoutSessionResponse(AccountInfo accountInfo = default(AccountInfo), Amount additionalAmount = default(Amount), Dictionary additionalData = default(Dictionary), List allowedPaymentMethods = default(List), Amount amount = default(Amount), ApplicationInfo applicationInfo = default(ApplicationInfo), AuthenticationData authenticationData = default(AuthenticationData), BillingAddress billingAddress = default(BillingAddress), List blockedPaymentMethods = default(List), int? captureDelayHours = default(int?), ChannelEnum? channel = default(ChannelEnum?), Company company = default(Company), string countryCode = default(string), DateTime dateOfBirth = default(DateTime), DateTime deliverAt = default(DateTime), DeliveryAddress deliveryAddress = default(DeliveryAddress), bool? enableOneClick = default(bool?), bool? enablePayOut = default(bool?), bool? enableRecurring = default(bool?), DateTime expiresAt = default(DateTime), FundOrigin fundOrigin = default(FundOrigin), FundRecipient fundRecipient = default(FundRecipient), Dictionary installmentOptions = default(Dictionary), List lineItems = default(List), Mandate mandate = default(Mandate), string mcc = default(string), string merchantAccount = default(string), string merchantOrderReference = default(string), Dictionary metadata = default(Dictionary), ModeEnum? mode = ModeEnum.Embedded, ThreeDSecureData mpiData = default(ThreeDSecureData), PlatformChargebackLogic platformChargebackLogic = default(PlatformChargebackLogic), string recurringExpiry = default(string), string recurringFrequency = default(string), RecurringProcessingModelEnum? recurringProcessingModel = default(RecurringProcessingModelEnum?), string redirectFromIssuerMethod = default(string), string redirectToIssuerMethod = default(string), string reference = default(string), string returnUrl = default(string), RiskData riskData = default(RiskData), string sessionData = default(string), string shopperEmail = default(string), string shopperIP = default(string), ShopperInteractionEnum? shopperInteraction = default(ShopperInteractionEnum?), string shopperLocale = default(string), Name shopperName = default(Name), string shopperReference = default(string), string shopperStatement = default(string), bool? showInstallmentAmount = default(bool?), bool? showRemovePaymentMethodButton = default(bool?), string socialSecurityNumber = default(string), bool? splitCardFundingSources = false, List splits = default(List), string store = default(string), StoreFiltrationModeEnum? storeFiltrationMode = default(StoreFiltrationModeEnum?), bool? storePaymentMethod = default(bool?), StorePaymentMethodModeEnum? storePaymentMethodMode = default(StorePaymentMethodModeEnum?), string telephoneNumber = default(string), string themeId = default(string), CheckoutSessionThreeDS2RequestData threeDS2RequestData = default(CheckoutSessionThreeDS2RequestData), bool? threeDSAuthenticationOnly = false, bool? trustedShopper = default(bool?), string url = default(string)) @@ -614,9 +614,9 @@ protected CreateCheckoutSessionResponse() { } public string Reference { get; set; } /// - /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. > The URL must not include personally identifiable information (PII), for example name or email address. + /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. We strongly recommend that you use a maximum of 1024 characters. > The URL must not include personally identifiable information (PII), for example name or email address. /// - /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. > The URL must not include personally identifiable information (PII), for example name or email address. + /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. We strongly recommend that you use a maximum of 1024 characters. > The URL must not include personally identifiable information (PII), for example name or email address. [DataMember(Name = "returnUrl", IsRequired = false, EmitDefaultValue = false)] public string ReturnUrl { get; set; } @@ -641,9 +641,9 @@ protected CreateCheckoutSessionResponse() { } public string ShopperEmail { get; set; } /// - /// The shopper's IP address. In general, we recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks). > For 3D Secure 2 transactions, schemes require `shopperIP` for all browser-based implementations. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). + /// The shopper's IP address. We recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks).> Required for Visa and JCB transactions that require 3D Secure 2 authentication for all web and mobile integrations, if you did not include the `shopperEmail`. For native mobile integrations, the field is required to support cases where authentication is routed to the redirect flow. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). /// - /// The shopper's IP address. In general, we recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks). > For 3D Secure 2 transactions, schemes require `shopperIP` for all browser-based implementations. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). + /// The shopper's IP address. We recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks).> Required for Visa and JCB transactions that require 3D Secure 2 authentication for all web and mobile integrations, if you did not include the `shopperEmail`. For native mobile integrations, the field is required to support cases where authentication is routed to the redirect flow. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). [DataMember(Name = "shopperIP", EmitDefaultValue = false)] public string ShopperIP { get; set; } @@ -724,9 +724,9 @@ protected CreateCheckoutSessionResponse() { } public bool? StorePaymentMethod { get; set; } /// - /// The shopper's telephone number. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication. /// - /// The shopper's telephone number. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication. [DataMember(Name = "telephoneNumber", EmitDefaultValue = false)] public string TelephoneNumber { get; set; } @@ -744,9 +744,9 @@ protected CreateCheckoutSessionResponse() { } public CheckoutSessionThreeDS2RequestData ThreeDS2RequestData { get; set; } /// - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorization.Default: **false**. /// - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorization.Default: **false**. [DataMember(Name = "threeDSAuthenticationOnly", EmitDefaultValue = false)] [Obsolete("Deprecated since Adyen Checkout API v69. Use `authenticationData.authenticationOnly` instead.")] public bool? ThreeDSAuthenticationOnly { get; set; } diff --git a/Adyen/Model/Checkout/DetailsRequestAuthenticationData.cs b/Adyen/Model/Checkout/DetailsRequestAuthenticationData.cs index 26c9d1432..ee3580d0d 100644 --- a/Adyen/Model/Checkout/DetailsRequestAuthenticationData.cs +++ b/Adyen/Model/Checkout/DetailsRequestAuthenticationData.cs @@ -35,16 +35,16 @@ public partial class DetailsRequestAuthenticationData : IEquatable /// Initializes a new instance of the class. /// - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. Default: *false**. (default to false). + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorization.Default: **false**. (default to false). public DetailsRequestAuthenticationData(bool? authenticationOnly = false) { this.AuthenticationOnly = authenticationOnly; } /// - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. Default: *false**. + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorization.Default: **false**. /// - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. Default: *false**. + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorization.Default: **false**. [DataMember(Name = "authenticationOnly", EmitDefaultValue = false)] public bool? AuthenticationOnly { get; set; } diff --git a/Adyen/Model/Checkout/DonationPaymentRequest.cs b/Adyen/Model/Checkout/DonationPaymentRequest.cs index bec73d6fa..1fd2eef0b 100644 --- a/Adyen/Model/Checkout/DonationPaymentRequest.cs +++ b/Adyen/Model/Checkout/DonationPaymentRequest.cs @@ -101,9 +101,9 @@ public enum RecurringProcessingModelEnum [DataMember(Name = "recurringProcessingModel", EmitDefaultValue = false)] public RecurringProcessingModelEnum? RecurringProcessingModel { get; set; } /// - /// Specifies the sales channel, through which the shopper gives their card details, and whether the shopper is a returning customer. For the web service API, Adyen assumes Ecommerce shopper interaction by default. This field has the following possible values: * `Ecommerce` - Online transactions where the cardholder is present (online). For better authorisation rates, we recommend sending the card security code (CSC) along with the request. * `ContAuth` - Card on file and/or subscription transactions, where the cardholder is known to the merchant (returning customer). If the shopper is present (online), you can supply also the CSC to improve authorisation (one-click payment). * `Moto` - Mail-order and telephone-order transactions where the shopper is in contact with the merchant via email or telephone. * `POS` - Point-of-sale transactions where the shopper is physically present to make a payment using a secure payment terminal. + /// Specifies the sales channel, through which the shopper gives their card details, and whether the shopper is a returning customer. For the web service API, Adyen assumes Ecommerce shopper interaction by default. This field has the following possible values: * `Ecommerce` - Online transactions where the cardholder is present (online). For better authorization rates, we recommend sending the card security code (CSC) along with the request. * `ContAuth` - Card on file and/or subscription transactions, where the cardholder is known to the merchant (returning customer). If the shopper is present (online), you can supply also the CSC to improve authorization (one-click payment). * `Moto` - Mail-order and telephone-order transactions where the shopper is in contact with the merchant via email or telephone. * `POS` - Point-of-sale transactions where the shopper is physically present to make a payment using a secure payment terminal. /// - /// Specifies the sales channel, through which the shopper gives their card details, and whether the shopper is a returning customer. For the web service API, Adyen assumes Ecommerce shopper interaction by default. This field has the following possible values: * `Ecommerce` - Online transactions where the cardholder is present (online). For better authorisation rates, we recommend sending the card security code (CSC) along with the request. * `ContAuth` - Card on file and/or subscription transactions, where the cardholder is known to the merchant (returning customer). If the shopper is present (online), you can supply also the CSC to improve authorisation (one-click payment). * `Moto` - Mail-order and telephone-order transactions where the shopper is in contact with the merchant via email or telephone. * `POS` - Point-of-sale transactions where the shopper is physically present to make a payment using a secure payment terminal. + /// Specifies the sales channel, through which the shopper gives their card details, and whether the shopper is a returning customer. For the web service API, Adyen assumes Ecommerce shopper interaction by default. This field has the following possible values: * `Ecommerce` - Online transactions where the cardholder is present (online). For better authorization rates, we recommend sending the card security code (CSC) along with the request. * `ContAuth` - Card on file and/or subscription transactions, where the cardholder is known to the merchant (returning customer). If the shopper is present (online), you can supply also the CSC to improve authorization (one-click payment). * `Moto` - Mail-order and telephone-order transactions where the shopper is in contact with the merchant via email or telephone. * `POS` - Point-of-sale transactions where the shopper is physically present to make a payment using a secure payment terminal. [JsonConverter(typeof(StringEnumConverter))] public enum ShopperInteractionEnum { @@ -135,9 +135,9 @@ public enum ShopperInteractionEnum /// - /// Specifies the sales channel, through which the shopper gives their card details, and whether the shopper is a returning customer. For the web service API, Adyen assumes Ecommerce shopper interaction by default. This field has the following possible values: * `Ecommerce` - Online transactions where the cardholder is present (online). For better authorisation rates, we recommend sending the card security code (CSC) along with the request. * `ContAuth` - Card on file and/or subscription transactions, where the cardholder is known to the merchant (returning customer). If the shopper is present (online), you can supply also the CSC to improve authorisation (one-click payment). * `Moto` - Mail-order and telephone-order transactions where the shopper is in contact with the merchant via email or telephone. * `POS` - Point-of-sale transactions where the shopper is physically present to make a payment using a secure payment terminal. + /// Specifies the sales channel, through which the shopper gives their card details, and whether the shopper is a returning customer. For the web service API, Adyen assumes Ecommerce shopper interaction by default. This field has the following possible values: * `Ecommerce` - Online transactions where the cardholder is present (online). For better authorization rates, we recommend sending the card security code (CSC) along with the request. * `ContAuth` - Card on file and/or subscription transactions, where the cardholder is known to the merchant (returning customer). If the shopper is present (online), you can supply also the CSC to improve authorization (one-click payment). * `Moto` - Mail-order and telephone-order transactions where the shopper is in contact with the merchant via email or telephone. * `POS` - Point-of-sale transactions where the shopper is physically present to make a payment using a secure payment terminal. /// - /// Specifies the sales channel, through which the shopper gives their card details, and whether the shopper is a returning customer. For the web service API, Adyen assumes Ecommerce shopper interaction by default. This field has the following possible values: * `Ecommerce` - Online transactions where the cardholder is present (online). For better authorisation rates, we recommend sending the card security code (CSC) along with the request. * `ContAuth` - Card on file and/or subscription transactions, where the cardholder is known to the merchant (returning customer). If the shopper is present (online), you can supply also the CSC to improve authorisation (one-click payment). * `Moto` - Mail-order and telephone-order transactions where the shopper is in contact with the merchant via email or telephone. * `POS` - Point-of-sale transactions where the shopper is physically present to make a payment using a secure payment terminal. + /// Specifies the sales channel, through which the shopper gives their card details, and whether the shopper is a returning customer. For the web service API, Adyen assumes Ecommerce shopper interaction by default. This field has the following possible values: * `Ecommerce` - Online transactions where the cardholder is present (online). For better authorization rates, we recommend sending the card security code (CSC) along with the request. * `ContAuth` - Card on file and/or subscription transactions, where the cardholder is known to the merchant (returning customer). If the shopper is present (online), you can supply also the CSC to improve authorization (one-click payment). * `Moto` - Mail-order and telephone-order transactions where the shopper is in contact with the merchant via email or telephone. * `POS` - Point-of-sale transactions where the shopper is physically present to make a payment using a secure payment terminal. [DataMember(Name = "shopperInteraction", EmitDefaultValue = false)] public ShopperInteractionEnum? ShopperInteraction { get; set; } /// @@ -172,24 +172,24 @@ protected DonationPaymentRequest() { } /// merchantRiskIndicator. /// Metadata consists of entries, each of which includes a key and a value. Limits: * Maximum 20 key-value pairs per request. When exceeding, the \"177\" error occurs: \"Metadata size exceeds limit\". * Maximum 20 characters per key. * Maximum 80 characters per value. . /// mpiData. - /// Required for the 3D Secure 2 `channel` **Web** integration. Set this parameter to the origin URL of the page that you are loading the 3D Secure Component from.. + /// > Required for browser-based (`channel` **Web**) 3D Secure 2 transactions.Set this to the origin URL of the page where you are rendering the Drop-in/Component. Do not include subdirectories and a trailing slash.. /// paymentMethod (required). /// Defines a recurring payment type. Required when creating a token to store payment details or using stored payment details. Allowed values: * `Subscription` – A transaction for a fixed or variable amount, which follows a fixed schedule. * `CardOnFile` – With a card-on-file (CoF) transaction, card details are stored to enable one-click or omnichannel journeys, or simply to streamline the checkout process. Any subscription not following a fixed schedule is also considered a card-on-file transaction. * `UnscheduledCardOnFile` – An unscheduled card-on-file (UCoF) transaction is a transaction that occurs on a non-fixed schedule and/or have variable amounts. For example, automatic top-ups when a cardholder's balance drops below a certain amount. . /// Specifies the redirect method (GET or POST) when redirecting back from the issuer.. /// Specifies the redirect method (GET or POST) when redirecting to the issuer.. /// The reference to uniquely identify a payment. This reference is used in all communication with you about the payment status. We recommend using a unique value per payment; however, it is not a requirement. If you need to provide multiple references for a transaction, separate them with hyphens (\"-\"). Maximum length: 80 characters. (required). - /// The URL to return to in case of a redirection. The format depends on the channel. This URL can have a maximum of 1024 characters. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. > The URL must not include personally identifiable information (PII), for example name or email address. (required). + /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. We strongly recommend that you use a maximum of 1024 characters. > The URL must not include personally identifiable information (PII), for example name or email address. (required). /// The date and time until when the session remains valid, in [ISO 8601](https://www.w3.org/TR/NOTE-datetime) format. For example: 2020-07-18T15:42:40.428+01:00. - /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > For 3D Secure 2 transactions, schemes require `shopperEmail` for all browser-based and mobile implementations.. - /// The shopper's IP address. In general, we recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks). > For 3D Secure 2 transactions, schemes require `shopperIP` for all browser-based implementations. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new).. - /// Specifies the sales channel, through which the shopper gives their card details, and whether the shopper is a returning customer. For the web service API, Adyen assumes Ecommerce shopper interaction by default. This field has the following possible values: * `Ecommerce` - Online transactions where the cardholder is present (online). For better authorisation rates, we recommend sending the card security code (CSC) along with the request. * `ContAuth` - Card on file and/or subscription transactions, where the cardholder is known to the merchant (returning customer). If the shopper is present (online), you can supply also the CSC to improve authorisation (one-click payment). * `Moto` - Mail-order and telephone-order transactions where the shopper is in contact with the merchant via email or telephone. * `POS` - Point-of-sale transactions where the shopper is physically present to make a payment using a secure payment terminal.. + /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > Required for Visa and JCB transactions that require 3D Secure 2 authentication if you did not include the `telephoneNumber`.. + /// The shopper's IP address. We recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks).> Required for Visa and JCB transactions that require 3D Secure 2 authentication for all web and mobile integrations, if you did not include the `shopperEmail`. For native mobile integrations, the field is required to support cases where authentication is routed to the redirect flow. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new).. + /// Specifies the sales channel, through which the shopper gives their card details, and whether the shopper is a returning customer. For the web service API, Adyen assumes Ecommerce shopper interaction by default. This field has the following possible values: * `Ecommerce` - Online transactions where the cardholder is present (online). For better authorization rates, we recommend sending the card security code (CSC) along with the request. * `ContAuth` - Card on file and/or subscription transactions, where the cardholder is known to the merchant (returning customer). If the shopper is present (online), you can supply also the CSC to improve authorization (one-click payment). * `Moto` - Mail-order and telephone-order transactions where the shopper is in contact with the merchant via email or telephone. * `POS` - Point-of-sale transactions where the shopper is physically present to make a payment using a secure payment terminal.. /// The combination of a language code and a country code to specify the language to be used in the payment.. /// shopperName. /// Required for recurring payments. Your reference to uniquely identify this shopper, for example user ID or account ID. Minimum length: 3 characters. > Your reference must not include personally identifiable information (PII), for example name or email address.. /// The shopper's social security number.. - /// The shopper's telephone number.. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication.. /// threeDS2RequestData. - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. (default to false). + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorization.Default: **false**. (default to false). public DonationPaymentRequest(AccountInfo accountInfo = default(AccountInfo), Dictionary additionalData = default(Dictionary), Amount amount = default(Amount), ApplicationInfo applicationInfo = default(ApplicationInfo), AuthenticationData authenticationData = default(AuthenticationData), BillingAddress billingAddress = default(BillingAddress), BrowserInfo browserInfo = default(BrowserInfo), ChannelEnum? channel = default(ChannelEnum?), string checkoutAttemptId = default(string), string conversionId = default(string), string countryCode = default(string), DateTime dateOfBirth = default(DateTime), DateTime deliverAt = default(DateTime), DeliveryAddress deliveryAddress = default(DeliveryAddress), string deviceFingerprint = default(string), string donationAccount = default(string), string donationCampaignId = default(string), string donationOriginalPspReference = default(string), string donationToken = default(string), List lineItems = default(List), string merchantAccount = default(string), MerchantRiskIndicator merchantRiskIndicator = default(MerchantRiskIndicator), Dictionary metadata = default(Dictionary), ThreeDSecureData mpiData = default(ThreeDSecureData), string origin = default(string), DonationPaymentMethod paymentMethod = default(DonationPaymentMethod), RecurringProcessingModelEnum? recurringProcessingModel = default(RecurringProcessingModelEnum?), string redirectFromIssuerMethod = default(string), string redirectToIssuerMethod = default(string), string reference = default(string), string returnUrl = default(string), string sessionValidity = default(string), string shopperEmail = default(string), string shopperIP = default(string), ShopperInteractionEnum? shopperInteraction = default(ShopperInteractionEnum?), string shopperLocale = default(string), Name shopperName = default(Name), string shopperReference = default(string), string socialSecurityNumber = default(string), string telephoneNumber = default(string), ThreeDS2RequestFields threeDS2RequestData = default(ThreeDS2RequestFields), bool? threeDSAuthenticationOnly = false) { this.Amount = amount; @@ -390,9 +390,9 @@ protected DonationPaymentRequest() { } public ThreeDSecureData MpiData { get; set; } /// - /// Required for the 3D Secure 2 `channel` **Web** integration. Set this parameter to the origin URL of the page that you are loading the 3D Secure Component from. + /// > Required for browser-based (`channel` **Web**) 3D Secure 2 transactions.Set this to the origin URL of the page where you are rendering the Drop-in/Component. Do not include subdirectories and a trailing slash. /// - /// Required for the 3D Secure 2 `channel` **Web** integration. Set this parameter to the origin URL of the page that you are loading the 3D Secure Component from. + /// > Required for browser-based (`channel` **Web**) 3D Secure 2 transactions.Set this to the origin URL of the page where you are rendering the Drop-in/Component. Do not include subdirectories and a trailing slash. [DataMember(Name = "origin", EmitDefaultValue = false)] public string Origin { get; set; } @@ -424,9 +424,9 @@ protected DonationPaymentRequest() { } public string Reference { get; set; } /// - /// The URL to return to in case of a redirection. The format depends on the channel. This URL can have a maximum of 1024 characters. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. > The URL must not include personally identifiable information (PII), for example name or email address. + /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. We strongly recommend that you use a maximum of 1024 characters. > The URL must not include personally identifiable information (PII), for example name or email address. /// - /// The URL to return to in case of a redirection. The format depends on the channel. This URL can have a maximum of 1024 characters. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. > The URL must not include personally identifiable information (PII), for example name or email address. + /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. We strongly recommend that you use a maximum of 1024 characters. > The URL must not include personally identifiable information (PII), for example name or email address. [DataMember(Name = "returnUrl", IsRequired = false, EmitDefaultValue = false)] public string ReturnUrl { get; set; } @@ -438,16 +438,16 @@ protected DonationPaymentRequest() { } public string SessionValidity { get; set; } /// - /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > For 3D Secure 2 transactions, schemes require `shopperEmail` for all browser-based and mobile implementations. + /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > Required for Visa and JCB transactions that require 3D Secure 2 authentication if you did not include the `telephoneNumber`. /// - /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > For 3D Secure 2 transactions, schemes require `shopperEmail` for all browser-based and mobile implementations. + /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > Required for Visa and JCB transactions that require 3D Secure 2 authentication if you did not include the `telephoneNumber`. [DataMember(Name = "shopperEmail", EmitDefaultValue = false)] public string ShopperEmail { get; set; } /// - /// The shopper's IP address. In general, we recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks). > For 3D Secure 2 transactions, schemes require `shopperIP` for all browser-based implementations. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). + /// The shopper's IP address. We recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks).> Required for Visa and JCB transactions that require 3D Secure 2 authentication for all web and mobile integrations, if you did not include the `shopperEmail`. For native mobile integrations, the field is required to support cases where authentication is routed to the redirect flow. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). /// - /// The shopper's IP address. In general, we recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks). > For 3D Secure 2 transactions, schemes require `shopperIP` for all browser-based implementations. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). + /// The shopper's IP address. We recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks).> Required for Visa and JCB transactions that require 3D Secure 2 authentication for all web and mobile integrations, if you did not include the `shopperEmail`. For native mobile integrations, the field is required to support cases where authentication is routed to the redirect flow. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). [DataMember(Name = "shopperIP", EmitDefaultValue = false)] public string ShopperIP { get; set; } @@ -479,9 +479,9 @@ protected DonationPaymentRequest() { } public string SocialSecurityNumber { get; set; } /// - /// The shopper's telephone number. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication. /// - /// The shopper's telephone number. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication. [DataMember(Name = "telephoneNumber", EmitDefaultValue = false)] public string TelephoneNumber { get; set; } @@ -492,9 +492,9 @@ protected DonationPaymentRequest() { } public ThreeDS2RequestFields ThreeDS2RequestData { get; set; } /// - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorization.Default: **false**. /// - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorization.Default: **false**. [DataMember(Name = "threeDSAuthenticationOnly", EmitDefaultValue = false)] [Obsolete("Deprecated since Adyen Checkout API v69. Use `authenticationData.authenticationOnly` instead.")] public bool? ThreeDSAuthenticationOnly { get; set; } diff --git a/Adyen/Model/Checkout/Payment.cs b/Adyen/Model/Checkout/Payment.cs new file mode 100644 index 000000000..05d8a43c4 --- /dev/null +++ b/Adyen/Model/Checkout/Payment.cs @@ -0,0 +1,195 @@ +/* +* Adyen Checkout API +* +* +* The version of the OpenAPI document: 71 +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using System.ComponentModel.DataAnnotations; +using OpenAPIDateConverter = Adyen.ApiSerialization.OpenAPIDateConverter; + +namespace Adyen.Model.Checkout +{ + /// + /// Payment + /// + [DataContract(Name = "Payment")] + public partial class Payment : IEquatable, IValidatableObject + { + /// + /// The result of the payment. For more information, see [Result codes](https://docs.adyen.com/online-payments/payment-result-codes). Possible values: * **Authorised** – The payment was successfully authorised. This state serves as an indicator to proceed with the delivery of goods and services. This is a final state. + /// + /// The result of the payment. For more information, see [Result codes](https://docs.adyen.com/online-payments/payment-result-codes). Possible values: * **Authorised** – The payment was successfully authorised. This state serves as an indicator to proceed with the delivery of goods and services. This is a final state. + [JsonConverter(typeof(StringEnumConverter))] + public enum ResultCodeEnum + { + /// + /// Enum Authorised for value: Authorised + /// + [EnumMember(Value = "Authorised")] + Authorised = 1 + + } + + + /// + /// The result of the payment. For more information, see [Result codes](https://docs.adyen.com/online-payments/payment-result-codes). Possible values: * **Authorised** – The payment was successfully authorised. This state serves as an indicator to proceed with the delivery of goods and services. This is a final state. + /// + /// The result of the payment. For more information, see [Result codes](https://docs.adyen.com/online-payments/payment-result-codes). Possible values: * **Authorised** – The payment was successfully authorised. This state serves as an indicator to proceed with the delivery of goods and services. This is a final state. + [DataMember(Name = "resultCode", EmitDefaultValue = false)] + public ResultCodeEnum? ResultCode { get; set; } + /// + /// Initializes a new instance of the class. + /// + /// amount. + /// paymentMethod. + /// Adyen's 16-character reference associated with the transaction/request. This value is globally unique. Use this reference when you communicate with us about this request.. + /// The result of the payment. For more information, see [Result codes](https://docs.adyen.com/online-payments/payment-result-codes). Possible values: * **Authorised** – The payment was successfully authorised. This state serves as an indicator to proceed with the delivery of goods and services. This is a final state. . + public Payment(Amount amount = default(Amount), ResponsePaymentMethod paymentMethod = default(ResponsePaymentMethod), string pspReference = default(string), ResultCodeEnum? resultCode = default(ResultCodeEnum?)) + { + this.Amount = amount; + this.PaymentMethod = paymentMethod; + this.PspReference = pspReference; + this.ResultCode = resultCode; + } + + /// + /// Gets or Sets Amount + /// + [DataMember(Name = "amount", EmitDefaultValue = false)] + public Amount Amount { get; set; } + + /// + /// Gets or Sets PaymentMethod + /// + [DataMember(Name = "paymentMethod", EmitDefaultValue = false)] + public ResponsePaymentMethod PaymentMethod { get; set; } + + /// + /// Adyen's 16-character reference associated with the transaction/request. This value is globally unique. Use this reference when you communicate with us about this request. + /// + /// Adyen's 16-character reference associated with the transaction/request. This value is globally unique. Use this reference when you communicate with us about this request. + [DataMember(Name = "pspReference", EmitDefaultValue = false)] + public string PspReference { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class Payment {\n"); + sb.Append(" Amount: ").Append(Amount).Append("\n"); + sb.Append(" PaymentMethod: ").Append(PaymentMethod).Append("\n"); + sb.Append(" PspReference: ").Append(PspReference).Append("\n"); + sb.Append(" ResultCode: ").Append(ResultCode).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as Payment); + } + + /// + /// Returns true if Payment instances are equal + /// + /// Instance of Payment to be compared + /// Boolean + public bool Equals(Payment input) + { + if (input == null) + { + return false; + } + return + ( + this.Amount == input.Amount || + (this.Amount != null && + this.Amount.Equals(input.Amount)) + ) && + ( + this.PaymentMethod == input.PaymentMethod || + (this.PaymentMethod != null && + this.PaymentMethod.Equals(input.PaymentMethod)) + ) && + ( + this.PspReference == input.PspReference || + (this.PspReference != null && + this.PspReference.Equals(input.PspReference)) + ) && + ( + this.ResultCode == input.ResultCode || + this.ResultCode.Equals(input.ResultCode) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Amount != null) + { + hashCode = (hashCode * 59) + this.Amount.GetHashCode(); + } + if (this.PaymentMethod != null) + { + hashCode = (hashCode * 59) + this.PaymentMethod.GetHashCode(); + } + if (this.PspReference != null) + { + hashCode = (hashCode * 59) + this.PspReference.GetHashCode(); + } + hashCode = (hashCode * 59) + this.ResultCode.GetHashCode(); + return hashCode; + } + } + /// + /// To validate all properties of the instance + /// + /// Validation context + /// Validation Result + public IEnumerable Validate(ValidationContext validationContext) + { + yield break; + } + } + +} diff --git a/Adyen/Model/Checkout/PaymentAmountUpdateRequest.cs b/Adyen/Model/Checkout/PaymentAmountUpdateRequest.cs index 5aac0b522..61f584801 100644 --- a/Adyen/Model/Checkout/PaymentAmountUpdateRequest.cs +++ b/Adyen/Model/Checkout/PaymentAmountUpdateRequest.cs @@ -76,16 +76,18 @@ protected PaymentAmountUpdateRequest() { } /// /// amount (required). /// applicationInfo. + /// enhancedSchemeData. /// The reason for the amount update. Possible values: * **delayedCharge** * **noShow** * **installment**. /// Price and product information of the refunded items, required for [partial refunds](https://docs.adyen.com/online-payments/refund#refund-a-payment). > This field is required for partial refunds with 3x 4x Oney, Affirm, Afterpay, Atome, Clearpay, Klarna, Ratepay, Walley, and Zip.. /// The merchant account that is used to process the payment. (required). /// Your reference for the amount update request. Maximum length: 80 characters.. /// An array of objects specifying how the amount should be split between accounts when using Adyen for Platforms. For more information, see how to process payments for [marketplaces](https://docs.adyen.com/marketplaces/process-payments) or [platforms](https://docs.adyen.com/platforms/process-payments).. - public PaymentAmountUpdateRequest(Amount amount = default(Amount), ApplicationInfo applicationInfo = default(ApplicationInfo), IndustryUsageEnum? industryUsage = default(IndustryUsageEnum?), List lineItems = default(List), string merchantAccount = default(string), string reference = default(string), List splits = default(List)) + public PaymentAmountUpdateRequest(Amount amount = default(Amount), ApplicationInfo applicationInfo = default(ApplicationInfo), EnhancedSchemeData enhancedSchemeData = default(EnhancedSchemeData), IndustryUsageEnum? industryUsage = default(IndustryUsageEnum?), List lineItems = default(List), string merchantAccount = default(string), string reference = default(string), List splits = default(List)) { this.Amount = amount; this.MerchantAccount = merchantAccount; this.ApplicationInfo = applicationInfo; + this.EnhancedSchemeData = enhancedSchemeData; this.IndustryUsage = industryUsage; this.LineItems = lineItems; this.Reference = reference; @@ -104,6 +106,12 @@ protected PaymentAmountUpdateRequest() { } [DataMember(Name = "applicationInfo", EmitDefaultValue = false)] public ApplicationInfo ApplicationInfo { get; set; } + /// + /// Gets or Sets EnhancedSchemeData + /// + [DataMember(Name = "enhancedSchemeData", EmitDefaultValue = false)] + public EnhancedSchemeData EnhancedSchemeData { get; set; } + /// /// Price and product information of the refunded items, required for [partial refunds](https://docs.adyen.com/online-payments/refund#refund-a-payment). > This field is required for partial refunds with 3x 4x Oney, Affirm, Afterpay, Atome, Clearpay, Klarna, Ratepay, Walley, and Zip. /// @@ -142,6 +150,7 @@ public override string ToString() sb.Append("class PaymentAmountUpdateRequest {\n"); sb.Append(" Amount: ").Append(Amount).Append("\n"); sb.Append(" ApplicationInfo: ").Append(ApplicationInfo).Append("\n"); + sb.Append(" EnhancedSchemeData: ").Append(EnhancedSchemeData).Append("\n"); sb.Append(" IndustryUsage: ").Append(IndustryUsage).Append("\n"); sb.Append(" LineItems: ").Append(LineItems).Append("\n"); sb.Append(" MerchantAccount: ").Append(MerchantAccount).Append("\n"); @@ -192,6 +201,11 @@ public bool Equals(PaymentAmountUpdateRequest input) (this.ApplicationInfo != null && this.ApplicationInfo.Equals(input.ApplicationInfo)) ) && + ( + this.EnhancedSchemeData == input.EnhancedSchemeData || + (this.EnhancedSchemeData != null && + this.EnhancedSchemeData.Equals(input.EnhancedSchemeData)) + ) && ( this.IndustryUsage == input.IndustryUsage || this.IndustryUsage.Equals(input.IndustryUsage) @@ -237,6 +251,10 @@ public override int GetHashCode() { hashCode = (hashCode * 59) + this.ApplicationInfo.GetHashCode(); } + if (this.EnhancedSchemeData != null) + { + hashCode = (hashCode * 59) + this.EnhancedSchemeData.GetHashCode(); + } hashCode = (hashCode * 59) + this.IndustryUsage.GetHashCode(); if (this.LineItems != null) { diff --git a/Adyen/Model/Checkout/PaymentCancelRequest.cs b/Adyen/Model/Checkout/PaymentCancelRequest.cs index 3795b9a0f..f31c3bf0b 100644 --- a/Adyen/Model/Checkout/PaymentCancelRequest.cs +++ b/Adyen/Model/Checkout/PaymentCancelRequest.cs @@ -41,12 +41,14 @@ protected PaymentCancelRequest() { } /// Initializes a new instance of the class. /// /// applicationInfo. + /// enhancedSchemeData. /// The merchant account that is used to process the payment. (required). /// Your reference for the cancel request. Maximum length: 80 characters.. - public PaymentCancelRequest(ApplicationInfo applicationInfo = default(ApplicationInfo), string merchantAccount = default(string), string reference = default(string)) + public PaymentCancelRequest(ApplicationInfo applicationInfo = default(ApplicationInfo), EnhancedSchemeData enhancedSchemeData = default(EnhancedSchemeData), string merchantAccount = default(string), string reference = default(string)) { this.MerchantAccount = merchantAccount; this.ApplicationInfo = applicationInfo; + this.EnhancedSchemeData = enhancedSchemeData; this.Reference = reference; } @@ -56,6 +58,12 @@ protected PaymentCancelRequest() { } [DataMember(Name = "applicationInfo", EmitDefaultValue = false)] public ApplicationInfo ApplicationInfo { get; set; } + /// + /// Gets or Sets EnhancedSchemeData + /// + [DataMember(Name = "enhancedSchemeData", EmitDefaultValue = false)] + public EnhancedSchemeData EnhancedSchemeData { get; set; } + /// /// The merchant account that is used to process the payment. /// @@ -79,6 +87,7 @@ public override string ToString() StringBuilder sb = new StringBuilder(); sb.Append("class PaymentCancelRequest {\n"); sb.Append(" ApplicationInfo: ").Append(ApplicationInfo).Append("\n"); + sb.Append(" EnhancedSchemeData: ").Append(EnhancedSchemeData).Append("\n"); sb.Append(" MerchantAccount: ").Append(MerchantAccount).Append("\n"); sb.Append(" Reference: ").Append(Reference).Append("\n"); sb.Append("}\n"); @@ -121,6 +130,11 @@ public bool Equals(PaymentCancelRequest input) (this.ApplicationInfo != null && this.ApplicationInfo.Equals(input.ApplicationInfo)) ) && + ( + this.EnhancedSchemeData == input.EnhancedSchemeData || + (this.EnhancedSchemeData != null && + this.EnhancedSchemeData.Equals(input.EnhancedSchemeData)) + ) && ( this.MerchantAccount == input.MerchantAccount || (this.MerchantAccount != null && @@ -146,6 +160,10 @@ public override int GetHashCode() { hashCode = (hashCode * 59) + this.ApplicationInfo.GetHashCode(); } + if (this.EnhancedSchemeData != null) + { + hashCode = (hashCode * 59) + this.EnhancedSchemeData.GetHashCode(); + } if (this.MerchantAccount != null) { hashCode = (hashCode * 59) + this.MerchantAccount.GetHashCode(); diff --git a/Adyen/Model/Checkout/PaymentDetails.cs b/Adyen/Model/Checkout/PaymentDetails.cs index bbb87300a..cf6bec41d 100644 --- a/Adyen/Model/Checkout/PaymentDetails.cs +++ b/Adyen/Model/Checkout/PaymentDetails.cs @@ -285,281 +285,293 @@ public enum TypeEnum [EnumMember(Value = "molpay_fpx")] MolpayFpx = 41, + /// + /// Enum Payme for value: payme + /// + [EnumMember(Value = "payme")] + Payme = 42, + + /// + /// Enum PaymePos for value: payme_pos + /// + [EnumMember(Value = "payme_pos")] + PaymePos = 43, + /// /// Enum Konbini for value: konbini /// [EnumMember(Value = "konbini")] - Konbini = 42, + Konbini = 44, /// /// Enum DirectEbanking for value: directEbanking /// [EnumMember(Value = "directEbanking")] - DirectEbanking = 43, + DirectEbanking = 45, /// /// Enum Boletobancario for value: boletobancario /// [EnumMember(Value = "boletobancario")] - Boletobancario = 44, + Boletobancario = 46, /// /// Enum Neteller for value: neteller /// [EnumMember(Value = "neteller")] - Neteller = 45, + Neteller = 47, /// /// Enum Cashticket for value: cashticket /// [EnumMember(Value = "cashticket")] - Cashticket = 46, + Cashticket = 48, /// /// Enum Ikano for value: ikano /// [EnumMember(Value = "ikano")] - Ikano = 47, + Ikano = 49, /// /// Enum Karenmillen for value: karenmillen /// [EnumMember(Value = "karenmillen")] - Karenmillen = 48, + Karenmillen = 50, /// /// Enum Oasis for value: oasis /// [EnumMember(Value = "oasis")] - Oasis = 49, + Oasis = 51, /// /// Enum Warehouse for value: warehouse /// [EnumMember(Value = "warehouse")] - Warehouse = 50, + Warehouse = 52, /// /// Enum PrimeiropayBoleto for value: primeiropay_boleto /// [EnumMember(Value = "primeiropay_boleto")] - PrimeiropayBoleto = 51, + PrimeiropayBoleto = 53, /// /// Enum Mada for value: mada /// [EnumMember(Value = "mada")] - Mada = 52, + Mada = 54, /// /// Enum Benefit for value: benefit /// [EnumMember(Value = "benefit")] - Benefit = 53, + Benefit = 55, /// /// Enum Knet for value: knet /// [EnumMember(Value = "knet")] - Knet = 54, + Knet = 56, /// /// Enum Omannet for value: omannet /// [EnumMember(Value = "omannet")] - Omannet = 55, + Omannet = 57, /// /// Enum GopayWallet for value: gopay_wallet /// [EnumMember(Value = "gopay_wallet")] - GopayWallet = 56, + GopayWallet = 58, /// /// Enum KcpNaverpay for value: kcp_naverpay /// [EnumMember(Value = "kcp_naverpay")] - KcpNaverpay = 57, + KcpNaverpay = 59, /// /// Enum OnlinebankingIN for value: onlinebanking_IN /// [EnumMember(Value = "onlinebanking_IN")] - OnlinebankingIN = 58, + OnlinebankingIN = 60, /// /// Enum Fawry for value: fawry /// [EnumMember(Value = "fawry")] - Fawry = 59, + Fawry = 61, /// /// Enum Atome for value: atome /// [EnumMember(Value = "atome")] - Atome = 60, + Atome = 62, /// /// Enum Moneybookers for value: moneybookers /// [EnumMember(Value = "moneybookers")] - Moneybookers = 61, + Moneybookers = 63, /// /// Enum Naps for value: naps /// [EnumMember(Value = "naps")] - Naps = 62, + Naps = 64, /// /// Enum Nordea for value: nordea /// [EnumMember(Value = "nordea")] - Nordea = 63, + Nordea = 65, /// /// Enum BoletobancarioBradesco for value: boletobancario_bradesco /// [EnumMember(Value = "boletobancario_bradesco")] - BoletobancarioBradesco = 64, + BoletobancarioBradesco = 66, /// /// Enum BoletobancarioItau for value: boletobancario_itau /// [EnumMember(Value = "boletobancario_itau")] - BoletobancarioItau = 65, + BoletobancarioItau = 67, /// /// Enum BoletobancarioSantander for value: boletobancario_santander /// [EnumMember(Value = "boletobancario_santander")] - BoletobancarioSantander = 66, + BoletobancarioSantander = 68, /// /// Enum BoletobancarioBancodobrasil for value: boletobancario_bancodobrasil /// [EnumMember(Value = "boletobancario_bancodobrasil")] - BoletobancarioBancodobrasil = 67, + BoletobancarioBancodobrasil = 69, /// /// Enum BoletobancarioHsbc for value: boletobancario_hsbc /// [EnumMember(Value = "boletobancario_hsbc")] - BoletobancarioHsbc = 68, + BoletobancarioHsbc = 70, /// /// Enum MolpayMaybank2u for value: molpay_maybank2u /// [EnumMember(Value = "molpay_maybank2u")] - MolpayMaybank2u = 69, + MolpayMaybank2u = 71, /// /// Enum MolpayCimb for value: molpay_cimb /// [EnumMember(Value = "molpay_cimb")] - MolpayCimb = 70, + MolpayCimb = 72, /// /// Enum MolpayRhb for value: molpay_rhb /// [EnumMember(Value = "molpay_rhb")] - MolpayRhb = 71, + MolpayRhb = 73, /// /// Enum MolpayAmb for value: molpay_amb /// [EnumMember(Value = "molpay_amb")] - MolpayAmb = 72, + MolpayAmb = 74, /// /// Enum MolpayHlb for value: molpay_hlb /// [EnumMember(Value = "molpay_hlb")] - MolpayHlb = 73, + MolpayHlb = 75, /// /// Enum MolpayAffinEpg for value: molpay_affin_epg /// [EnumMember(Value = "molpay_affin_epg")] - MolpayAffinEpg = 74, + MolpayAffinEpg = 76, /// /// Enum MolpayBankislam for value: molpay_bankislam /// [EnumMember(Value = "molpay_bankislam")] - MolpayBankislam = 75, + MolpayBankislam = 77, /// /// Enum MolpayPublicbank for value: molpay_publicbank /// [EnumMember(Value = "molpay_publicbank")] - MolpayPublicbank = 76, + MolpayPublicbank = 78, /// /// Enum FpxAgrobank for value: fpx_agrobank /// [EnumMember(Value = "fpx_agrobank")] - FpxAgrobank = 77, + FpxAgrobank = 79, /// /// Enum Touchngo for value: touchngo /// [EnumMember(Value = "touchngo")] - Touchngo = 78, + Touchngo = 80, /// /// Enum Maybank2uMae for value: maybank2u_mae /// [EnumMember(Value = "maybank2u_mae")] - Maybank2uMae = 79, + Maybank2uMae = 81, /// /// Enum Duitnow for value: duitnow /// [EnumMember(Value = "duitnow")] - Duitnow = 80, + Duitnow = 82, /// /// Enum Promptpay for value: promptpay /// [EnumMember(Value = "promptpay")] - Promptpay = 81, + Promptpay = 83, /// /// Enum TwintPos for value: twint_pos /// [EnumMember(Value = "twint_pos")] - TwintPos = 82, + TwintPos = 84, /// /// Enum AlipayHk for value: alipay_hk /// [EnumMember(Value = "alipay_hk")] - AlipayHk = 83, + AlipayHk = 85, /// /// Enum AlipayHkWeb for value: alipay_hk_web /// [EnumMember(Value = "alipay_hk_web")] - AlipayHkWeb = 84, + AlipayHkWeb = 86, /// /// Enum AlipayHkWap for value: alipay_hk_wap /// [EnumMember(Value = "alipay_hk_wap")] - AlipayHkWap = 85, + AlipayHkWap = 87, /// /// Enum AlipayWap for value: alipay_wap /// [EnumMember(Value = "alipay_wap")] - AlipayWap = 86, + AlipayWap = 88, /// /// Enum Balanceplatform for value: balanceplatform /// [EnumMember(Value = "balanceplatform")] - Balanceplatform = 87 + Balanceplatform = 89 } diff --git a/Adyen/Model/Checkout/PaymentLinkRequest.cs b/Adyen/Model/Checkout/PaymentLinkRequest.cs index 2053acec1..b21f923cb 100644 --- a/Adyen/Model/Checkout/PaymentLinkRequest.cs +++ b/Adyen/Model/Checkout/PaymentLinkRequest.cs @@ -193,7 +193,7 @@ protected PaymentLinkRequest() { } /// An array of objects specifying how to split a payment when using [Adyen for Platforms](https://docs.adyen.com/platforms/process-payments#providing-split-information), [Classic Platforms integration](https://docs.adyen.com/classic-platforms/processing-payments#providing-split-information), or [Issuing](https://docs.adyen.com/issuing/manage-funds#split).. /// The physical store, for which this payment is processed.. /// Indicates if the details of the payment method will be stored for the shopper. Possible values: * **disabled** – No details will be stored (default). * **askForConsent** – If the `shopperReference` is provided, the UI lets the shopper choose if they want their payment details to be stored. * **enabled** – If the `shopperReference` is provided, the details will be stored without asking the shopper for consent. When set to **askForConsent** or **enabled**, you must also include the `recurringProcessingModel` parameter.. - /// The shopper's telephone number.. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication.. /// A [theme](https://docs.adyen.com/unified-commerce/pay-by-link/payment-links/api#themes) to customize the appearance of the payment page. If not specified, the payment page is rendered according to the theme set as default in your Customer Area.. /// threeDS2RequestData. public PaymentLinkRequest(List allowedPaymentMethods = default(List), Amount amount = default(Amount), ApplicationInfo applicationInfo = default(ApplicationInfo), Address billingAddress = default(Address), List blockedPaymentMethods = default(List), int? captureDelayHours = default(int?), string countryCode = default(string), DateTime dateOfBirth = default(DateTime), DateTime deliverAt = default(DateTime), Address deliveryAddress = default(Address), string description = default(string), DateTime expiresAt = default(DateTime), FundOrigin fundOrigin = default(FundOrigin), FundRecipient fundRecipient = default(FundRecipient), Dictionary installmentOptions = default(Dictionary), List lineItems = default(List), bool? manualCapture = default(bool?), string mcc = default(string), string merchantAccount = default(string), string merchantOrderReference = default(string), Dictionary metadata = default(Dictionary), PlatformChargebackLogic platformChargebackLogic = default(PlatformChargebackLogic), RecurringProcessingModelEnum? recurringProcessingModel = default(RecurringProcessingModelEnum?), string reference = default(string), List requiredShopperFields = default(List), string returnUrl = default(string), bool? reusable = default(bool?), RiskData riskData = default(RiskData), string shopperEmail = default(string), string shopperLocale = default(string), Name shopperName = default(Name), string shopperReference = default(string), string shopperStatement = default(string), bool? showRemovePaymentMethodButton = true, string socialSecurityNumber = default(string), bool? splitCardFundingSources = false, List splits = default(List), string store = default(string), StorePaymentMethodModeEnum? storePaymentMethodMode = default(StorePaymentMethodModeEnum?), string telephoneNumber = default(string), string themeId = default(string), CheckoutSessionThreeDS2RequestData threeDS2RequestData = default(CheckoutSessionThreeDS2RequestData)) @@ -487,9 +487,9 @@ protected PaymentLinkRequest() { } public string Store { get; set; } /// - /// The shopper's telephone number. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication. /// - /// The shopper's telephone number. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication. [DataMember(Name = "telephoneNumber", EmitDefaultValue = false)] public string TelephoneNumber { get; set; } diff --git a/Adyen/Model/Checkout/PaymentLinkResponse.cs b/Adyen/Model/Checkout/PaymentLinkResponse.cs index ad21bf01e..6895075ad 100644 --- a/Adyen/Model/Checkout/PaymentLinkResponse.cs +++ b/Adyen/Model/Checkout/PaymentLinkResponse.cs @@ -240,7 +240,7 @@ protected PaymentLinkResponse() { } /// Status of the payment link. Possible values: * **active**: The link can be used to make payments. * **expired**: The expiry date for the payment link has passed. Shoppers can no longer use the link to make payments. * **completed**: The shopper completed the payment. * **paymentPending**: The shopper is in the process of making the payment. Applies to payment methods with an asynchronous flow. (required). /// The physical store, for which this payment is processed.. /// Indicates if the details of the payment method will be stored for the shopper. Possible values: * **disabled** – No details will be stored (default). * **askForConsent** – If the `shopperReference` is provided, the UI lets the shopper choose if they want their payment details to be stored. * **enabled** – If the `shopperReference` is provided, the details will be stored without asking the shopper for consent. When set to **askForConsent** or **enabled**, you must also include the `recurringProcessingModel` parameter.. - /// The shopper's telephone number.. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication.. /// A [theme](https://docs.adyen.com/unified-commerce/pay-by-link/payment-links/api#themes) to customize the appearance of the payment page. If not specified, the payment page is rendered according to the theme set as default in your Customer Area.. /// threeDS2RequestData. /// The date when the payment link status was updated. [ISO 8601](https://www.w3.org/TR/NOTE-datetime) format: YYYY-MM-DDThh:mm:ss+TZD, for example, **2020-12-18T10:15:30+01:00**.. @@ -544,9 +544,9 @@ protected PaymentLinkResponse() { } public string Store { get; set; } /// - /// The shopper's telephone number. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication. /// - /// The shopper's telephone number. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication. [DataMember(Name = "telephoneNumber", EmitDefaultValue = false)] public string TelephoneNumber { get; set; } diff --git a/Adyen/Model/Checkout/PaymentMethod.cs b/Adyen/Model/Checkout/PaymentMethod.cs index 2e34268ad..14a47b70b 100644 --- a/Adyen/Model/Checkout/PaymentMethod.cs +++ b/Adyen/Model/Checkout/PaymentMethod.cs @@ -72,8 +72,9 @@ public enum FundingSourceEnum /// All input details to be provided to complete the payment with this payment method.. /// A list of issuers for this payment method.. /// The displayable name of this payment method.. + /// Indicates whether this payment method should be promoted or not.. /// The unique payment method code.. - public PaymentMethod(List apps = default(List), string brand = default(string), List brands = default(List), Dictionary configuration = default(Dictionary), FundingSourceEnum? fundingSource = default(FundingSourceEnum?), PaymentMethodGroup group = default(PaymentMethodGroup), List inputDetails = default(List), List issuers = default(List), string name = default(string), string type = default(string)) + public PaymentMethod(List apps = default(List), string brand = default(string), List brands = default(List), Dictionary configuration = default(Dictionary), FundingSourceEnum? fundingSource = default(FundingSourceEnum?), PaymentMethodGroup group = default(PaymentMethodGroup), List inputDetails = default(List), List issuers = default(List), string name = default(string), bool? promoted = default(bool?), string type = default(string)) { this.Apps = apps; this.Brand = brand; @@ -84,6 +85,7 @@ public enum FundingSourceEnum this.InputDetails = inputDetails; this.Issuers = issuers; this.Name = name; + this.Promoted = promoted; this.Type = type; } @@ -143,6 +145,13 @@ public enum FundingSourceEnum [DataMember(Name = "name", EmitDefaultValue = false)] public string Name { get; set; } + /// + /// Indicates whether this payment method should be promoted or not. + /// + /// Indicates whether this payment method should be promoted or not. + [DataMember(Name = "promoted", EmitDefaultValue = false)] + public bool? Promoted { get; set; } + /// /// The unique payment method code. /// @@ -167,6 +176,7 @@ public override string ToString() sb.Append(" InputDetails: ").Append(InputDetails).Append("\n"); sb.Append(" Issuers: ").Append(Issuers).Append("\n"); sb.Append(" Name: ").Append(Name).Append("\n"); + sb.Append(" Promoted: ").Append(Promoted).Append("\n"); sb.Append(" Type: ").Append(Type).Append("\n"); sb.Append("}\n"); return sb.ToString(); @@ -252,6 +262,10 @@ public bool Equals(PaymentMethod input) (this.Name != null && this.Name.Equals(input.Name)) ) && + ( + this.Promoted == input.Promoted || + this.Promoted.Equals(input.Promoted) + ) && ( this.Type == input.Type || (this.Type != null && @@ -301,6 +315,7 @@ public override int GetHashCode() { hashCode = (hashCode * 59) + this.Name.GetHashCode(); } + hashCode = (hashCode * 59) + this.Promoted.GetHashCode(); if (this.Type != null) { hashCode = (hashCode * 59) + this.Type.GetHashCode(); diff --git a/Adyen/Model/Checkout/PaymentMethodsRequest.cs b/Adyen/Model/Checkout/PaymentMethodsRequest.cs index 5ea97d524..19f498a3a 100644 --- a/Adyen/Model/Checkout/PaymentMethodsRequest.cs +++ b/Adyen/Model/Checkout/PaymentMethodsRequest.cs @@ -112,32 +112,40 @@ protected PaymentMethodsRequest() { } /// List of payment methods to be presented to the shopper. To refer to payment methods, use their [payment method type](https://docs.adyen.com/payment-methods/payment-method-types). Example: `\"allowedPaymentMethods\":[\"ideal\",\"applepay\"]`. /// amount. /// List of payment methods to be hidden from the shopper. To refer to payment methods, use their [payment method type](https://docs.adyen.com/payment-methods/payment-method-types). Example: `\"blockedPaymentMethods\":[\"ideal\",\"applepay\"]`. + /// browserInfo. /// The platform where a payment transaction takes place. This field can be used for filtering out payment methods that are only available on specific platforms. Possible values: * iOS * Android * Web. /// The shopper's country code.. /// The merchant account identifier, with which you want to process the transaction. (required). /// order. /// A unique ID that can be used to associate `/paymentMethods` and `/payments` requests with the same shopper transaction, offering insights into conversion rates.. + /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > Required for Visa and JCB transactions that require 3D Secure 2 authentication if you did not include the `telephoneNumber`.. + /// The shopper's IP address. We recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks).> Required for Visa and JCB transactions that require 3D Secure 2 authentication for all web and mobile integrations, if you did not include the `shopperEmail`. For native mobile integrations, the field is required to support cases where authentication is routed to the redirect flow. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new).. /// The combination of a language code and a country code to specify the language to be used in the payment.. /// Required for recurring payments. Your reference to uniquely identify this shopper, for example user ID or account ID. The value is case-sensitive and must be at least three characters. > Your reference must not include personally identifiable information (PII) such as name or email address.. /// Boolean value indicating whether the card payment method should be split into separate debit and credit options. (default to false). /// Required for Adyen for Platforms integrations if you are a platform model. This is your [reference](https://docs.adyen.com/api-explorer/Management/3/post/merchants/(merchantId)/stores#request-reference) (on [balance platform](https://docs.adyen.com/platforms)) or the [storeReference](https://docs.adyen.com/api-explorer/Account/latest/post/updateAccountHolder#request-accountHolderDetails-storeDetails-storeReference) (in the [classic integration](https://docs.adyen.com/classic-platforms/processing-payments/route-payment-to-store/#route-a-payment-to-a-store)) for the ecommerce or point-of-sale store that is processing the payment.. /// Specifies how payment methods should be filtered based on the 'store' parameter: - 'exclusive': Only payment methods belonging to the specified 'store' are returned. - 'inclusive': Payment methods from the 'store' and those not associated with any other store are returned.. - public PaymentMethodsRequest(Dictionary additionalData = default(Dictionary), List allowedPaymentMethods = default(List), Amount amount = default(Amount), List blockedPaymentMethods = default(List), ChannelEnum? channel = default(ChannelEnum?), string countryCode = default(string), string merchantAccount = default(string), EncryptedOrderData order = default(EncryptedOrderData), string shopperConversionId = default(string), string shopperLocale = default(string), string shopperReference = default(string), bool? splitCardFundingSources = false, string store = default(string), StoreFiltrationModeEnum? storeFiltrationMode = default(StoreFiltrationModeEnum?)) + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication.. + public PaymentMethodsRequest(Dictionary additionalData = default(Dictionary), List allowedPaymentMethods = default(List), Amount amount = default(Amount), List blockedPaymentMethods = default(List), BrowserInfo browserInfo = default(BrowserInfo), ChannelEnum? channel = default(ChannelEnum?), string countryCode = default(string), string merchantAccount = default(string), EncryptedOrderData order = default(EncryptedOrderData), string shopperConversionId = default(string), string shopperEmail = default(string), string shopperIP = default(string), string shopperLocale = default(string), string shopperReference = default(string), bool? splitCardFundingSources = false, string store = default(string), StoreFiltrationModeEnum? storeFiltrationMode = default(StoreFiltrationModeEnum?), string telephoneNumber = default(string)) { this.MerchantAccount = merchantAccount; this.AdditionalData = additionalData; this.AllowedPaymentMethods = allowedPaymentMethods; this.Amount = amount; this.BlockedPaymentMethods = blockedPaymentMethods; + this.BrowserInfo = browserInfo; this.Channel = channel; this.CountryCode = countryCode; this.Order = order; this.ShopperConversionId = shopperConversionId; + this.ShopperEmail = shopperEmail; + this.ShopperIP = shopperIP; this.ShopperLocale = shopperLocale; this.ShopperReference = shopperReference; this.SplitCardFundingSources = splitCardFundingSources; this.Store = store; this.StoreFiltrationMode = storeFiltrationMode; + this.TelephoneNumber = telephoneNumber; } /// @@ -167,6 +175,12 @@ protected PaymentMethodsRequest() { } [DataMember(Name = "blockedPaymentMethods", EmitDefaultValue = false)] public List BlockedPaymentMethods { get; set; } + /// + /// Gets or Sets BrowserInfo + /// + [DataMember(Name = "browserInfo", EmitDefaultValue = false)] + public BrowserInfo BrowserInfo { get; set; } + /// /// The shopper's country code. /// @@ -194,6 +208,20 @@ protected PaymentMethodsRequest() { } [DataMember(Name = "shopperConversionId", EmitDefaultValue = false)] public string ShopperConversionId { get; set; } + /// + /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > Required for Visa and JCB transactions that require 3D Secure 2 authentication if you did not include the `telephoneNumber`. + /// + /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > Required for Visa and JCB transactions that require 3D Secure 2 authentication if you did not include the `telephoneNumber`. + [DataMember(Name = "shopperEmail", EmitDefaultValue = false)] + public string ShopperEmail { get; set; } + + /// + /// The shopper's IP address. We recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks).> Required for Visa and JCB transactions that require 3D Secure 2 authentication for all web and mobile integrations, if you did not include the `shopperEmail`. For native mobile integrations, the field is required to support cases where authentication is routed to the redirect flow. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). + /// + /// The shopper's IP address. We recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks).> Required for Visa and JCB transactions that require 3D Secure 2 authentication for all web and mobile integrations, if you did not include the `shopperEmail`. For native mobile integrations, the field is required to support cases where authentication is routed to the redirect flow. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). + [DataMember(Name = "shopperIP", EmitDefaultValue = false)] + public string ShopperIP { get; set; } + /// /// The combination of a language code and a country code to specify the language to be used in the payment. /// @@ -222,6 +250,13 @@ protected PaymentMethodsRequest() { } [DataMember(Name = "store", EmitDefaultValue = false)] public string Store { get; set; } + /// + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication. + /// + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication. + [DataMember(Name = "telephoneNumber", EmitDefaultValue = false)] + public string TelephoneNumber { get; set; } + /// /// Returns the string presentation of the object /// @@ -234,16 +269,20 @@ public override string ToString() sb.Append(" AllowedPaymentMethods: ").Append(AllowedPaymentMethods).Append("\n"); sb.Append(" Amount: ").Append(Amount).Append("\n"); sb.Append(" BlockedPaymentMethods: ").Append(BlockedPaymentMethods).Append("\n"); + sb.Append(" BrowserInfo: ").Append(BrowserInfo).Append("\n"); sb.Append(" Channel: ").Append(Channel).Append("\n"); sb.Append(" CountryCode: ").Append(CountryCode).Append("\n"); sb.Append(" MerchantAccount: ").Append(MerchantAccount).Append("\n"); sb.Append(" Order: ").Append(Order).Append("\n"); sb.Append(" ShopperConversionId: ").Append(ShopperConversionId).Append("\n"); + sb.Append(" ShopperEmail: ").Append(ShopperEmail).Append("\n"); + sb.Append(" ShopperIP: ").Append(ShopperIP).Append("\n"); sb.Append(" ShopperLocale: ").Append(ShopperLocale).Append("\n"); sb.Append(" ShopperReference: ").Append(ShopperReference).Append("\n"); sb.Append(" SplitCardFundingSources: ").Append(SplitCardFundingSources).Append("\n"); sb.Append(" Store: ").Append(Store).Append("\n"); sb.Append(" StoreFiltrationMode: ").Append(StoreFiltrationMode).Append("\n"); + sb.Append(" TelephoneNumber: ").Append(TelephoneNumber).Append("\n"); sb.Append("}\n"); return sb.ToString(); } @@ -302,6 +341,11 @@ public bool Equals(PaymentMethodsRequest input) input.BlockedPaymentMethods != null && this.BlockedPaymentMethods.SequenceEqual(input.BlockedPaymentMethods) ) && + ( + this.BrowserInfo == input.BrowserInfo || + (this.BrowserInfo != null && + this.BrowserInfo.Equals(input.BrowserInfo)) + ) && ( this.Channel == input.Channel || this.Channel.Equals(input.Channel) @@ -326,6 +370,16 @@ public bool Equals(PaymentMethodsRequest input) (this.ShopperConversionId != null && this.ShopperConversionId.Equals(input.ShopperConversionId)) ) && + ( + this.ShopperEmail == input.ShopperEmail || + (this.ShopperEmail != null && + this.ShopperEmail.Equals(input.ShopperEmail)) + ) && + ( + this.ShopperIP == input.ShopperIP || + (this.ShopperIP != null && + this.ShopperIP.Equals(input.ShopperIP)) + ) && ( this.ShopperLocale == input.ShopperLocale || (this.ShopperLocale != null && @@ -348,6 +402,11 @@ public bool Equals(PaymentMethodsRequest input) ( this.StoreFiltrationMode == input.StoreFiltrationMode || this.StoreFiltrationMode.Equals(input.StoreFiltrationMode) + ) && + ( + this.TelephoneNumber == input.TelephoneNumber || + (this.TelephoneNumber != null && + this.TelephoneNumber.Equals(input.TelephoneNumber)) ); } @@ -376,6 +435,10 @@ public override int GetHashCode() { hashCode = (hashCode * 59) + this.BlockedPaymentMethods.GetHashCode(); } + if (this.BrowserInfo != null) + { + hashCode = (hashCode * 59) + this.BrowserInfo.GetHashCode(); + } hashCode = (hashCode * 59) + this.Channel.GetHashCode(); if (this.CountryCode != null) { @@ -393,6 +456,14 @@ public override int GetHashCode() { hashCode = (hashCode * 59) + this.ShopperConversionId.GetHashCode(); } + if (this.ShopperEmail != null) + { + hashCode = (hashCode * 59) + this.ShopperEmail.GetHashCode(); + } + if (this.ShopperIP != null) + { + hashCode = (hashCode * 59) + this.ShopperIP.GetHashCode(); + } if (this.ShopperLocale != null) { hashCode = (hashCode * 59) + this.ShopperLocale.GetHashCode(); @@ -407,6 +478,10 @@ public override int GetHashCode() hashCode = (hashCode * 59) + this.Store.GetHashCode(); } hashCode = (hashCode * 59) + this.StoreFiltrationMode.GetHashCode(); + if (this.TelephoneNumber != null) + { + hashCode = (hashCode * 59) + this.TelephoneNumber.GetHashCode(); + } return hashCode; } } diff --git a/Adyen/Model/Checkout/PaymentRefundRequest.cs b/Adyen/Model/Checkout/PaymentRefundRequest.cs index 646667bb5..a5652d910 100644 --- a/Adyen/Model/Checkout/PaymentRefundRequest.cs +++ b/Adyen/Model/Checkout/PaymentRefundRequest.cs @@ -88,17 +88,21 @@ protected PaymentRefundRequest() { } /// /// amount (required). /// applicationInfo. + /// This is only available for PayPal refunds. The [`pspReference`](https://docs.adyen.com/api-explorer/#/CheckoutService/latest/post/payments__resParam_pspReference) of the specific capture to refund.. + /// enhancedSchemeData. /// Price and product information of the refunded items, required for [partial refunds](https://docs.adyen.com/online-payments/refund#refund-a-payment). > This field is required for partial refunds with 3x 4x Oney, Affirm, Afterpay, Atome, Clearpay, Klarna, Ratepay, Walley, and Zip.. /// The merchant account that is used to process the payment. (required). /// The reason for the refund request. Possible values: * **FRAUD** * **CUSTOMER REQUEST** * **RETURN** * **DUPLICATE** * **OTHER** . /// Your reference for the refund request. Maximum length: 80 characters.. /// An array of objects specifying how the amount should be split between accounts when using Adyen for Platforms. For more information, see how to process payments for [marketplaces](https://docs.adyen.com/marketplaces/split-payments) or [platforms](https://docs.adyen.com/platforms/online-payments/split-payments/).. /// The online store or [physical store](https://docs.adyen.com/point-of-sale/design-your-integration/determine-account-structure/#create-stores) that is processing the refund. This must be the same as the store name configured in your Customer Area. Otherwise, you get an error and the refund fails.. - public PaymentRefundRequest(Amount amount = default(Amount), ApplicationInfo applicationInfo = default(ApplicationInfo), List lineItems = default(List), string merchantAccount = default(string), MerchantRefundReasonEnum? merchantRefundReason = default(MerchantRefundReasonEnum?), string reference = default(string), List splits = default(List), string store = default(string)) + public PaymentRefundRequest(Amount amount = default(Amount), ApplicationInfo applicationInfo = default(ApplicationInfo), string capturePspReference = default(string), EnhancedSchemeData enhancedSchemeData = default(EnhancedSchemeData), List lineItems = default(List), string merchantAccount = default(string), MerchantRefundReasonEnum? merchantRefundReason = default(MerchantRefundReasonEnum?), string reference = default(string), List splits = default(List), string store = default(string)) { this.Amount = amount; this.MerchantAccount = merchantAccount; this.ApplicationInfo = applicationInfo; + this.CapturePspReference = capturePspReference; + this.EnhancedSchemeData = enhancedSchemeData; this.LineItems = lineItems; this.MerchantRefundReason = merchantRefundReason; this.Reference = reference; @@ -118,6 +122,19 @@ protected PaymentRefundRequest() { } [DataMember(Name = "applicationInfo", EmitDefaultValue = false)] public ApplicationInfo ApplicationInfo { get; set; } + /// + /// This is only available for PayPal refunds. The [`pspReference`](https://docs.adyen.com/api-explorer/#/CheckoutService/latest/post/payments__resParam_pspReference) of the specific capture to refund. + /// + /// This is only available for PayPal refunds. The [`pspReference`](https://docs.adyen.com/api-explorer/#/CheckoutService/latest/post/payments__resParam_pspReference) of the specific capture to refund. + [DataMember(Name = "capturePspReference", EmitDefaultValue = false)] + public string CapturePspReference { get; set; } + + /// + /// Gets or Sets EnhancedSchemeData + /// + [DataMember(Name = "enhancedSchemeData", EmitDefaultValue = false)] + public EnhancedSchemeData EnhancedSchemeData { get; set; } + /// /// Price and product information of the refunded items, required for [partial refunds](https://docs.adyen.com/online-payments/refund#refund-a-payment). > This field is required for partial refunds with 3x 4x Oney, Affirm, Afterpay, Atome, Clearpay, Klarna, Ratepay, Walley, and Zip. /// @@ -163,6 +180,8 @@ public override string ToString() sb.Append("class PaymentRefundRequest {\n"); sb.Append(" Amount: ").Append(Amount).Append("\n"); sb.Append(" ApplicationInfo: ").Append(ApplicationInfo).Append("\n"); + sb.Append(" CapturePspReference: ").Append(CapturePspReference).Append("\n"); + sb.Append(" EnhancedSchemeData: ").Append(EnhancedSchemeData).Append("\n"); sb.Append(" LineItems: ").Append(LineItems).Append("\n"); sb.Append(" MerchantAccount: ").Append(MerchantAccount).Append("\n"); sb.Append(" MerchantRefundReason: ").Append(MerchantRefundReason).Append("\n"); @@ -214,6 +233,16 @@ public bool Equals(PaymentRefundRequest input) (this.ApplicationInfo != null && this.ApplicationInfo.Equals(input.ApplicationInfo)) ) && + ( + this.CapturePspReference == input.CapturePspReference || + (this.CapturePspReference != null && + this.CapturePspReference.Equals(input.CapturePspReference)) + ) && + ( + this.EnhancedSchemeData == input.EnhancedSchemeData || + (this.EnhancedSchemeData != null && + this.EnhancedSchemeData.Equals(input.EnhancedSchemeData)) + ) && ( this.LineItems == input.LineItems || this.LineItems != null && @@ -264,6 +293,14 @@ public override int GetHashCode() { hashCode = (hashCode * 59) + this.ApplicationInfo.GetHashCode(); } + if (this.CapturePspReference != null) + { + hashCode = (hashCode * 59) + this.CapturePspReference.GetHashCode(); + } + if (this.EnhancedSchemeData != null) + { + hashCode = (hashCode * 59) + this.EnhancedSchemeData.GetHashCode(); + } if (this.LineItems != null) { hashCode = (hashCode * 59) + this.LineItems.GetHashCode(); diff --git a/Adyen/Model/Checkout/PaymentRequest.cs b/Adyen/Model/Checkout/PaymentRequest.cs index 621c5c153..ddb2751e2 100644 --- a/Adyen/Model/Checkout/PaymentRequest.cs +++ b/Adyen/Model/Checkout/PaymentRequest.cs @@ -252,7 +252,7 @@ protected PaymentRequest() { } /// mpiData. /// order. /// When you are doing multiple partial (gift card) payments, this is the `pspReference` of the first payment. We use this to link the multiple payments to each other. As your own reference for linking multiple payments, use the `merchantOrderReference`instead.. - /// Required for the 3D Secure 2 `channel` **Web** integration. Set this parameter to the origin URL of the page that you are loading the 3D Secure Component from.. + /// > Required for browser-based (`channel` **Web**) 3D Secure 2 transactions.Set this to the origin URL of the page where you are rendering the Drop-in/Component. Do not include subdirectories and a trailing slash.. /// paymentMethod (required). /// platformChargebackLogic. /// Date after which no further authorisations shall be performed. Only for 3D Secure 2.. @@ -261,12 +261,12 @@ protected PaymentRequest() { } /// Specifies the redirect method (GET or POST) when redirecting back from the issuer.. /// Specifies the redirect method (GET or POST) when redirecting to the issuer.. /// The reference to uniquely identify a payment. This reference is used in all communication with you about the payment status. We recommend using a unique value per payment; however, it is not a requirement. If you need to provide multiple references for a transaction, separate them with hyphens (\"-\"). Maximum length: 80 characters. (required). - /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.example.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. > The URL must not include personally identifiable information (PII), for example name or email address. (required). + /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. We strongly recommend that you use a maximum of 1024 characters. > The URL must not include personally identifiable information (PII), for example name or email address. (required). /// riskData. /// The date and time until when the session remains valid, in [ISO 8601](https://www.w3.org/TR/NOTE-datetime) format. For example: 2020-07-18T15:42:40.428+01:00. /// A unique ID that can be used to associate `/paymentMethods` and `/payments` requests with the same shopper transaction, offering insights into conversion rates.. - /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > For 3D Secure 2 transactions, schemes require `shopperEmail` for all browser-based and mobile implementations.. - /// The shopper's IP address. In general, we recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks). > For 3D Secure 2 transactions, schemes require `shopperIP` for all browser-based implementations. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new).. + /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > Required for Visa and JCB transactions that require 3D Secure 2 authentication if you did not include the `telephoneNumber`.. + /// The shopper's IP address. We recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks).> Required for Visa and JCB transactions that require 3D Secure 2 authentication for all web and mobile integrations, if you did not include the `shopperEmail`. For native mobile integrations, the field is required to support cases where authentication is routed to the redirect flow. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new).. /// Specifies the sales channel, through which the shopper gives their card details, and whether the shopper is a returning customer. For the web service API, Adyen assumes Ecommerce shopper interaction by default. This field has the following possible values: * `Ecommerce` - Online transactions where the cardholder is present (online). For better authorisation rates, we recommend sending the card security code (CSC) along with the request. * `ContAuth` - Card on file and/or subscription transactions, where the cardholder is known to the merchant (returning customer). If the shopper is present (online), you can supply also the CSC to improve authorisation (one-click payment). * `Moto` - Mail-order and telephone-order transactions where the shopper is in contact with the merchant via email or telephone. * `POS` - Point-of-sale transactions where the shopper is physically present to make a payment using a secure payment terminal.. /// The combination of a language code and a country code to specify the language to be used in the payment.. /// shopperName. @@ -278,9 +278,9 @@ protected PaymentRequest() { } /// When true and `shopperReference` is provided, the payment details will be stored for future [recurring payments](https://docs.adyen.com/online-payments/tokenization/#recurring-payment-types).. /// This field contains additional information on the submerchant, who is onboarded to an acquirer through a payment facilitator or aggregator. /// surcharge. - /// The shopper's telephone number.. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication.. /// threeDS2RequestData. - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. (default to false). + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorisation.Default: **false**. (default to false). /// Set to true if the payment should be routed to a trusted MID.. public PaymentRequest(AccountInfo accountInfo = default(AccountInfo), Amount additionalAmount = default(Amount), Dictionary additionalData = default(Dictionary), Amount amount = default(Amount), ApplicationInfo applicationInfo = default(ApplicationInfo), AuthenticationData authenticationData = default(AuthenticationData), CheckoutBankAccount bankAccount = default(CheckoutBankAccount), BillingAddress billingAddress = default(BillingAddress), BrowserInfo browserInfo = default(BrowserInfo), int? captureDelayHours = default(int?), ChannelEnum? channel = default(ChannelEnum?), string checkoutAttemptId = default(string), Company company = default(Company), string conversionId = default(string), string countryCode = default(string), DateTime dateOfBirth = default(DateTime), ForexQuote dccQuote = default(ForexQuote), DateTime deliverAt = default(DateTime), DeliveryAddress deliveryAddress = default(DeliveryAddress), DateTime deliveryDate = default(DateTime), string deviceFingerprint = default(string), bool? enableOneClick = default(bool?), bool? enablePayOut = default(bool?), bool? enableRecurring = default(bool?), EnhancedSchemeData enhancedSchemeData = default(EnhancedSchemeData), EntityTypeEnum? entityType = default(EntityTypeEnum?), int? fraudOffset = default(int?), FundOrigin fundOrigin = default(FundOrigin), FundRecipient fundRecipient = default(FundRecipient), IndustryUsageEnum? industryUsage = default(IndustryUsageEnum?), Installments installments = default(Installments), List lineItems = default(List), Dictionary localizedShopperStatement = default(Dictionary), Mandate mandate = default(Mandate), string mcc = default(string), string merchantAccount = default(string), string merchantOrderReference = default(string), MerchantRiskIndicator merchantRiskIndicator = default(MerchantRiskIndicator), Dictionary metadata = default(Dictionary), ThreeDSecureData mpiData = default(ThreeDSecureData), EncryptedOrderData order = default(EncryptedOrderData), string orderReference = default(string), string origin = default(string), CheckoutPaymentMethod paymentMethod = default(CheckoutPaymentMethod), PlatformChargebackLogic platformChargebackLogic = default(PlatformChargebackLogic), string recurringExpiry = default(string), string recurringFrequency = default(string), RecurringProcessingModelEnum? recurringProcessingModel = default(RecurringProcessingModelEnum?), string redirectFromIssuerMethod = default(string), string redirectToIssuerMethod = default(string), string reference = default(string), string returnUrl = default(string), RiskData riskData = default(RiskData), string sessionValidity = default(string), string shopperConversionId = default(string), string shopperEmail = default(string), string shopperIP = default(string), ShopperInteractionEnum? shopperInteraction = default(ShopperInteractionEnum?), string shopperLocale = default(string), Name shopperName = default(Name), string shopperReference = default(string), string shopperStatement = default(string), string socialSecurityNumber = default(string), List splits = default(List), string store = default(string), bool? storePaymentMethod = default(bool?), List subMerchants = default(List), Surcharge surcharge = default(Surcharge), string telephoneNumber = default(string), ThreeDS2RequestFields threeDS2RequestData = default(ThreeDS2RequestFields), bool? threeDSAuthenticationOnly = false, bool? trustedShopper = default(bool?)) { @@ -615,9 +615,9 @@ protected PaymentRequest() { } public string OrderReference { get; set; } /// - /// Required for the 3D Secure 2 `channel` **Web** integration. Set this parameter to the origin URL of the page that you are loading the 3D Secure Component from. + /// > Required for browser-based (`channel` **Web**) 3D Secure 2 transactions.Set this to the origin URL of the page where you are rendering the Drop-in/Component. Do not include subdirectories and a trailing slash. /// - /// Required for the 3D Secure 2 `channel` **Web** integration. Set this parameter to the origin URL of the page that you are loading the 3D Secure Component from. + /// > Required for browser-based (`channel` **Web**) 3D Secure 2 transactions.Set this to the origin URL of the page where you are rendering the Drop-in/Component. Do not include subdirectories and a trailing slash. [DataMember(Name = "origin", EmitDefaultValue = false)] public string Origin { get; set; } @@ -669,9 +669,9 @@ protected PaymentRequest() { } public string Reference { get; set; } /// - /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.example.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. > The URL must not include personally identifiable information (PII), for example name or email address. + /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. We strongly recommend that you use a maximum of 1024 characters. > The URL must not include personally identifiable information (PII), for example name or email address. /// - /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.example.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. > The URL must not include personally identifiable information (PII), for example name or email address. + /// The URL to return to in case of a redirection. The format depends on the channel. * For web, include the protocol `http://` or `https://`. You can also include your own additional query parameters, for example, shopper ID or order reference number. Example: `https://your-company.com/checkout?shopperOrder=12xy` * For iOS, use the custom URL for your app. To know more about setting custom URL schemes, refer to the [Apple Developer documentation](https://developer.apple.com/documentation/uikit/inter-process_communication/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app). Example: `my-app://` * For Android, use a custom URL handled by an Activity on your app. You can configure it with an [intent filter](https://developer.android.com/guide/components/intents-filters). Example: `my-app://your.package.name` If the URL to return to includes non-ASCII characters, like spaces or special letters, URL encode the value. We strongly recommend that you use a maximum of 1024 characters. > The URL must not include personally identifiable information (PII), for example name or email address. [DataMember(Name = "returnUrl", IsRequired = false, EmitDefaultValue = false)] public string ReturnUrl { get; set; } @@ -696,16 +696,16 @@ protected PaymentRequest() { } public string ShopperConversionId { get; set; } /// - /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > For 3D Secure 2 transactions, schemes require `shopperEmail` for all browser-based and mobile implementations. + /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > Required for Visa and JCB transactions that require 3D Secure 2 authentication if you did not include the `telephoneNumber`. /// - /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > For 3D Secure 2 transactions, schemes require `shopperEmail` for all browser-based and mobile implementations. + /// The shopper's email address. We recommend that you provide this data, as it is used in velocity fraud checks. > Required for Visa and JCB transactions that require 3D Secure 2 authentication if you did not include the `telephoneNumber`. [DataMember(Name = "shopperEmail", EmitDefaultValue = false)] public string ShopperEmail { get; set; } /// - /// The shopper's IP address. In general, we recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks). > For 3D Secure 2 transactions, schemes require `shopperIP` for all browser-based implementations. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). + /// The shopper's IP address. We recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks).> Required for Visa and JCB transactions that require 3D Secure 2 authentication for all web and mobile integrations, if you did not include the `shopperEmail`. For native mobile integrations, the field is required to support cases where authentication is routed to the redirect flow. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). /// - /// The shopper's IP address. In general, we recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks). > For 3D Secure 2 transactions, schemes require `shopperIP` for all browser-based implementations. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). + /// The shopper's IP address. We recommend that you provide this data, as it is used in a number of risk checks (for instance, number of payment attempts or location-based checks).> Required for Visa and JCB transactions that require 3D Secure 2 authentication for all web and mobile integrations, if you did not include the `shopperEmail`. For native mobile integrations, the field is required to support cases where authentication is routed to the redirect flow. This field is also mandatory for some merchants depending on your business model. For more information, [contact Support](https://www.adyen.help/hc/en-us/requests/new). [DataMember(Name = "shopperIP", EmitDefaultValue = false)] public string ShopperIP { get; set; } @@ -778,9 +778,9 @@ protected PaymentRequest() { } public Surcharge Surcharge { get; set; } /// - /// The shopper's telephone number. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication. /// - /// The shopper's telephone number. + /// The shopper's telephone number. > Required for Visa and JCB transactions that require 3D Secure 2 authentication, if you did not include the `shopperEmail`. The phone number must include a plus sign (+) and a country code (1-3 digits), followed by the number (4-15 digits). If the value you provide does not follow the guidelines, we drop the value and do not submit it for authentication. [DataMember(Name = "telephoneNumber", EmitDefaultValue = false)] public string TelephoneNumber { get; set; } @@ -791,9 +791,9 @@ protected PaymentRequest() { } public ThreeDS2RequestFields ThreeDS2RequestData { get; set; } /// - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorisation.Default: **false**. /// - /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. + /// Required to trigger the [authentication-only flow](https://docs.adyen.com/online-payments/3d-secure/authentication-only/). If set to **true**, you will only perform the 3D Secure 2 authentication, and will not proceed to the payment authorisation.Default: **false**. [DataMember(Name = "threeDSAuthenticationOnly", EmitDefaultValue = false)] [Obsolete("Deprecated since Adyen Checkout API v69. Use `authenticationData.authenticationOnly` instead.")] public bool? ThreeDSAuthenticationOnly { get; set; } @@ -1573,6 +1573,12 @@ public override int GetHashCode() yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for Origin, length must be less than 80.", new [] { "Origin" }); } + // ReturnUrl (string) maxLength + if (this.ReturnUrl != null && this.ReturnUrl.Length > 8000) + { + yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for ReturnUrl, length must be less than 8000.", new [] { "ReturnUrl" }); + } + // ShopperConversionId (string) maxLength if (this.ShopperConversionId != null && this.ShopperConversionId.Length > 256) { diff --git a/Adyen/Model/Checkout/PaymentReversalRequest.cs b/Adyen/Model/Checkout/PaymentReversalRequest.cs index d68e1eacd..7202748ad 100644 --- a/Adyen/Model/Checkout/PaymentReversalRequest.cs +++ b/Adyen/Model/Checkout/PaymentReversalRequest.cs @@ -41,12 +41,14 @@ protected PaymentReversalRequest() { } /// Initializes a new instance of the class. /// /// applicationInfo. + /// enhancedSchemeData. /// The merchant account that is used to process the payment. (required). /// Your reference for the reversal request. Maximum length: 80 characters.. - public PaymentReversalRequest(ApplicationInfo applicationInfo = default(ApplicationInfo), string merchantAccount = default(string), string reference = default(string)) + public PaymentReversalRequest(ApplicationInfo applicationInfo = default(ApplicationInfo), EnhancedSchemeData enhancedSchemeData = default(EnhancedSchemeData), string merchantAccount = default(string), string reference = default(string)) { this.MerchantAccount = merchantAccount; this.ApplicationInfo = applicationInfo; + this.EnhancedSchemeData = enhancedSchemeData; this.Reference = reference; } @@ -56,6 +58,12 @@ protected PaymentReversalRequest() { } [DataMember(Name = "applicationInfo", EmitDefaultValue = false)] public ApplicationInfo ApplicationInfo { get; set; } + /// + /// Gets or Sets EnhancedSchemeData + /// + [DataMember(Name = "enhancedSchemeData", EmitDefaultValue = false)] + public EnhancedSchemeData EnhancedSchemeData { get; set; } + /// /// The merchant account that is used to process the payment. /// @@ -79,6 +87,7 @@ public override string ToString() StringBuilder sb = new StringBuilder(); sb.Append("class PaymentReversalRequest {\n"); sb.Append(" ApplicationInfo: ").Append(ApplicationInfo).Append("\n"); + sb.Append(" EnhancedSchemeData: ").Append(EnhancedSchemeData).Append("\n"); sb.Append(" MerchantAccount: ").Append(MerchantAccount).Append("\n"); sb.Append(" Reference: ").Append(Reference).Append("\n"); sb.Append("}\n"); @@ -121,6 +130,11 @@ public bool Equals(PaymentReversalRequest input) (this.ApplicationInfo != null && this.ApplicationInfo.Equals(input.ApplicationInfo)) ) && + ( + this.EnhancedSchemeData == input.EnhancedSchemeData || + (this.EnhancedSchemeData != null && + this.EnhancedSchemeData.Equals(input.EnhancedSchemeData)) + ) && ( this.MerchantAccount == input.MerchantAccount || (this.MerchantAccount != null && @@ -146,6 +160,10 @@ public override int GetHashCode() { hashCode = (hashCode * 59) + this.ApplicationInfo.GetHashCode(); } + if (this.EnhancedSchemeData != null) + { + hashCode = (hashCode * 59) + this.EnhancedSchemeData.GetHashCode(); + } if (this.MerchantAccount != null) { hashCode = (hashCode * 59) + this.MerchantAccount.GetHashCode(); diff --git a/Adyen/Model/Checkout/PixRecurring.cs b/Adyen/Model/Checkout/PixRecurring.cs index 6eca2f8c8..fd084618e 100644 --- a/Adyen/Model/Checkout/PixRecurring.cs +++ b/Adyen/Model/Checkout/PixRecurring.cs @@ -82,6 +82,7 @@ public enum FrequencyEnum /// Initializes a new instance of the class. /// /// The date on which the shopper's payment method will be charged, in YYYY-MM-DD format.. + /// Flag used to define whether liquidation can happen only on business days. /// End date of the billing plan, in YYYY-MM-DD format. The end date must align with the frequency and the start date of the billing plan. If left blank, the subscription will continue indefinitely unless it is cancelled by the shopper.. /// The frequency at which the shopper will be charged.. /// minAmount. @@ -90,9 +91,10 @@ public enum FrequencyEnum /// The text that that will be shown on the shopper's bank statement for the recurring payments. We recommend to add a descriptive text about the subscription to let your shoppers recognize your recurring payments. Maximum length: 35 characters.. /// When set to true, you can retry for failed recurring payments. The default value is true.. /// Start date of the billing plan, in YYYY-MM-DD format. The default value is the transaction date.. - public PixRecurring(string billingDate = default(string), string endsAt = default(string), FrequencyEnum? frequency = default(FrequencyEnum?), Amount minAmount = default(Amount), string originalPspReference = default(string), Amount recurringAmount = default(Amount), string recurringStatement = default(string), bool? retryPolicy = default(bool?), string startsAt = default(string)) + public PixRecurring(string billingDate = default(string), bool? businessDayOnly = default(bool?), string endsAt = default(string), FrequencyEnum? frequency = default(FrequencyEnum?), Amount minAmount = default(Amount), string originalPspReference = default(string), Amount recurringAmount = default(Amount), string recurringStatement = default(string), bool? retryPolicy = default(bool?), string startsAt = default(string)) { this.BillingDate = billingDate; + this.BusinessDayOnly = businessDayOnly; this.EndsAt = endsAt; this.Frequency = frequency; this.MinAmount = minAmount; @@ -110,6 +112,13 @@ public enum FrequencyEnum [DataMember(Name = "billingDate", EmitDefaultValue = false)] public string BillingDate { get; set; } + /// + /// Flag used to define whether liquidation can happen only on business days + /// + /// Flag used to define whether liquidation can happen only on business days + [DataMember(Name = "businessDayOnly", EmitDefaultValue = false)] + public bool? BusinessDayOnly { get; set; } + /// /// End date of the billing plan, in YYYY-MM-DD format. The end date must align with the frequency and the start date of the billing plan. If left blank, the subscription will continue indefinitely unless it is cancelled by the shopper. /// @@ -166,6 +175,7 @@ public override string ToString() StringBuilder sb = new StringBuilder(); sb.Append("class PixRecurring {\n"); sb.Append(" BillingDate: ").Append(BillingDate).Append("\n"); + sb.Append(" BusinessDayOnly: ").Append(BusinessDayOnly).Append("\n"); sb.Append(" EndsAt: ").Append(EndsAt).Append("\n"); sb.Append(" Frequency: ").Append(Frequency).Append("\n"); sb.Append(" MinAmount: ").Append(MinAmount).Append("\n"); @@ -214,6 +224,10 @@ public bool Equals(PixRecurring input) (this.BillingDate != null && this.BillingDate.Equals(input.BillingDate)) ) && + ( + this.BusinessDayOnly == input.BusinessDayOnly || + this.BusinessDayOnly.Equals(input.BusinessDayOnly) + ) && ( this.EndsAt == input.EndsAt || (this.EndsAt != null && @@ -267,6 +281,7 @@ public override int GetHashCode() { hashCode = (hashCode * 59) + this.BillingDate.GetHashCode(); } + hashCode = (hashCode * 59) + this.BusinessDayOnly.GetHashCode(); if (this.EndsAt != null) { hashCode = (hashCode * 59) + this.EndsAt.GetHashCode(); diff --git a/Adyen/Model/Checkout/ResponseAdditionalDataCommon.cs b/Adyen/Model/Checkout/ResponseAdditionalDataCommon.cs index 952bfb504..914418d3a 100644 --- a/Adyen/Model/Checkout/ResponseAdditionalDataCommon.cs +++ b/Adyen/Model/Checkout/ResponseAdditionalDataCommon.cs @@ -61,9 +61,9 @@ public enum FraudResultTypeEnum [DataMember(Name = "fraudResultType", EmitDefaultValue = false)] public FraudResultTypeEnum? FraudResultType { get; set; } /// - /// The risk level of the transaction as classified by the [machine learning](https://docs.adyen.com/risk-management/configure-your-risk-profile/machine-learning-rules/) fraud risk rule. The risk level indicates the likelihood that a transaction will result in a fraudulent dispute. The possible return values are:\\n* veryLow\\n* low\\n* medium\\n* high\\n* veryHigh\\n\\n> + /// The risk level of the transaction as classified by the [machine learning](https://docs.adyen.com/risk-management/configure-your-risk-profile/machine-learning-rules/) fraud risk rule. The risk level indicates the likelihood that a transaction will result in a fraudulent dispute. The possible return values are: * veryLow * low * medium * high * veryHigh /// - /// The risk level of the transaction as classified by the [machine learning](https://docs.adyen.com/risk-management/configure-your-risk-profile/machine-learning-rules/) fraud risk rule. The risk level indicates the likelihood that a transaction will result in a fraudulent dispute. The possible return values are:\\n* veryLow\\n* low\\n* medium\\n* high\\n* veryHigh\\n\\n> + /// The risk level of the transaction as classified by the [machine learning](https://docs.adyen.com/risk-management/configure-your-risk-profile/machine-learning-rules/) fraud risk rule. The risk level indicates the likelihood that a transaction will result in a fraudulent dispute. The possible return values are: * veryLow * low * medium * high * veryHigh [JsonConverter(typeof(StringEnumConverter))] public enum FraudRiskLevelEnum { @@ -101,9 +101,9 @@ public enum FraudRiskLevelEnum /// - /// The risk level of the transaction as classified by the [machine learning](https://docs.adyen.com/risk-management/configure-your-risk-profile/machine-learning-rules/) fraud risk rule. The risk level indicates the likelihood that a transaction will result in a fraudulent dispute. The possible return values are:\\n* veryLow\\n* low\\n* medium\\n* high\\n* veryHigh\\n\\n> + /// The risk level of the transaction as classified by the [machine learning](https://docs.adyen.com/risk-management/configure-your-risk-profile/machine-learning-rules/) fraud risk rule. The risk level indicates the likelihood that a transaction will result in a fraudulent dispute. The possible return values are: * veryLow * low * medium * high * veryHigh /// - /// The risk level of the transaction as classified by the [machine learning](https://docs.adyen.com/risk-management/configure-your-risk-profile/machine-learning-rules/) fraud risk rule. The risk level indicates the likelihood that a transaction will result in a fraudulent dispute. The possible return values are:\\n* veryLow\\n* low\\n* medium\\n* high\\n* veryHigh\\n\\n> + /// The risk level of the transaction as classified by the [machine learning](https://docs.adyen.com/risk-management/configure-your-risk-profile/machine-learning-rules/) fraud risk rule. The risk level indicates the likelihood that a transaction will result in a fraudulent dispute. The possible return values are: * veryLow * low * medium * high * veryHigh [DataMember(Name = "fraudRiskLevel", EmitDefaultValue = false)] public FraudRiskLevelEnum? FraudRiskLevel { get; set; } /// @@ -200,7 +200,7 @@ public enum TokenizationStoreOperationTypeEnum /// The fraud score due to a particular fraud check. The fraud check name is found in the key of the key-value pair.. /// Indicates if the payment is sent to manual review.. /// The fraud result properties of the payment.. - /// The risk level of the transaction as classified by the [machine learning](https://docs.adyen.com/risk-management/configure-your-risk-profile/machine-learning-rules/) fraud risk rule. The risk level indicates the likelihood that a transaction will result in a fraudulent dispute. The possible return values are:\\n* veryLow\\n* low\\n* medium\\n* high\\n* veryHigh\\n\\n>. + /// The risk level of the transaction as classified by the [machine learning](https://docs.adyen.com/risk-management/configure-your-risk-profile/machine-learning-rules/) fraud risk rule. The risk level indicates the likelihood that a transaction will result in a fraudulent dispute. The possible return values are: * veryLow * low * medium * high * veryHigh . /// Information regarding the funding type of the card. The possible return values are: * CHARGE * CREDIT * DEBIT * PREPAID * PREPAID_RELOADABLE * PREPAID_NONRELOADABLE * DEFFERED_DEBIT > This functionality requires additional configuration on Adyen's end. To enable it, contact the Support Team. For receiving this field in the notification, enable **Include Funding Source** in **Notifications** > **Additional settings**.. /// Indicates availability of funds. Visa: * \"I\" (fast funds are supported) * \"N\" (otherwise) Mastercard: * \"I\" (product type is Prepaid or Debit, or issuing country is in CEE/HGEM list) * \"N\" (otherwise) > Returned when you verify a card BIN or estimate costs, and only if payoutEligible is \"Y\" or \"D\".. /// Provides the more granular indication of why a transaction was refused. When a transaction fails with either \"Refused\", \"Restricted Card\", \"Transaction Not Permitted\", \"Not supported\" or \"DeclinedNon Generic\" refusalReason from the issuer, Adyen cross references its PSP-wide data for extra insight into the refusal reason. If an inferred refusal reason is available, the `inferredRefusalReason`, field is populated and the `refusalReason`, is set to \"Not Supported\". Possible values: * 3D Secure Mandated * Closed Account * ContAuth Not Supported * CVC Mandated * Ecommerce Not Allowed * Crossborder Not Supported * Card Updated * Low Authrate Bin * Non-reloadable prepaid card. diff --git a/Adyen/Model/Checkout/RivertyDetails.cs b/Adyen/Model/Checkout/RivertyDetails.cs index dc6f32dfe..7b43f55cf 100644 --- a/Adyen/Model/Checkout/RivertyDetails.cs +++ b/Adyen/Model/Checkout/RivertyDetails.cs @@ -82,8 +82,9 @@ protected RivertyDetails() { } /// Shopper name, date of birth, phone number, and email address.. /// This is the `recurringDetailReference` returned in the response when you created the token.. /// This is the `recurringDetailReference` returned in the response when you created the token.. + /// The payment method subtype.. /// **riverty** (required) (default to TypeEnum.Riverty). - public RivertyDetails(string billingAddress = default(string), string checkoutAttemptId = default(string), string deliveryAddress = default(string), string deviceFingerprint = default(string), string iban = default(string), string personalDetails = default(string), string recurringDetailReference = default(string), string storedPaymentMethodId = default(string), TypeEnum type = TypeEnum.Riverty) + public RivertyDetails(string billingAddress = default(string), string checkoutAttemptId = default(string), string deliveryAddress = default(string), string deviceFingerprint = default(string), string iban = default(string), string personalDetails = default(string), string recurringDetailReference = default(string), string storedPaymentMethodId = default(string), string subtype = default(string), TypeEnum type = TypeEnum.Riverty) { this.Type = type; this.BillingAddress = billingAddress; @@ -94,6 +95,7 @@ protected RivertyDetails() { } this.PersonalDetails = personalDetails; this.RecurringDetailReference = recurringDetailReference; this.StoredPaymentMethodId = storedPaymentMethodId; + this.Subtype = subtype; } /// @@ -153,6 +155,13 @@ protected RivertyDetails() { } [DataMember(Name = "storedPaymentMethodId", EmitDefaultValue = false)] public string StoredPaymentMethodId { get; set; } + /// + /// The payment method subtype. + /// + /// The payment method subtype. + [DataMember(Name = "subtype", EmitDefaultValue = false)] + public string Subtype { get; set; } + /// /// Returns the string presentation of the object /// @@ -169,6 +178,7 @@ public override string ToString() sb.Append(" PersonalDetails: ").Append(PersonalDetails).Append("\n"); sb.Append(" RecurringDetailReference: ").Append(RecurringDetailReference).Append("\n"); sb.Append(" StoredPaymentMethodId: ").Append(StoredPaymentMethodId).Append("\n"); + sb.Append(" Subtype: ").Append(Subtype).Append("\n"); sb.Append(" Type: ").Append(Type).Append("\n"); sb.Append("}\n"); return sb.ToString(); @@ -245,6 +255,11 @@ public bool Equals(RivertyDetails input) (this.StoredPaymentMethodId != null && this.StoredPaymentMethodId.Equals(input.StoredPaymentMethodId)) ) && + ( + this.Subtype == input.Subtype || + (this.Subtype != null && + this.Subtype.Equals(input.Subtype)) + ) && ( this.Type == input.Type || this.Type.Equals(input.Type) @@ -292,6 +307,10 @@ public override int GetHashCode() { hashCode = (hashCode * 59) + this.StoredPaymentMethodId.GetHashCode(); } + if (this.Subtype != null) + { + hashCode = (hashCode * 59) + this.Subtype.GetHashCode(); + } hashCode = (hashCode * 59) + this.Type.GetHashCode(); return hashCode; } diff --git a/Adyen/Model/Checkout/SessionResultResponse.cs b/Adyen/Model/Checkout/SessionResultResponse.cs index d93c1199b..60a5fe465 100644 --- a/Adyen/Model/Checkout/SessionResultResponse.cs +++ b/Adyen/Model/Checkout/SessionResultResponse.cs @@ -87,14 +87,27 @@ public enum StatusEnum /// /// Initializes a new instance of the class. /// + /// Contains additional information about the payment. Some fields are included only if you enable them. To enable these fields in your Customer Area, go to **Developers** > **Additional data**.. /// A unique identifier of the session.. + /// A list of all authorised payments done for this session.. + /// The unique reference that you provided in the original `/sessions` request. This identifies the payment and is used in all communication with you about the payment status.. /// The status of the session. The status included in the response doesn't get updated. Don't make the request again to check for payment status updates. Possible values: * **completed**: the shopper completed the payment, and the payment was authorized. * **paymentPending**: the shopper is in the process of making the payment. This applies to payment methods with an asynchronous flow, like voucher payments where the shopper completes the payment in a physical shop. * **refused**: the session has been refused, because of too many refused payment attempts. The shopper can no longer complete the payment with this session. * **canceled**: the shopper canceled the payment. * **expired**: the session expired. The shopper can no longer complete the payment with this session. By default, the session expires one hour after it is created.. - public SessionResultResponse(string id = default(string), StatusEnum? status = default(StatusEnum?)) + public SessionResultResponse(Dictionary additionalData = default(Dictionary), string id = default(string), List payments = default(List), string reference = default(string), StatusEnum? status = default(StatusEnum?)) { + this.AdditionalData = additionalData; this.Id = id; + this.Payments = payments; + this.Reference = reference; this.Status = status; } + /// + /// Contains additional information about the payment. Some fields are included only if you enable them. To enable these fields in your Customer Area, go to **Developers** > **Additional data**. + /// + /// Contains additional information about the payment. Some fields are included only if you enable them. To enable these fields in your Customer Area, go to **Developers** > **Additional data**. + [DataMember(Name = "additionalData", EmitDefaultValue = false)] + public Dictionary AdditionalData { get; set; } + /// /// A unique identifier of the session. /// @@ -102,6 +115,20 @@ public enum StatusEnum [DataMember(Name = "id", EmitDefaultValue = false)] public string Id { get; set; } + /// + /// A list of all authorised payments done for this session. + /// + /// A list of all authorised payments done for this session. + [DataMember(Name = "payments", EmitDefaultValue = false)] + public List Payments { get; set; } + + /// + /// The unique reference that you provided in the original `/sessions` request. This identifies the payment and is used in all communication with you about the payment status. + /// + /// The unique reference that you provided in the original `/sessions` request. This identifies the payment and is used in all communication with you about the payment status. + [DataMember(Name = "reference", EmitDefaultValue = false)] + public string Reference { get; set; } + /// /// Returns the string presentation of the object /// @@ -110,7 +137,10 @@ public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append("class SessionResultResponse {\n"); + sb.Append(" AdditionalData: ").Append(AdditionalData).Append("\n"); sb.Append(" Id: ").Append(Id).Append("\n"); + sb.Append(" Payments: ").Append(Payments).Append("\n"); + sb.Append(" Reference: ").Append(Reference).Append("\n"); sb.Append(" Status: ").Append(Status).Append("\n"); sb.Append("}\n"); return sb.ToString(); @@ -147,11 +177,28 @@ public bool Equals(SessionResultResponse input) return false; } return + ( + this.AdditionalData == input.AdditionalData || + this.AdditionalData != null && + input.AdditionalData != null && + this.AdditionalData.SequenceEqual(input.AdditionalData) + ) && ( this.Id == input.Id || (this.Id != null && this.Id.Equals(input.Id)) ) && + ( + this.Payments == input.Payments || + this.Payments != null && + input.Payments != null && + this.Payments.SequenceEqual(input.Payments) + ) && + ( + this.Reference == input.Reference || + (this.Reference != null && + this.Reference.Equals(input.Reference)) + ) && ( this.Status == input.Status || this.Status.Equals(input.Status) @@ -167,10 +214,22 @@ public override int GetHashCode() unchecked // Overflow is fine, just wrap { int hashCode = 41; + if (this.AdditionalData != null) + { + hashCode = (hashCode * 59) + this.AdditionalData.GetHashCode(); + } if (this.Id != null) { hashCode = (hashCode * 59) + this.Id.GetHashCode(); } + if (this.Payments != null) + { + hashCode = (hashCode * 59) + this.Payments.GetHashCode(); + } + if (this.Reference != null) + { + hashCode = (hashCode * 59) + this.Reference.GetHashCode(); + } hashCode = (hashCode * 59) + this.Status.GetHashCode(); return hashCode; } diff --git a/Adyen/Model/Checkout/StandalonePaymentCancelRequest.cs b/Adyen/Model/Checkout/StandalonePaymentCancelRequest.cs index 941dadf40..0f75335d7 100644 --- a/Adyen/Model/Checkout/StandalonePaymentCancelRequest.cs +++ b/Adyen/Model/Checkout/StandalonePaymentCancelRequest.cs @@ -41,14 +41,16 @@ protected StandalonePaymentCancelRequest() { } /// Initializes a new instance of the class. /// /// applicationInfo. + /// enhancedSchemeData. /// The merchant account that is used to process the payment. (required). /// The [`reference`](https://docs.adyen.com/api-explorer/#/CheckoutService/latest/post/payments__reqParam_reference) of the payment that you want to cancel. (required). /// Your reference for the cancel request. Maximum length: 80 characters.. - public StandalonePaymentCancelRequest(ApplicationInfo applicationInfo = default(ApplicationInfo), string merchantAccount = default(string), string paymentReference = default(string), string reference = default(string)) + public StandalonePaymentCancelRequest(ApplicationInfo applicationInfo = default(ApplicationInfo), EnhancedSchemeData enhancedSchemeData = default(EnhancedSchemeData), string merchantAccount = default(string), string paymentReference = default(string), string reference = default(string)) { this.MerchantAccount = merchantAccount; this.PaymentReference = paymentReference; this.ApplicationInfo = applicationInfo; + this.EnhancedSchemeData = enhancedSchemeData; this.Reference = reference; } @@ -58,6 +60,12 @@ protected StandalonePaymentCancelRequest() { } [DataMember(Name = "applicationInfo", EmitDefaultValue = false)] public ApplicationInfo ApplicationInfo { get; set; } + /// + /// Gets or Sets EnhancedSchemeData + /// + [DataMember(Name = "enhancedSchemeData", EmitDefaultValue = false)] + public EnhancedSchemeData EnhancedSchemeData { get; set; } + /// /// The merchant account that is used to process the payment. /// @@ -88,6 +96,7 @@ public override string ToString() StringBuilder sb = new StringBuilder(); sb.Append("class StandalonePaymentCancelRequest {\n"); sb.Append(" ApplicationInfo: ").Append(ApplicationInfo).Append("\n"); + sb.Append(" EnhancedSchemeData: ").Append(EnhancedSchemeData).Append("\n"); sb.Append(" MerchantAccount: ").Append(MerchantAccount).Append("\n"); sb.Append(" PaymentReference: ").Append(PaymentReference).Append("\n"); sb.Append(" Reference: ").Append(Reference).Append("\n"); @@ -131,6 +140,11 @@ public bool Equals(StandalonePaymentCancelRequest input) (this.ApplicationInfo != null && this.ApplicationInfo.Equals(input.ApplicationInfo)) ) && + ( + this.EnhancedSchemeData == input.EnhancedSchemeData || + (this.EnhancedSchemeData != null && + this.EnhancedSchemeData.Equals(input.EnhancedSchemeData)) + ) && ( this.MerchantAccount == input.MerchantAccount || (this.MerchantAccount != null && @@ -161,6 +175,10 @@ public override int GetHashCode() { hashCode = (hashCode * 59) + this.ApplicationInfo.GetHashCode(); } + if (this.EnhancedSchemeData != null) + { + hashCode = (hashCode * 59) + this.EnhancedSchemeData.GetHashCode(); + } if (this.MerchantAccount != null) { hashCode = (hashCode * 59) + this.MerchantAccount.GetHashCode(); diff --git a/Adyen/Model/Checkout/SubMerchantInfo.cs b/Adyen/Model/Checkout/SubMerchantInfo.cs index 0aadcc313..b6faa1053 100644 --- a/Adyen/Model/Checkout/SubMerchantInfo.cs +++ b/Adyen/Model/Checkout/SubMerchantInfo.cs @@ -40,7 +40,7 @@ public partial class SubMerchantInfo : IEquatable, IValidatable /// Required for transactions performed by registered payment facilitators. The email associated with the sub-merchant's account.. /// Required for transactions performed by registered payment facilitators. A unique identifier that you create for the sub-merchant, used by schemes to identify the sub-merchant. * Format: Alphanumeric * Maximum length: 15 characters. /// Required for transactions performed by registered payment facilitators. The sub-merchant's 4-digit Merchant Category Code (MCC). * Format: Numeric * Fixed length: 4 digits. - /// Required for transactions performed by registered payment facilitators. The name of the sub-merchant. Based on scheme specifications, this value will overwrite the shopper statement that will appear in the card statement. * Format: Alphanumeric * Maximum length: 22 characters. + /// Required for transactions performed by registered payment facilitators. The name of the sub-merchant. Based on scheme specifications, this value will overwrite the shopper statement that will appear in the card statement. Exception: for acquirers in Brazil, this value does not overwrite the shopper statement. * Format: Alphanumeric * Maximum length: 22 characters. /// Required for transactions performed by registered payment facilitators. The phone number associated with the sub-merchant's account.. /// registeredSince. /// Required for transactions performed by registered payment facilitators. The tax ID of the sub-merchant. * Format: Numeric * Fixed length: 11 digits for the CPF or 14 digits for the CNPJ. @@ -93,9 +93,9 @@ public partial class SubMerchantInfo : IEquatable, IValidatable public string Mcc { get; set; } /// - /// Required for transactions performed by registered payment facilitators. The name of the sub-merchant. Based on scheme specifications, this value will overwrite the shopper statement that will appear in the card statement. * Format: Alphanumeric * Maximum length: 22 characters + /// Required for transactions performed by registered payment facilitators. The name of the sub-merchant. Based on scheme specifications, this value will overwrite the shopper statement that will appear in the card statement. Exception: for acquirers in Brazil, this value does not overwrite the shopper statement. * Format: Alphanumeric * Maximum length: 22 characters /// - /// Required for transactions performed by registered payment facilitators. The name of the sub-merchant. Based on scheme specifications, this value will overwrite the shopper statement that will appear in the card statement. * Format: Alphanumeric * Maximum length: 22 characters + /// Required for transactions performed by registered payment facilitators. The name of the sub-merchant. Based on scheme specifications, this value will overwrite the shopper statement that will appear in the card statement. Exception: for acquirers in Brazil, this value does not overwrite the shopper statement. * Format: Alphanumeric * Maximum length: 22 characters [DataMember(Name = "name", EmitDefaultValue = false)] public string Name { get; set; } diff --git a/Adyen/Model/Checkout/ThreeDS2RequestData.cs b/Adyen/Model/Checkout/ThreeDS2RequestData.cs index e277b3180..97da42098 100644 --- a/Adyen/Model/Checkout/ThreeDS2RequestData.cs +++ b/Adyen/Model/Checkout/ThreeDS2RequestData.cs @@ -67,9 +67,9 @@ public enum AcctTypeEnum [DataMember(Name = "acctType", EmitDefaultValue = false)] public AcctTypeEnum? AcctType { get; set; } /// - /// Indicates whether the Cardholder Shipping Address and Cardholder Billing Address are the same. Allowed values: * **Y** — Shipping Address matches Billing Address. * **N** — Shipping Address does not match Billing Address. + /// Indicates whether the cardholder shipping address and cardholder billing address are the same. Allowed values: * **Y** — Shipping address matches billing address. * **N** — Shipping address does not match billing address. /// - /// Indicates whether the Cardholder Shipping Address and Cardholder Billing Address are the same. Allowed values: * **Y** — Shipping Address matches Billing Address. * **N** — Shipping Address does not match Billing Address. + /// Indicates whether the cardholder shipping address and cardholder billing address are the same. Allowed values: * **Y** — Shipping address matches billing address. * **N** — Shipping address does not match billing address. [JsonConverter(typeof(StringEnumConverter))] public enum AddrMatchEnum { @@ -89,9 +89,9 @@ public enum AddrMatchEnum /// - /// Indicates whether the Cardholder Shipping Address and Cardholder Billing Address are the same. Allowed values: * **Y** — Shipping Address matches Billing Address. * **N** — Shipping Address does not match Billing Address. + /// Indicates whether the cardholder shipping address and cardholder billing address are the same. Allowed values: * **Y** — Shipping address matches billing address. * **N** — Shipping address does not match billing address. /// - /// Indicates whether the Cardholder Shipping Address and Cardholder Billing Address are the same. Allowed values: * **Y** — Shipping Address matches Billing Address. * **N** — Shipping Address does not match Billing Address. + /// Indicates whether the cardholder shipping address and cardholder billing address are the same. Allowed values: * **Y** — Shipping address matches billing address. * **N** — Shipping address does not match billing address. [DataMember(Name = "addrMatch", EmitDefaultValue = false)] public AddrMatchEnum? AddrMatch { get; set; } /// @@ -291,7 +291,7 @@ protected ThreeDS2RequestData() { } /// Indicates the type of account. For example, for a multi-account card product. Length: 2 characters. Allowed values: * **01** — Not applicable * **02** — Credit * **03** — Debit. /// Required for [authentication-only integration](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only). The acquiring BIN enrolled for 3D Secure 2. This string should match the value that you will use in the authorisation. Use 123456 on the Test platform.. /// Required for [authentication-only integration](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only). The merchantId that is enrolled for 3D Secure 2 by the merchant's acquirer. This string should match the value that you will use in the authorisation. Use 123456 on the Test platform.. - /// Indicates whether the Cardholder Shipping Address and Cardholder Billing Address are the same. Allowed values: * **Y** — Shipping Address matches Billing Address. * **N** — Shipping Address does not match Billing Address.. + /// Indicates whether the cardholder shipping address and cardholder billing address are the same. Allowed values: * **Y** — Shipping address matches billing address. * **N** — Shipping address does not match billing address.. /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. (default to false). /// Possibility to specify a preference for receiving a challenge from the issuer. Allowed values: * `noPreference` * `requestNoChallenge` * `requestChallenge` * `requestChallengeAsMandate` . /// The environment of the shopper. Allowed values: * `app` * `browser` (required). diff --git a/Adyen/Model/Checkout/ThreeDS2RequestFields.cs b/Adyen/Model/Checkout/ThreeDS2RequestFields.cs index c2fce35b6..f53902a61 100644 --- a/Adyen/Model/Checkout/ThreeDS2RequestFields.cs +++ b/Adyen/Model/Checkout/ThreeDS2RequestFields.cs @@ -67,9 +67,9 @@ public enum AcctTypeEnum [DataMember(Name = "acctType", EmitDefaultValue = false)] public AcctTypeEnum? AcctType { get; set; } /// - /// Indicates whether the Cardholder Shipping Address and Cardholder Billing Address are the same. Allowed values: * **Y** — Shipping Address matches Billing Address. * **N** — Shipping Address does not match Billing Address. + /// Indicates whether the cardholder shipping Address and cardholder billing address are the same. Allowed values: * **Y** — Shipping Address matches Billing Address. * **N** — Shipping Address does not match Billing Address. /// - /// Indicates whether the Cardholder Shipping Address and Cardholder Billing Address are the same. Allowed values: * **Y** — Shipping Address matches Billing Address. * **N** — Shipping Address does not match Billing Address. + /// Indicates whether the cardholder shipping Address and cardholder billing address are the same. Allowed values: * **Y** — Shipping Address matches Billing Address. * **N** — Shipping Address does not match Billing Address. [JsonConverter(typeof(StringEnumConverter))] public enum AddrMatchEnum { @@ -89,9 +89,9 @@ public enum AddrMatchEnum /// - /// Indicates whether the Cardholder Shipping Address and Cardholder Billing Address are the same. Allowed values: * **Y** — Shipping Address matches Billing Address. * **N** — Shipping Address does not match Billing Address. + /// Indicates whether the cardholder shipping Address and cardholder billing address are the same. Allowed values: * **Y** — Shipping Address matches Billing Address. * **N** — Shipping Address does not match Billing Address. /// - /// Indicates whether the Cardholder Shipping Address and Cardholder Billing Address are the same. Allowed values: * **Y** — Shipping Address matches Billing Address. * **N** — Shipping Address does not match Billing Address. + /// Indicates whether the cardholder shipping Address and cardholder billing address are the same. Allowed values: * **Y** — Shipping Address matches Billing Address. * **N** — Shipping Address does not match Billing Address. [DataMember(Name = "addrMatch", EmitDefaultValue = false)] public AddrMatchEnum? AddrMatch { get; set; } /// @@ -286,7 +286,7 @@ public enum TransactionTypeEnum /// Indicates the type of account. For example, for a multi-account card product. Length: 2 characters. Allowed values: * **01** — Not applicable * **02** — Credit * **03** — Debit. /// Required for [authentication-only integration](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only). The acquiring BIN enrolled for 3D Secure 2. This string should match the value that you will use in the authorisation. Use 123456 on the Test platform.. /// Required for [authentication-only integration](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only). The merchantId that is enrolled for 3D Secure 2 by the merchant's acquirer. This string should match the value that you will use in the authorisation. Use 123456 on the Test platform.. - /// Indicates whether the Cardholder Shipping Address and Cardholder Billing Address are the same. Allowed values: * **Y** — Shipping Address matches Billing Address. * **N** — Shipping Address does not match Billing Address.. + /// Indicates whether the cardholder shipping Address and cardholder billing address are the same. Allowed values: * **Y** — Shipping Address matches Billing Address. * **N** — Shipping Address does not match Billing Address.. /// If set to true, you will only perform the [3D Secure 2 authentication](https://docs.adyen.com/online-payments/3d-secure/other-3ds-flows/authentication-only), and not the payment authorisation. (default to false). /// Possibility to specify a preference for receiving a challenge from the issuer. Allowed values: * `noPreference` * `requestNoChallenge` * `requestChallenge` * `requestChallengeAsMandate` . /// deviceRenderOptions. diff --git a/Adyen/Model/Checkout/ThreeDSRequestData.cs b/Adyen/Model/Checkout/ThreeDSRequestData.cs index da6917c30..4b5d418bb 100644 --- a/Adyen/Model/Checkout/ThreeDSRequestData.cs +++ b/Adyen/Model/Checkout/ThreeDSRequestData.cs @@ -79,9 +79,9 @@ public enum ChallengeWindowSizeEnum [DataMember(Name = "challengeWindowSize", EmitDefaultValue = false)] public ChallengeWindowSizeEnum? ChallengeWindowSize { get; set; } /// - /// Flag for data only flow. + /// Required to trigger the [data-only flow](https://docs.adyen.com/online-payments/3d-secure/data-only/). When set to **true**, forces the 3D Secure 2 data-only flow for all transactions where it is possible. /// - /// Flag for data only flow. + /// Required to trigger the [data-only flow](https://docs.adyen.com/online-payments/3d-secure/data-only/). When set to **true**, forces the 3D Secure 2 data-only flow for all transactions where it is possible. [JsonConverter(typeof(StringEnumConverter))] public enum DataOnlyEnum { @@ -101,15 +101,15 @@ public enum DataOnlyEnum /// - /// Flag for data only flow. + /// Required to trigger the [data-only flow](https://docs.adyen.com/online-payments/3d-secure/data-only/). When set to **true**, forces the 3D Secure 2 data-only flow for all transactions where it is possible. /// - /// Flag for data only flow. + /// Required to trigger the [data-only flow](https://docs.adyen.com/online-payments/3d-secure/data-only/). When set to **true**, forces the 3D Secure 2 data-only flow for all transactions where it is possible. [DataMember(Name = "dataOnly", EmitDefaultValue = false)] public DataOnlyEnum? DataOnly { get; set; } /// - /// Indicates if [native 3D Secure authentication](https://docs.adyen.com/online-payments/3d-secure/native-3ds2) should be used when available. Possible values: * **preferred**: Use native 3D Secure authentication when available. * **disabled**: Only use the redirect 3D Secure authentication flow. + /// Indicates if [native 3D Secure authentication](https://docs.adyen.com/online-payments/3d-secure/native-3ds2) should be triggered when available. Adyen can still select to fallback to the redirect flow to optimize authorization rates and improve the shopper's experience. Possible values: * **preferred**: Use native 3D Secure authentication when available. * **disabled**: Use the redirect 3D Secure authentication flow. /// - /// Indicates if [native 3D Secure authentication](https://docs.adyen.com/online-payments/3d-secure/native-3ds2) should be used when available. Possible values: * **preferred**: Use native 3D Secure authentication when available. * **disabled**: Only use the redirect 3D Secure authentication flow. + /// Indicates if [native 3D Secure authentication](https://docs.adyen.com/online-payments/3d-secure/native-3ds2) should be triggered when available. Adyen can still select to fallback to the redirect flow to optimize authorization rates and improve the shopper's experience. Possible values: * **preferred**: Use native 3D Secure authentication when available. * **disabled**: Use the redirect 3D Secure authentication flow. [JsonConverter(typeof(StringEnumConverter))] public enum NativeThreeDSEnum { @@ -129,9 +129,9 @@ public enum NativeThreeDSEnum /// - /// Indicates if [native 3D Secure authentication](https://docs.adyen.com/online-payments/3d-secure/native-3ds2) should be used when available. Possible values: * **preferred**: Use native 3D Secure authentication when available. * **disabled**: Only use the redirect 3D Secure authentication flow. + /// Indicates if [native 3D Secure authentication](https://docs.adyen.com/online-payments/3d-secure/native-3ds2) should be triggered when available. Adyen can still select to fallback to the redirect flow to optimize authorization rates and improve the shopper's experience. Possible values: * **preferred**: Use native 3D Secure authentication when available. * **disabled**: Use the redirect 3D Secure authentication flow. /// - /// Indicates if [native 3D Secure authentication](https://docs.adyen.com/online-payments/3d-secure/native-3ds2) should be used when available. Possible values: * **preferred**: Use native 3D Secure authentication when available. * **disabled**: Only use the redirect 3D Secure authentication flow. + /// Indicates if [native 3D Secure authentication](https://docs.adyen.com/online-payments/3d-secure/native-3ds2) should be triggered when available. Adyen can still select to fallback to the redirect flow to optimize authorization rates and improve the shopper's experience. Possible values: * **preferred**: Use native 3D Secure authentication when available. * **disabled**: Use the redirect 3D Secure authentication flow. [DataMember(Name = "nativeThreeDS", EmitDefaultValue = false)] public NativeThreeDSEnum? NativeThreeDS { get; set; } /// @@ -166,8 +166,8 @@ public enum ThreeDSVersionEnum /// Initializes a new instance of the class. /// /// Dimensions of the 3DS2 challenge window to be displayed to the cardholder. Possible values: * **01** - size of 250x400 * **02** - size of 390x400 * **03** - size of 500x600 * **04** - size of 600x400 * **05** - Fullscreen. - /// Flag for data only flow.. - /// Indicates if [native 3D Secure authentication](https://docs.adyen.com/online-payments/3d-secure/native-3ds2) should be used when available. Possible values: * **preferred**: Use native 3D Secure authentication when available. * **disabled**: Only use the redirect 3D Secure authentication flow.. + /// Required to trigger the [data-only flow](https://docs.adyen.com/online-payments/3d-secure/data-only/). When set to **true**, forces the 3D Secure 2 data-only flow for all transactions where it is possible. . + /// Indicates if [native 3D Secure authentication](https://docs.adyen.com/online-payments/3d-secure/native-3ds2) should be triggered when available. Adyen can still select to fallback to the redirect flow to optimize authorization rates and improve the shopper's experience. Possible values: * **preferred**: Use native 3D Secure authentication when available. * **disabled**: Use the redirect 3D Secure authentication flow.. /// The version of 3D Secure to use. Possible values: * **2.1.0** * **2.2.0**. public ThreeDSRequestData(ChallengeWindowSizeEnum? challengeWindowSize = default(ChallengeWindowSizeEnum?), DataOnlyEnum? dataOnly = default(DataOnlyEnum?), NativeThreeDSEnum? nativeThreeDS = default(NativeThreeDSEnum?), ThreeDSVersionEnum? threeDSVersion = default(ThreeDSVersionEnum?)) { diff --git a/Adyen/Model/ConfigurationWebhooks/Card.cs b/Adyen/Model/ConfigurationWebhooks/Card.cs index 3e1d4f563..4bcabca25 100644 --- a/Adyen/Model/ConfigurationWebhooks/Card.cs +++ b/Adyen/Model/ConfigurationWebhooks/Card.cs @@ -86,7 +86,7 @@ protected Card() { } /// The form factor of the card. Possible values: **virtual**, **physical**. (required). /// Last last four digits of the card number.. /// The primary account number (PAN) of the card. > The PAN is masked by default and returned only for single-use virtual cards. (required). - /// Allocates a specific product range for either a physical or a virtual card. Possible values: **fullySupported**, **secureCorporate**. >Reach out to your Adyen contact to get the values relevant for your integration.. + /// The 3DS configuration of the physical or the virtual card. Possible values: **fullySupported**, **secureCorporate**. > Reach out to your Adyen contact to get the values relevant for your integration.. public Card(Authentication authentication = default(Authentication), string bin = default(string), string brand = default(string), string brandVariant = default(string), string cardholderName = default(string), CardConfiguration configuration = default(CardConfiguration), string cvc = default(string), DeliveryContact deliveryContact = default(DeliveryContact), Expiry expiration = default(Expiry), FormFactorEnum formFactor = default(FormFactorEnum), string lastFour = default(string), string number = default(string), string threeDSecure = default(string)) { this.Brand = brand; @@ -178,9 +178,9 @@ protected Card() { } public string Number { get; set; } /// - /// Allocates a specific product range for either a physical or a virtual card. Possible values: **fullySupported**, **secureCorporate**. >Reach out to your Adyen contact to get the values relevant for your integration. + /// The 3DS configuration of the physical or the virtual card. Possible values: **fullySupported**, **secureCorporate**. > Reach out to your Adyen contact to get the values relevant for your integration. /// - /// Allocates a specific product range for either a physical or a virtual card. Possible values: **fullySupported**, **secureCorporate**. >Reach out to your Adyen contact to get the values relevant for your integration. + /// The 3DS configuration of the physical or the virtual card. Possible values: **fullySupported**, **secureCorporate**. > Reach out to your Adyen contact to get the values relevant for your integration. [DataMember(Name = "threeDSecure", EmitDefaultValue = false)] public string ThreeDSecure { get; set; } diff --git a/Adyen/Model/ConfigurationWebhooks/Device.cs b/Adyen/Model/ConfigurationWebhooks/Device.cs new file mode 100644 index 000000000..57051897d --- /dev/null +++ b/Adyen/Model/ConfigurationWebhooks/Device.cs @@ -0,0 +1,148 @@ +/* +* Configuration webhooks +* +* +* The version of the OpenAPI document: 2 +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using System.ComponentModel.DataAnnotations; +using OpenAPIDateConverter = Adyen.ApiSerialization.OpenAPIDateConverter; + +namespace Adyen.Model.ConfigurationWebhooks +{ + /// + /// Device + /// + [DataContract(Name = "Device")] + public partial class Device : IEquatable, IValidatableObject + { + /// + /// Initializes a new instance of the class. + /// + /// The type of the device used for provisioning the network token. For example, **phone**, **mobile_phone**, **watch**, **mobilephone_or_tablet**, etc. + /// The operating system of the device used for provisioning the network token.. + public Device(string formFactor = default(string), string osName = default(string)) + { + this.FormFactor = formFactor; + this.OsName = osName; + } + + /// + /// The type of the device used for provisioning the network token. For example, **phone**, **mobile_phone**, **watch**, **mobilephone_or_tablet**, etc + /// + /// The type of the device used for provisioning the network token. For example, **phone**, **mobile_phone**, **watch**, **mobilephone_or_tablet**, etc + [DataMember(Name = "formFactor", EmitDefaultValue = false)] + public string FormFactor { get; set; } + + /// + /// The operating system of the device used for provisioning the network token. + /// + /// The operating system of the device used for provisioning the network token. + [DataMember(Name = "osName", EmitDefaultValue = false)] + public string OsName { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class Device {\n"); + sb.Append(" FormFactor: ").Append(FormFactor).Append("\n"); + sb.Append(" OsName: ").Append(OsName).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as Device); + } + + /// + /// Returns true if Device instances are equal + /// + /// Instance of Device to be compared + /// Boolean + public bool Equals(Device input) + { + if (input == null) + { + return false; + } + return + ( + this.FormFactor == input.FormFactor || + (this.FormFactor != null && + this.FormFactor.Equals(input.FormFactor)) + ) && + ( + this.OsName == input.OsName || + (this.OsName != null && + this.OsName.Equals(input.OsName)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.FormFactor != null) + { + hashCode = (hashCode * 59) + this.FormFactor.GetHashCode(); + } + if (this.OsName != null) + { + hashCode = (hashCode * 59) + this.OsName.GetHashCode(); + } + return hashCode; + } + } + /// + /// To validate all properties of the instance + /// + /// Validation context + /// Validation Result + public IEnumerable Validate(ValidationContext validationContext) + { + yield break; + } + } + +} diff --git a/Adyen/Model/ConfigurationWebhooks/NetworkTokenNotificationDataV2.cs b/Adyen/Model/ConfigurationWebhooks/NetworkTokenNotificationDataV2.cs new file mode 100644 index 000000000..48eed65c6 --- /dev/null +++ b/Adyen/Model/ConfigurationWebhooks/NetworkTokenNotificationDataV2.cs @@ -0,0 +1,332 @@ +/* +* Configuration webhooks +* +* +* The version of the OpenAPI document: 2 +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using System.ComponentModel.DataAnnotations; +using OpenAPIDateConverter = Adyen.ApiSerialization.OpenAPIDateConverter; + +namespace Adyen.Model.ConfigurationWebhooks +{ + /// + /// NetworkTokenNotificationDataV2 + /// + [DataContract(Name = "NetworkTokenNotificationDataV2")] + public partial class NetworkTokenNotificationDataV2 : IEquatable, IValidatableObject + { + /// + /// Initializes a new instance of the class. + /// + /// authentication. + /// Specifies whether the authentication process was triggered during token provisioning.. + /// The unique identifier of the balance platform.. + /// The decision about the network token provisioning. Possible values: **approved**, **declined**, **requiresAuthentication**.. + /// The unique identifier of the network token.. + /// The unique identifier of the payment instrument to which the network token is associated.. + /// The status of the network token.. + /// The last four digits of the network token. Use this value to help your user to identify their network token.. + /// tokenRequestor. + /// The type of network token.. + /// The rules used to validate the request for provisioning the network token.. + /// wallet. + public NetworkTokenNotificationDataV2(TokenAuthentication authentication = default(TokenAuthentication), bool? authenticationApplied = default(bool?), string balancePlatform = default(string), string decision = default(string), string id = default(string), string paymentInstrumentId = default(string), string status = default(string), string tokenLastFour = default(string), NetworkTokenRequestor tokenRequestor = default(NetworkTokenRequestor), string type = default(string), List validationFacts = default(List), Wallet wallet = default(Wallet)) + { + this.Authentication = authentication; + this.AuthenticationApplied = authenticationApplied; + this.BalancePlatform = balancePlatform; + this.Decision = decision; + this.Id = id; + this.PaymentInstrumentId = paymentInstrumentId; + this.Status = status; + this.TokenLastFour = tokenLastFour; + this.TokenRequestor = tokenRequestor; + this.Type = type; + this.ValidationFacts = validationFacts; + this.Wallet = wallet; + } + + /// + /// Gets or Sets Authentication + /// + [DataMember(Name = "authentication", EmitDefaultValue = false)] + public TokenAuthentication Authentication { get; set; } + + /// + /// Specifies whether the authentication process was triggered during token provisioning. + /// + /// Specifies whether the authentication process was triggered during token provisioning. + [DataMember(Name = "authenticationApplied", EmitDefaultValue = false)] + public bool? AuthenticationApplied { get; set; } + + /// + /// The unique identifier of the balance platform. + /// + /// The unique identifier of the balance platform. + [DataMember(Name = "balancePlatform", EmitDefaultValue = false)] + public string BalancePlatform { get; set; } + + /// + /// The decision about the network token provisioning. Possible values: **approved**, **declined**, **requiresAuthentication**. + /// + /// The decision about the network token provisioning. Possible values: **approved**, **declined**, **requiresAuthentication**. + [DataMember(Name = "decision", EmitDefaultValue = false)] + public string Decision { get; set; } + + /// + /// The unique identifier of the network token. + /// + /// The unique identifier of the network token. + [DataMember(Name = "id", EmitDefaultValue = false)] + public string Id { get; set; } + + /// + /// The unique identifier of the payment instrument to which the network token is associated. + /// + /// The unique identifier of the payment instrument to which the network token is associated. + [DataMember(Name = "paymentInstrumentId", EmitDefaultValue = false)] + public string PaymentInstrumentId { get; set; } + + /// + /// The status of the network token. + /// + /// The status of the network token. + [DataMember(Name = "status", EmitDefaultValue = false)] + public string Status { get; set; } + + /// + /// The last four digits of the network token. Use this value to help your user to identify their network token. + /// + /// The last four digits of the network token. Use this value to help your user to identify their network token. + [DataMember(Name = "tokenLastFour", EmitDefaultValue = false)] + public string TokenLastFour { get; set; } + + /// + /// Gets or Sets TokenRequestor + /// + [DataMember(Name = "tokenRequestor", EmitDefaultValue = false)] + public NetworkTokenRequestor TokenRequestor { get; set; } + + /// + /// The type of network token. + /// + /// The type of network token. + [DataMember(Name = "type", EmitDefaultValue = false)] + public string Type { get; set; } + + /// + /// The rules used to validate the request for provisioning the network token. + /// + /// The rules used to validate the request for provisioning the network token. + [DataMember(Name = "validationFacts", EmitDefaultValue = false)] + public List ValidationFacts { get; set; } + + /// + /// Gets or Sets Wallet + /// + [DataMember(Name = "wallet", EmitDefaultValue = false)] + public Wallet Wallet { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class NetworkTokenNotificationDataV2 {\n"); + sb.Append(" Authentication: ").Append(Authentication).Append("\n"); + sb.Append(" AuthenticationApplied: ").Append(AuthenticationApplied).Append("\n"); + sb.Append(" BalancePlatform: ").Append(BalancePlatform).Append("\n"); + sb.Append(" Decision: ").Append(Decision).Append("\n"); + sb.Append(" Id: ").Append(Id).Append("\n"); + sb.Append(" PaymentInstrumentId: ").Append(PaymentInstrumentId).Append("\n"); + sb.Append(" Status: ").Append(Status).Append("\n"); + sb.Append(" TokenLastFour: ").Append(TokenLastFour).Append("\n"); + sb.Append(" TokenRequestor: ").Append(TokenRequestor).Append("\n"); + sb.Append(" Type: ").Append(Type).Append("\n"); + sb.Append(" ValidationFacts: ").Append(ValidationFacts).Append("\n"); + sb.Append(" Wallet: ").Append(Wallet).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as NetworkTokenNotificationDataV2); + } + + /// + /// Returns true if NetworkTokenNotificationDataV2 instances are equal + /// + /// Instance of NetworkTokenNotificationDataV2 to be compared + /// Boolean + public bool Equals(NetworkTokenNotificationDataV2 input) + { + if (input == null) + { + return false; + } + return + ( + this.Authentication == input.Authentication || + (this.Authentication != null && + this.Authentication.Equals(input.Authentication)) + ) && + ( + this.AuthenticationApplied == input.AuthenticationApplied || + this.AuthenticationApplied.Equals(input.AuthenticationApplied) + ) && + ( + this.BalancePlatform == input.BalancePlatform || + (this.BalancePlatform != null && + this.BalancePlatform.Equals(input.BalancePlatform)) + ) && + ( + this.Decision == input.Decision || + (this.Decision != null && + this.Decision.Equals(input.Decision)) + ) && + ( + this.Id == input.Id || + (this.Id != null && + this.Id.Equals(input.Id)) + ) && + ( + this.PaymentInstrumentId == input.PaymentInstrumentId || + (this.PaymentInstrumentId != null && + this.PaymentInstrumentId.Equals(input.PaymentInstrumentId)) + ) && + ( + this.Status == input.Status || + (this.Status != null && + this.Status.Equals(input.Status)) + ) && + ( + this.TokenLastFour == input.TokenLastFour || + (this.TokenLastFour != null && + this.TokenLastFour.Equals(input.TokenLastFour)) + ) && + ( + this.TokenRequestor == input.TokenRequestor || + (this.TokenRequestor != null && + this.TokenRequestor.Equals(input.TokenRequestor)) + ) && + ( + this.Type == input.Type || + (this.Type != null && + this.Type.Equals(input.Type)) + ) && + ( + this.ValidationFacts == input.ValidationFacts || + this.ValidationFacts != null && + input.ValidationFacts != null && + this.ValidationFacts.SequenceEqual(input.ValidationFacts) + ) && + ( + this.Wallet == input.Wallet || + (this.Wallet != null && + this.Wallet.Equals(input.Wallet)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Authentication != null) + { + hashCode = (hashCode * 59) + this.Authentication.GetHashCode(); + } + hashCode = (hashCode * 59) + this.AuthenticationApplied.GetHashCode(); + if (this.BalancePlatform != null) + { + hashCode = (hashCode * 59) + this.BalancePlatform.GetHashCode(); + } + if (this.Decision != null) + { + hashCode = (hashCode * 59) + this.Decision.GetHashCode(); + } + if (this.Id != null) + { + hashCode = (hashCode * 59) + this.Id.GetHashCode(); + } + if (this.PaymentInstrumentId != null) + { + hashCode = (hashCode * 59) + this.PaymentInstrumentId.GetHashCode(); + } + if (this.Status != null) + { + hashCode = (hashCode * 59) + this.Status.GetHashCode(); + } + if (this.TokenLastFour != null) + { + hashCode = (hashCode * 59) + this.TokenLastFour.GetHashCode(); + } + if (this.TokenRequestor != null) + { + hashCode = (hashCode * 59) + this.TokenRequestor.GetHashCode(); + } + if (this.Type != null) + { + hashCode = (hashCode * 59) + this.Type.GetHashCode(); + } + if (this.ValidationFacts != null) + { + hashCode = (hashCode * 59) + this.ValidationFacts.GetHashCode(); + } + if (this.Wallet != null) + { + hashCode = (hashCode * 59) + this.Wallet.GetHashCode(); + } + return hashCode; + } + } + /// + /// To validate all properties of the instance + /// + /// Validation context + /// Validation Result + public IEnumerable Validate(ValidationContext validationContext) + { + yield break; + } + } + +} diff --git a/Adyen/Model/ConfigurationWebhooks/NetworkTokenNotificationRequest.cs b/Adyen/Model/ConfigurationWebhooks/NetworkTokenNotificationRequest.cs new file mode 100644 index 000000000..4b080ddf6 --- /dev/null +++ b/Adyen/Model/ConfigurationWebhooks/NetworkTokenNotificationRequest.cs @@ -0,0 +1,207 @@ +/* +* Configuration webhooks +* +* +* The version of the OpenAPI document: 2 +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using System.ComponentModel.DataAnnotations; +using OpenAPIDateConverter = Adyen.ApiSerialization.OpenAPIDateConverter; + +namespace Adyen.Model.ConfigurationWebhooks +{ + /// + /// NetworkTokenNotificationRequest + /// + [DataContract(Name = "NetworkTokenNotificationRequest")] + public partial class NetworkTokenNotificationRequest : IEquatable, IValidatableObject + { + /// + /// The type of webhook. + /// + /// The type of webhook. + [JsonConverter(typeof(StringEnumConverter))] + public enum TypeEnum + { + /// + /// Enum Created for value: balancePlatform.networkToken.created + /// + [EnumMember(Value = "balancePlatform.networkToken.created")] + Created = 1, + + /// + /// Enum Updated for value: balancePlatform.networkToken.updated + /// + [EnumMember(Value = "balancePlatform.networkToken.updated")] + Updated = 2 + + } + + + /// + /// The type of webhook. + /// + /// The type of webhook. + [DataMember(Name = "type", IsRequired = false, EmitDefaultValue = false)] + public TypeEnum Type { get; set; } + /// + /// Initializes a new instance of the class. + /// + [JsonConstructorAttribute] + protected NetworkTokenNotificationRequest() { } + /// + /// Initializes a new instance of the class. + /// + /// data (required). + /// The environment from which the webhook originated. Possible values: **test**, **live**. (required). + /// When the event was queued.. + /// The type of webhook. (required). + public NetworkTokenNotificationRequest(NetworkTokenNotificationDataV2 data = default(NetworkTokenNotificationDataV2), string environment = default(string), DateTime timestamp = default(DateTime), TypeEnum type = default(TypeEnum)) + { + this.Data = data; + this.Environment = environment; + this.Type = type; + this.Timestamp = timestamp; + } + + /// + /// Gets or Sets Data + /// + [DataMember(Name = "data", IsRequired = false, EmitDefaultValue = false)] + public NetworkTokenNotificationDataV2 Data { get; set; } + + /// + /// The environment from which the webhook originated. Possible values: **test**, **live**. + /// + /// The environment from which the webhook originated. Possible values: **test**, **live**. + [DataMember(Name = "environment", IsRequired = false, EmitDefaultValue = false)] + public string Environment { get; set; } + + /// + /// When the event was queued. + /// + /// When the event was queued. + [DataMember(Name = "timestamp", EmitDefaultValue = false)] + public DateTime Timestamp { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class NetworkTokenNotificationRequest {\n"); + sb.Append(" Data: ").Append(Data).Append("\n"); + sb.Append(" Environment: ").Append(Environment).Append("\n"); + sb.Append(" Timestamp: ").Append(Timestamp).Append("\n"); + sb.Append(" Type: ").Append(Type).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as NetworkTokenNotificationRequest); + } + + /// + /// Returns true if NetworkTokenNotificationRequest instances are equal + /// + /// Instance of NetworkTokenNotificationRequest to be compared + /// Boolean + public bool Equals(NetworkTokenNotificationRequest input) + { + if (input == null) + { + return false; + } + return + ( + this.Data == input.Data || + (this.Data != null && + this.Data.Equals(input.Data)) + ) && + ( + this.Environment == input.Environment || + (this.Environment != null && + this.Environment.Equals(input.Environment)) + ) && + ( + this.Timestamp == input.Timestamp || + (this.Timestamp != null && + this.Timestamp.Equals(input.Timestamp)) + ) && + ( + this.Type == input.Type || + this.Type.Equals(input.Type) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Data != null) + { + hashCode = (hashCode * 59) + this.Data.GetHashCode(); + } + if (this.Environment != null) + { + hashCode = (hashCode * 59) + this.Environment.GetHashCode(); + } + if (this.Timestamp != null) + { + hashCode = (hashCode * 59) + this.Timestamp.GetHashCode(); + } + hashCode = (hashCode * 59) + this.Type.GetHashCode(); + return hashCode; + } + } + /// + /// To validate all properties of the instance + /// + /// Validation context + /// Validation Result + public IEnumerable Validate(ValidationContext validationContext) + { + yield break; + } + } + +} diff --git a/Adyen/Model/ConfigurationWebhooks/NetworkTokenRequestor.cs b/Adyen/Model/ConfigurationWebhooks/NetworkTokenRequestor.cs new file mode 100644 index 000000000..a46d337f8 --- /dev/null +++ b/Adyen/Model/ConfigurationWebhooks/NetworkTokenRequestor.cs @@ -0,0 +1,148 @@ +/* +* Configuration webhooks +* +* +* The version of the OpenAPI document: 2 +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using System.ComponentModel.DataAnnotations; +using OpenAPIDateConverter = Adyen.ApiSerialization.OpenAPIDateConverter; + +namespace Adyen.Model.ConfigurationWebhooks +{ + /// + /// NetworkTokenRequestor + /// + [DataContract(Name = "NetworkTokenRequestor")] + public partial class NetworkTokenRequestor : IEquatable, IValidatableObject + { + /// + /// Initializes a new instance of the class. + /// + /// The id of the network token requestor.. + /// The name of the network token requestor.. + public NetworkTokenRequestor(string id = default(string), string name = default(string)) + { + this.Id = id; + this.Name = name; + } + + /// + /// The id of the network token requestor. + /// + /// The id of the network token requestor. + [DataMember(Name = "id", EmitDefaultValue = false)] + public string Id { get; set; } + + /// + /// The name of the network token requestor. + /// + /// The name of the network token requestor. + [DataMember(Name = "name", EmitDefaultValue = false)] + public string Name { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class NetworkTokenRequestor {\n"); + sb.Append(" Id: ").Append(Id).Append("\n"); + sb.Append(" Name: ").Append(Name).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as NetworkTokenRequestor); + } + + /// + /// Returns true if NetworkTokenRequestor instances are equal + /// + /// Instance of NetworkTokenRequestor to be compared + /// Boolean + public bool Equals(NetworkTokenRequestor input) + { + if (input == null) + { + return false; + } + return + ( + this.Id == input.Id || + (this.Id != null && + this.Id.Equals(input.Id)) + ) && + ( + this.Name == input.Name || + (this.Name != null && + this.Name.Equals(input.Name)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Id != null) + { + hashCode = (hashCode * 59) + this.Id.GetHashCode(); + } + if (this.Name != null) + { + hashCode = (hashCode * 59) + this.Name.GetHashCode(); + } + return hashCode; + } + } + /// + /// To validate all properties of the instance + /// + /// Validation context + /// Validation Result + public IEnumerable Validate(ValidationContext validationContext) + { + yield break; + } + } + +} diff --git a/Adyen/Model/ConfigurationWebhooks/PlatformPaymentConfiguration.cs b/Adyen/Model/ConfigurationWebhooks/PlatformPaymentConfiguration.cs index ba2c0c489..fbb4fd82f 100644 --- a/Adyen/Model/ConfigurationWebhooks/PlatformPaymentConfiguration.cs +++ b/Adyen/Model/ConfigurationWebhooks/PlatformPaymentConfiguration.cs @@ -35,8 +35,8 @@ public partial class PlatformPaymentConfiguration : IEquatable /// Initializes a new instance of the class. /// - /// Specifies at what time a [sales day](https://docs.adyen.com/platforms/settle-funds/sales-day-settlement#sales-day) ends for this account. Possible values: Time in **\"HH:MM\"** format. **HH** ranges from **00** to **07**. **MM** must be **00**. Default value: **\"00:00\"**.. - /// Specifies after how many business days the funds in a [settlement batch](https://docs.adyen.com/platforms/settle-funds/sales-day-settlement#settlement-batch) are made available in this balance account. Possible values: **1** to **20**, or **null**. * Setting this value to an integer enables Sales day settlement in this balance account. See how Sales day settlement works in your [marketplace](https://docs.adyen.com/marketplaces/settle-funds/sales-day-settlement) or [platform](https://docs.adyen.com/platforms/settle-funds/sales-day-settlement). * Setting this value to **null** enables Pass-through settlement in this balance account. See how Pass-through settlement works in your [marketplace](https://docs.adyen.com/marketplaces/settle-funds/pass-through-settlement) or [platform](https://docs.adyen.com/platforms/settle-funds/pass-through-settlement). Default value: **null**.. + /// Specifies at what time a sales day ends for this account. Possible values: Time in **\"HH:MM\"** format. **HH** ranges from **00** to **07**. **MM** must be **00**. Default value: **\"00:00\"**.. + /// Specifies after how many business days the funds in a settlement batch are made available in this balance account. Possible values: **1** to **20**, or **null**. Default value: **null**.. public PlatformPaymentConfiguration(string salesDayClosingTime = default(string), int? settlementDelayDays = default(int?)) { this.SalesDayClosingTime = salesDayClosingTime; @@ -44,16 +44,16 @@ public partial class PlatformPaymentConfiguration : IEquatable - /// Specifies at what time a [sales day](https://docs.adyen.com/platforms/settle-funds/sales-day-settlement#sales-day) ends for this account. Possible values: Time in **\"HH:MM\"** format. **HH** ranges from **00** to **07**. **MM** must be **00**. Default value: **\"00:00\"**. + /// Specifies at what time a sales day ends for this account. Possible values: Time in **\"HH:MM\"** format. **HH** ranges from **00** to **07**. **MM** must be **00**. Default value: **\"00:00\"**. /// - /// Specifies at what time a [sales day](https://docs.adyen.com/platforms/settle-funds/sales-day-settlement#sales-day) ends for this account. Possible values: Time in **\"HH:MM\"** format. **HH** ranges from **00** to **07**. **MM** must be **00**. Default value: **\"00:00\"**. + /// Specifies at what time a sales day ends for this account. Possible values: Time in **\"HH:MM\"** format. **HH** ranges from **00** to **07**. **MM** must be **00**. Default value: **\"00:00\"**. [DataMember(Name = "salesDayClosingTime", EmitDefaultValue = false)] public string SalesDayClosingTime { get; set; } /// - /// Specifies after how many business days the funds in a [settlement batch](https://docs.adyen.com/platforms/settle-funds/sales-day-settlement#settlement-batch) are made available in this balance account. Possible values: **1** to **20**, or **null**. * Setting this value to an integer enables Sales day settlement in this balance account. See how Sales day settlement works in your [marketplace](https://docs.adyen.com/marketplaces/settle-funds/sales-day-settlement) or [platform](https://docs.adyen.com/platforms/settle-funds/sales-day-settlement). * Setting this value to **null** enables Pass-through settlement in this balance account. See how Pass-through settlement works in your [marketplace](https://docs.adyen.com/marketplaces/settle-funds/pass-through-settlement) or [platform](https://docs.adyen.com/platforms/settle-funds/pass-through-settlement). Default value: **null**. + /// Specifies after how many business days the funds in a settlement batch are made available in this balance account. Possible values: **1** to **20**, or **null**. Default value: **null**. /// - /// Specifies after how many business days the funds in a [settlement batch](https://docs.adyen.com/platforms/settle-funds/sales-day-settlement#settlement-batch) are made available in this balance account. Possible values: **1** to **20**, or **null**. * Setting this value to an integer enables Sales day settlement in this balance account. See how Sales day settlement works in your [marketplace](https://docs.adyen.com/marketplaces/settle-funds/sales-day-settlement) or [platform](https://docs.adyen.com/platforms/settle-funds/sales-day-settlement). * Setting this value to **null** enables Pass-through settlement in this balance account. See how Pass-through settlement works in your [marketplace](https://docs.adyen.com/marketplaces/settle-funds/pass-through-settlement) or [platform](https://docs.adyen.com/platforms/settle-funds/pass-through-settlement). Default value: **null**. + /// Specifies after how many business days the funds in a settlement batch are made available in this balance account. Possible values: **1** to **20**, or **null**. Default value: **null**. [DataMember(Name = "settlementDelayDays", EmitDefaultValue = false)] public int? SettlementDelayDays { get; set; } diff --git a/Adyen/Model/ConfigurationWebhooks/SweepConfigurationV2.cs b/Adyen/Model/ConfigurationWebhooks/SweepConfigurationV2.cs index bf9a72e76..600e905ce 100644 --- a/Adyen/Model/ConfigurationWebhooks/SweepConfigurationV2.cs +++ b/Adyen/Model/ConfigurationWebhooks/SweepConfigurationV2.cs @@ -113,9 +113,9 @@ public enum PrioritiesEnum /// - /// The list of priorities for the bank transfer. This sets the speed at which the transfer is sent and the fees that you have to pay. You can provide multiple priorities, ordered by your preference. Adyen will try to pay out using the priorities in the given order. If the first priority is not currently supported or enabled for your platform, the system will try the next one, and so on. The request will be accepted as long as **at least one** of the provided priorities is valid (i.e., supported by Adyen and activated for your platform). For example, if you provide `[\"wire\",\"regular\"]`, and `wire` is not supported but `regular` is, the request will still be accepted and processed. Possible values: * **regular**: for normal, low-value transactions. * **fast**: a faster way to transfer funds, but the fees are higher. Recommended for high-priority, low-value transactions. * **wire**: the fastest way to transfer funds, but this has the highest fees. Recommended for high-priority, high-value transactions. * **instant**: for instant funds transfers in [SEPA countries](https://www.ecb.europa.eu/paym/integration/retail/sepa/html/index.en.html). * **crossBorder**: for high-value transfers to a recipient in a different country. * **internal**: for transfers to an Adyen-issued business bank account (by bank account number/IBAN). Set `category` to **bank**. For more details, see optional priorities setup for [marketplaces](https://docs.adyen.com/marketplaces/payout-to-users/scheduled-payouts#optional-priorities-setup) or [platforms](https://docs.adyen.com/platforms/payout-to-users/scheduled-payouts#optional-priorities-setup). + /// The list of priorities for the bank transfer. This sets the speed at which the transfer is sent and the fees that you have to pay. You can provide multiple priorities, ordered by your preference. Adyen will try to pay out using the priorities in the given order. If the first priority is not currently supported or enabled for your platform, the system will try the next one, and so on. The request will be accepted as long as **at least one** of the provided priorities is valid (i.e., supported by Adyen and activated for your platform). For example, if you provide `[\"wire\",\"regular\"]`, and `wire` is not supported but `regular` is, the request will still be accepted and processed. Possible values: * **regular**: for normal, low-value transactions. * **fast**: a faster way to transfer funds, but the fees are higher. Recommended for high-priority, low-value transactions. * **wire**: the fastest way to transfer funds, but this has the highest fees. Recommended for high-priority, high-value transactions. * **instant**: for instant funds transfers within the United States and in [SEPA locations](https://www.ecb.europa.eu/paym/integration/retail/sepa/html/index.en.html). * **crossBorder**: for high-value transfers to a recipient in a different country. * **internal**: for transfers to an Adyen-issued business bank account (by bank account number/IBAN). Set `category` to **bank**. For more details, see optional priorities setup for [marketplaces](https://docs.adyen.com/marketplaces/payout-to-users/scheduled-payouts#optional-priorities-setup) or [platforms](https://docs.adyen.com/platforms/payout-to-users/scheduled-payouts#optional-priorities-setup). /// - /// The list of priorities for the bank transfer. This sets the speed at which the transfer is sent and the fees that you have to pay. You can provide multiple priorities, ordered by your preference. Adyen will try to pay out using the priorities in the given order. If the first priority is not currently supported or enabled for your platform, the system will try the next one, and so on. The request will be accepted as long as **at least one** of the provided priorities is valid (i.e., supported by Adyen and activated for your platform). For example, if you provide `[\"wire\",\"regular\"]`, and `wire` is not supported but `regular` is, the request will still be accepted and processed. Possible values: * **regular**: for normal, low-value transactions. * **fast**: a faster way to transfer funds, but the fees are higher. Recommended for high-priority, low-value transactions. * **wire**: the fastest way to transfer funds, but this has the highest fees. Recommended for high-priority, high-value transactions. * **instant**: for instant funds transfers in [SEPA countries](https://www.ecb.europa.eu/paym/integration/retail/sepa/html/index.en.html). * **crossBorder**: for high-value transfers to a recipient in a different country. * **internal**: for transfers to an Adyen-issued business bank account (by bank account number/IBAN). Set `category` to **bank**. For more details, see optional priorities setup for [marketplaces](https://docs.adyen.com/marketplaces/payout-to-users/scheduled-payouts#optional-priorities-setup) or [platforms](https://docs.adyen.com/platforms/payout-to-users/scheduled-payouts#optional-priorities-setup). + /// The list of priorities for the bank transfer. This sets the speed at which the transfer is sent and the fees that you have to pay. You can provide multiple priorities, ordered by your preference. Adyen will try to pay out using the priorities in the given order. If the first priority is not currently supported or enabled for your platform, the system will try the next one, and so on. The request will be accepted as long as **at least one** of the provided priorities is valid (i.e., supported by Adyen and activated for your platform). For example, if you provide `[\"wire\",\"regular\"]`, and `wire` is not supported but `regular` is, the request will still be accepted and processed. Possible values: * **regular**: for normal, low-value transactions. * **fast**: a faster way to transfer funds, but the fees are higher. Recommended for high-priority, low-value transactions. * **wire**: the fastest way to transfer funds, but this has the highest fees. Recommended for high-priority, high-value transactions. * **instant**: for instant funds transfers within the United States and in [SEPA locations](https://www.ecb.europa.eu/paym/integration/retail/sepa/html/index.en.html). * **crossBorder**: for high-value transfers to a recipient in a different country. * **internal**: for transfers to an Adyen-issued business bank account (by bank account number/IBAN). Set `category` to **bank**. For more details, see optional priorities setup for [marketplaces](https://docs.adyen.com/marketplaces/payout-to-users/scheduled-payouts#optional-priorities-setup) or [platforms](https://docs.adyen.com/platforms/payout-to-users/scheduled-payouts#optional-priorities-setup). [DataMember(Name = "priorities", EmitDefaultValue = false)] public List Priorities { get; set; } /// @@ -347,7 +347,7 @@ protected SweepConfigurationV2() { } /// The three-character [ISO currency code](https://docs.adyen.com/development-resources/currency-codes) in uppercase. For example, **EUR**. The sweep currency must match any of the [balances currencies](https://docs.adyen.com/api-explorer/#/balanceplatform/latest/get/balanceAccounts/{id}__resParam_balances). (required). /// The message that will be used in the sweep transfer's description body with a maximum length of 140 characters. If the message is longer after replacing placeholders, the message will be cut off at 140 characters.. /// The unique identifier of the sweep. (required). - /// The list of priorities for the bank transfer. This sets the speed at which the transfer is sent and the fees that you have to pay. You can provide multiple priorities, ordered by your preference. Adyen will try to pay out using the priorities in the given order. If the first priority is not currently supported or enabled for your platform, the system will try the next one, and so on. The request will be accepted as long as **at least one** of the provided priorities is valid (i.e., supported by Adyen and activated for your platform). For example, if you provide `[\"wire\",\"regular\"]`, and `wire` is not supported but `regular` is, the request will still be accepted and processed. Possible values: * **regular**: for normal, low-value transactions. * **fast**: a faster way to transfer funds, but the fees are higher. Recommended for high-priority, low-value transactions. * **wire**: the fastest way to transfer funds, but this has the highest fees. Recommended for high-priority, high-value transactions. * **instant**: for instant funds transfers in [SEPA countries](https://www.ecb.europa.eu/paym/integration/retail/sepa/html/index.en.html). * **crossBorder**: for high-value transfers to a recipient in a different country. * **internal**: for transfers to an Adyen-issued business bank account (by bank account number/IBAN). Set `category` to **bank**. For more details, see optional priorities setup for [marketplaces](https://docs.adyen.com/marketplaces/payout-to-users/scheduled-payouts#optional-priorities-setup) or [platforms](https://docs.adyen.com/platforms/payout-to-users/scheduled-payouts#optional-priorities-setup).. + /// The list of priorities for the bank transfer. This sets the speed at which the transfer is sent and the fees that you have to pay. You can provide multiple priorities, ordered by your preference. Adyen will try to pay out using the priorities in the given order. If the first priority is not currently supported or enabled for your platform, the system will try the next one, and so on. The request will be accepted as long as **at least one** of the provided priorities is valid (i.e., supported by Adyen and activated for your platform). For example, if you provide `[\"wire\",\"regular\"]`, and `wire` is not supported but `regular` is, the request will still be accepted and processed. Possible values: * **regular**: for normal, low-value transactions. * **fast**: a faster way to transfer funds, but the fees are higher. Recommended for high-priority, low-value transactions. * **wire**: the fastest way to transfer funds, but this has the highest fees. Recommended for high-priority, high-value transactions. * **instant**: for instant funds transfers within the United States and in [SEPA locations](https://www.ecb.europa.eu/paym/integration/retail/sepa/html/index.en.html). * **crossBorder**: for high-value transfers to a recipient in a different country. * **internal**: for transfers to an Adyen-issued business bank account (by bank account number/IBAN). Set `category` to **bank**. For more details, see optional priorities setup for [marketplaces](https://docs.adyen.com/marketplaces/payout-to-users/scheduled-payouts#optional-priorities-setup) or [platforms](https://docs.adyen.com/platforms/payout-to-users/scheduled-payouts#optional-priorities-setup).. /// The reason for disabling the sweep.. /// The human readable reason for disabling the sweep.. /// Your reference for the sweep configuration.. diff --git a/Adyen/Model/ConfigurationWebhooks/TokenAuthentication.cs b/Adyen/Model/ConfigurationWebhooks/TokenAuthentication.cs new file mode 100644 index 000000000..2624aaf5d --- /dev/null +++ b/Adyen/Model/ConfigurationWebhooks/TokenAuthentication.cs @@ -0,0 +1,148 @@ +/* +* Configuration webhooks +* +* +* The version of the OpenAPI document: 2 +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using System.ComponentModel.DataAnnotations; +using OpenAPIDateConverter = Adyen.ApiSerialization.OpenAPIDateConverter; + +namespace Adyen.Model.ConfigurationWebhooks +{ + /// + /// TokenAuthentication + /// + [DataContract(Name = "TokenAuthentication")] + public partial class TokenAuthentication : IEquatable, IValidatableObject + { + /// + /// Initializes a new instance of the class. + /// + /// The method used to complete the authentication process. Possible values: **sms_OTP**, **email_OTP**.. + /// The result of the authentication process.. + public TokenAuthentication(string method = default(string), string result = default(string)) + { + this.Method = method; + this.Result = result; + } + + /// + /// The method used to complete the authentication process. Possible values: **sms_OTP**, **email_OTP**. + /// + /// The method used to complete the authentication process. Possible values: **sms_OTP**, **email_OTP**. + [DataMember(Name = "method", EmitDefaultValue = false)] + public string Method { get; set; } + + /// + /// The result of the authentication process. + /// + /// The result of the authentication process. + [DataMember(Name = "result", EmitDefaultValue = false)] + public string Result { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class TokenAuthentication {\n"); + sb.Append(" Method: ").Append(Method).Append("\n"); + sb.Append(" Result: ").Append(Result).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as TokenAuthentication); + } + + /// + /// Returns true if TokenAuthentication instances are equal + /// + /// Instance of TokenAuthentication to be compared + /// Boolean + public bool Equals(TokenAuthentication input) + { + if (input == null) + { + return false; + } + return + ( + this.Method == input.Method || + (this.Method != null && + this.Method.Equals(input.Method)) + ) && + ( + this.Result == input.Result || + (this.Result != null && + this.Result.Equals(input.Result)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Method != null) + { + hashCode = (hashCode * 59) + this.Method.GetHashCode(); + } + if (this.Result != null) + { + hashCode = (hashCode * 59) + this.Result.GetHashCode(); + } + return hashCode; + } + } + /// + /// To validate all properties of the instance + /// + /// Validation context + /// Validation Result + public IEnumerable Validate(ValidationContext validationContext) + { + yield break; + } + } + +} diff --git a/Adyen/Model/ConfigurationWebhooks/ValidationFacts.cs b/Adyen/Model/ConfigurationWebhooks/ValidationFacts.cs new file mode 100644 index 000000000..a32b90f8d --- /dev/null +++ b/Adyen/Model/ConfigurationWebhooks/ValidationFacts.cs @@ -0,0 +1,197 @@ +/* +* Configuration webhooks +* +* +* The version of the OpenAPI document: 2 +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using System.ComponentModel.DataAnnotations; +using OpenAPIDateConverter = Adyen.ApiSerialization.OpenAPIDateConverter; + +namespace Adyen.Model.ConfigurationWebhooks +{ + /// + /// ValidationFacts + /// + [DataContract(Name = "ValidationFacts")] + public partial class ValidationFacts : IEquatable, IValidatableObject + { + /// + /// The evaluation result of the validation facts. Possible values: **valid**, **invalid**, **notValidated**, **notApplicable**. + /// + /// The evaluation result of the validation facts. Possible values: **valid**, **invalid**, **notValidated**, **notApplicable**. + [JsonConverter(typeof(StringEnumConverter))] + public enum ResultEnum + { + /// + /// Enum Invalid for value: invalid + /// + [EnumMember(Value = "invalid")] + Invalid = 1, + + /// + /// Enum NotApplicable for value: notApplicable + /// + [EnumMember(Value = "notApplicable")] + NotApplicable = 2, + + /// + /// Enum NotValidated for value: notValidated + /// + [EnumMember(Value = "notValidated")] + NotValidated = 3, + + /// + /// Enum Valid for value: valid + /// + [EnumMember(Value = "valid")] + Valid = 4 + + } + + + /// + /// The evaluation result of the validation facts. Possible values: **valid**, **invalid**, **notValidated**, **notApplicable**. + /// + /// The evaluation result of the validation facts. Possible values: **valid**, **invalid**, **notValidated**, **notApplicable**. + [DataMember(Name = "result", EmitDefaultValue = false)] + public ResultEnum? Result { get; set; } + /// + /// Initializes a new instance of the class. + /// + /// The reason for the `result` of the validations. This field is only sent for `validationFacts.type` **walletValidation**, when `validationFacts.result` is **invalid**.. + /// The evaluation result of the validation facts. Possible values: **valid**, **invalid**, **notValidated**, **notApplicable**.. + /// The type of the validation fact.. + public ValidationFacts(List reasons = default(List), ResultEnum? result = default(ResultEnum?), string type = default(string)) + { + this.Reasons = reasons; + this.Result = result; + this.Type = type; + } + + /// + /// The reason for the `result` of the validations. This field is only sent for `validationFacts.type` **walletValidation**, when `validationFacts.result` is **invalid**. + /// + /// The reason for the `result` of the validations. This field is only sent for `validationFacts.type` **walletValidation**, when `validationFacts.result` is **invalid**. + [DataMember(Name = "reasons", EmitDefaultValue = false)] + public List Reasons { get; set; } + + /// + /// The type of the validation fact. + /// + /// The type of the validation fact. + [DataMember(Name = "type", EmitDefaultValue = false)] + public string Type { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class ValidationFacts {\n"); + sb.Append(" Reasons: ").Append(Reasons).Append("\n"); + sb.Append(" Result: ").Append(Result).Append("\n"); + sb.Append(" Type: ").Append(Type).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as ValidationFacts); + } + + /// + /// Returns true if ValidationFacts instances are equal + /// + /// Instance of ValidationFacts to be compared + /// Boolean + public bool Equals(ValidationFacts input) + { + if (input == null) + { + return false; + } + return + ( + this.Reasons == input.Reasons || + this.Reasons != null && + input.Reasons != null && + this.Reasons.SequenceEqual(input.Reasons) + ) && + ( + this.Result == input.Result || + this.Result.Equals(input.Result) + ) && + ( + this.Type == input.Type || + (this.Type != null && + this.Type.Equals(input.Type)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.Reasons != null) + { + hashCode = (hashCode * 59) + this.Reasons.GetHashCode(); + } + hashCode = (hashCode * 59) + this.Result.GetHashCode(); + if (this.Type != null) + { + hashCode = (hashCode * 59) + this.Type.GetHashCode(); + } + return hashCode; + } + } + /// + /// To validate all properties of the instance + /// + /// Validation context + /// Validation Result + public IEnumerable Validate(ValidationContext validationContext) + { + yield break; + } + } + +} diff --git a/Adyen/Model/ConfigurationWebhooks/VerificationError.cs b/Adyen/Model/ConfigurationWebhooks/VerificationError.cs index f53da3050..13056cb02 100644 --- a/Adyen/Model/ConfigurationWebhooks/VerificationError.cs +++ b/Adyen/Model/ConfigurationWebhooks/VerificationError.cs @@ -379,9 +379,9 @@ public enum CapabilitiesEnum [DataMember(Name = "capabilities", EmitDefaultValue = false)] public List Capabilities { get; set; } /// - /// The type of error. Possible values: **invalidInput**, **dataMissing**. + /// The type of error. Possible values: * **invalidInput** * **dataMissing** * **pendingStatus** * **dataReview** /// - /// The type of error. Possible values: **invalidInput**, **dataMissing**. + /// The type of error. Possible values: * **invalidInput** * **dataMissing** * **pendingStatus** * **dataReview** [JsonConverter(typeof(StringEnumConverter))] public enum TypeEnum { @@ -401,15 +401,20 @@ public enum TypeEnum /// Enum PendingStatus for value: pendingStatus /// [EnumMember(Value = "pendingStatus")] - PendingStatus = 3 - + PendingStatus = 3, + + /// + /// Enum DataReview for value: dataReview + /// + [EnumMember(Value = "dataReview")] + DataReview = 4 } /// - /// The type of error. Possible values: **invalidInput**, **dataMissing**. + /// The type of error. Possible values: * **invalidInput** * **dataMissing** * **pendingStatus** * **dataReview** /// - /// The type of error. Possible values: **invalidInput**, **dataMissing**. + /// The type of error. Possible values: * **invalidInput** * **dataMissing** * **pendingStatus** * **dataReview** [DataMember(Name = "type", EmitDefaultValue = false)] public TypeEnum? Type { get; set; } /// @@ -420,7 +425,7 @@ public enum TypeEnum /// A description of the error.. /// Contains the actions that you can take to resolve the verification error.. /// Contains more granular information about the verification error.. - /// The type of error. Possible values: **invalidInput**, **dataMissing**.. + /// The type of error. Possible values: * **invalidInput** * **dataMissing** * **pendingStatus** * **dataReview** . public VerificationError(List capabilities = default(List), string code = default(string), string message = default(string), List remediatingActions = default(List), List subErrors = default(List), TypeEnum? type = default(TypeEnum?)) { this.Capabilities = capabilities; diff --git a/Adyen/Model/ConfigurationWebhooks/VerificationErrorRecursive.cs b/Adyen/Model/ConfigurationWebhooks/VerificationErrorRecursive.cs index f7df3cfaa..0a7dd6603 100644 --- a/Adyen/Model/ConfigurationWebhooks/VerificationErrorRecursive.cs +++ b/Adyen/Model/ConfigurationWebhooks/VerificationErrorRecursive.cs @@ -379,9 +379,9 @@ public enum CapabilitiesEnum [DataMember(Name = "capabilities", EmitDefaultValue = false)] public List Capabilities { get; set; } /// - /// The type of error. Possible values: **invalidInput**, **dataMissing**. + /// The type of error. Possible values: * **invalidInput** * **dataMissing** * **pendingStatus** * **dataReview** /// - /// The type of error. Possible values: **invalidInput**, **dataMissing**. + /// The type of error. Possible values: * **invalidInput** * **dataMissing** * **pendingStatus** * **dataReview** [JsonConverter(typeof(StringEnumConverter))] public enum TypeEnum { @@ -401,15 +401,21 @@ public enum TypeEnum /// Enum PendingStatus for value: pendingStatus /// [EnumMember(Value = "pendingStatus")] - PendingStatus = 3 + PendingStatus = 3, + + /// + /// Enum DataReview for value: dataReview + /// + [EnumMember(Value = "dataReview")] + DataReview = 4 } /// - /// The type of error. Possible values: **invalidInput**, **dataMissing**. + /// The type of error. Possible values: * **invalidInput** * **dataMissing** * **pendingStatus** * **dataReview** /// - /// The type of error. Possible values: **invalidInput**, **dataMissing**. + /// The type of error. Possible values: * **invalidInput** * **dataMissing** * **pendingStatus** * **dataReview** [DataMember(Name = "type", EmitDefaultValue = false)] public TypeEnum? Type { get; set; } /// @@ -418,7 +424,7 @@ public enum TypeEnum /// Contains the capabilities that the verification error applies to.. /// The verification error code.. /// A description of the error.. - /// The type of error. Possible values: **invalidInput**, **dataMissing**.. + /// The type of error. Possible values: * **invalidInput** * **dataMissing** * **pendingStatus** * **dataReview** . /// Contains the actions that you can take to resolve the verification error.. public VerificationErrorRecursive(List capabilities = default(List), string code = default(string), string message = default(string), TypeEnum? type = default(TypeEnum?), List remediatingActions = default(List)) { diff --git a/Adyen/Model/ConfigurationWebhooks/Wallet.cs b/Adyen/Model/ConfigurationWebhooks/Wallet.cs new file mode 100644 index 000000000..1f3e1cb73 --- /dev/null +++ b/Adyen/Model/ConfigurationWebhooks/Wallet.cs @@ -0,0 +1,475 @@ +/* +* Configuration webhooks +* +* +* The version of the OpenAPI document: 2 +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using System.ComponentModel.DataAnnotations; +using OpenAPIDateConverter = Adyen.ApiSerialization.OpenAPIDateConverter; + +namespace Adyen.Model.ConfigurationWebhooks +{ + /// + /// Wallet + /// + [DataContract(Name = "Wallet")] + public partial class Wallet : IEquatable, IValidatableObject + { + /// + /// Defines RecommendationReasons + /// + [JsonConverter(typeof(StringEnumConverter))] + public enum RecommendationReasonsEnum + { + /// + /// Enum AccountCardTooNew for value: accountCardTooNew + /// + [EnumMember(Value = "accountCardTooNew")] + AccountCardTooNew = 1, + + /// + /// Enum AccountHighRisk for value: accountHighRisk + /// + [EnumMember(Value = "accountHighRisk")] + AccountHighRisk = 2, + + /// + /// Enum AccountRecentlyChanged for value: accountRecentlyChanged + /// + [EnumMember(Value = "accountRecentlyChanged")] + AccountRecentlyChanged = 3, + + /// + /// Enum AccountTooNew for value: accountTooNew + /// + [EnumMember(Value = "accountTooNew")] + AccountTooNew = 4, + + /// + /// Enum AccountTooNewSinceLaunch for value: accountTooNewSinceLaunch + /// + [EnumMember(Value = "accountTooNewSinceLaunch")] + AccountTooNewSinceLaunch = 5, + + /// + /// Enum CardholderPanAssociatedToAccountWithinThresholdDays for value: cardholderPanAssociatedToAccountWithinThresholdDays + /// + [EnumMember(Value = "cardholderPanAssociatedToAccountWithinThresholdDays")] + CardholderPanAssociatedToAccountWithinThresholdDays = 6, + + /// + /// Enum ChangesMadeToAccountDataWithinThresholdDays for value: changesMadeToAccountDataWithinThresholdDays + /// + [EnumMember(Value = "changesMadeToAccountDataWithinThresholdDays")] + ChangesMadeToAccountDataWithinThresholdDays = 7, + + /// + /// Enum DeviceProvisioningLocationOutsideOfCardholdersWalletAccountHomeCountry for value: deviceProvisioningLocationOutsideOfCardholdersWalletAccountHomeCountry + /// + [EnumMember(Value = "deviceProvisioningLocationOutsideOfCardholdersWalletAccountHomeCountry")] + DeviceProvisioningLocationOutsideOfCardholdersWalletAccountHomeCountry = 8, + + /// + /// Enum DeviceRecentlyLost for value: deviceRecentlyLost + /// + [EnumMember(Value = "deviceRecentlyLost")] + DeviceRecentlyLost = 9, + + /// + /// Enum EncryptedPaymentInstrumentDataIsBeingPushedByTheIssuerToTheSameDeviceThatIssuerApplicationAuthenticatedButWithSuccessfulUpfrontAuthentication for value: encryptedPaymentInstrumentDataIsBeingPushedByTheIssuerToTheSameDeviceThatIssuerApplicationAuthenticatedButWithSuccessfulUpfrontAuthentication + /// + [EnumMember(Value = "encryptedPaymentInstrumentDataIsBeingPushedByTheIssuerToTheSameDeviceThatIssuerApplicationAuthenticatedButWithSuccessfulUpfrontAuthentication")] + EncryptedPaymentInstrumentDataIsBeingPushedByTheIssuerToTheSameDeviceThatIssuerApplicationAuthenticatedButWithSuccessfulUpfrontAuthentication = 10, + + /// + /// Enum EncryptedPaymentInstrumentDataIsBeingPushedByTheIssuerToTheSameDeviceThatIssuerApplicationAuthenticatedButWithoutAnyUpfrontAuthentication for value: encryptedPaymentInstrumentDataIsBeingPushedByTheIssuerToTheSameDeviceThatIssuerApplicationAuthenticatedButWithoutAnyUpfrontAuthentication + /// + [EnumMember(Value = "encryptedPaymentInstrumentDataIsBeingPushedByTheIssuerToTheSameDeviceThatIssuerApplicationAuthenticatedButWithoutAnyUpfrontAuthentication")] + EncryptedPaymentInstrumentDataIsBeingPushedByTheIssuerToTheSameDeviceThatIssuerApplicationAuthenticatedButWithoutAnyUpfrontAuthentication = 11, + + /// + /// Enum EncryptedPaymentInstrumentDataIsPushedToADifferentDeviceThanTheOneThatIssuerApplicationAuthenticated for value: encryptedPaymentInstrumentDataIsPushedToADifferentDeviceThanTheOneThatIssuerApplicationAuthenticated + /// + [EnumMember(Value = "encryptedPaymentInstrumentDataIsPushedToADifferentDeviceThanTheOneThatIssuerApplicationAuthenticated")] + EncryptedPaymentInstrumentDataIsPushedToADifferentDeviceThanTheOneThatIssuerApplicationAuthenticated = 12, + + /// + /// Enum EncryptedPaymentInstrumentDataIsPushedToADifferentUserThanTheCardHolder for value: encryptedPaymentInstrumentDataIsPushedToADifferentUserThanTheCardHolder + /// + [EnumMember(Value = "encryptedPaymentInstrumentDataIsPushedToADifferentUserThanTheCardHolder")] + EncryptedPaymentInstrumentDataIsPushedToADifferentUserThanTheCardHolder = 13, + + /// + /// Enum HasSuspendedTokens for value: hasSuspendedTokens + /// + [EnumMember(Value = "hasSuspendedTokens")] + HasSuspendedTokens = 14, + + /// + /// Enum InactiveAccount for value: inactiveAccount + /// + [EnumMember(Value = "inactiveAccount")] + InactiveAccount = 15, + + /// + /// Enum IssuerDeferredIDVDecision for value: issuerDeferredIDVDecision + /// + [EnumMember(Value = "issuerDeferredIDVDecision")] + IssuerDeferredIDVDecision = 16, + + /// + /// Enum IssuerEncryptedPaymentInstrumentDataExpired for value: issuerEncryptedPaymentInstrumentDataExpired + /// + [EnumMember(Value = "issuerEncryptedPaymentInstrumentDataExpired")] + IssuerEncryptedPaymentInstrumentDataExpired = 17, + + /// + /// Enum LowAccountScore for value: lowAccountScore + /// + [EnumMember(Value = "lowAccountScore")] + LowAccountScore = 18, + + /// + /// Enum LowDeviceScore for value: lowDeviceScore + /// + [EnumMember(Value = "lowDeviceScore")] + LowDeviceScore = 19, + + /// + /// Enum LowPhoneNumberScore for value: lowPhoneNumberScore + /// + [EnumMember(Value = "lowPhoneNumberScore")] + LowPhoneNumberScore = 20, + + /// + /// Enum NumberOfActiveTokensGreaterThanThreshold for value: numberOfActiveTokensGreaterThanThreshold + /// + [EnumMember(Value = "numberOfActiveTokensGreaterThanThreshold")] + NumberOfActiveTokensGreaterThanThreshold = 21, + + /// + /// Enum NumberOfActiveTokensOnAllDevicesIsGreaterThanThreshold for value: numberOfActiveTokensOnAllDevicesIsGreaterThanThreshold + /// + [EnumMember(Value = "numberOfActiveTokensOnAllDevicesIsGreaterThanThreshold")] + NumberOfActiveTokensOnAllDevicesIsGreaterThanThreshold = 22, + + /// + /// Enum NumberOfDaysSinceDeviceWasLastReportedLostIsLessThanThresholdDays for value: numberOfDaysSinceDeviceWasLastReportedLostIsLessThanThresholdDays + /// + [EnumMember(Value = "numberOfDaysSinceDeviceWasLastReportedLostIsLessThanThresholdDays")] + NumberOfDaysSinceDeviceWasLastReportedLostIsLessThanThresholdDays = 23, + + /// + /// Enum NumberOfDevicesWithSameUseridWithTokenIsGreaterThanThreshold for value: numberOfDevicesWithSameUseridWithTokenIsGreaterThanThreshold + /// + [EnumMember(Value = "numberOfDevicesWithSameUseridWithTokenIsGreaterThanThreshold")] + NumberOfDevicesWithSameUseridWithTokenIsGreaterThanThreshold = 24, + + /// + /// Enum NumberOfTransactionsInLast12MonthsLessThanThresholdNumber for value: numberOfTransactionsInLast12MonthsLessThanThresholdNumber + /// + [EnumMember(Value = "numberOfTransactionsInLast12MonthsLessThanThresholdNumber")] + NumberOfTransactionsInLast12MonthsLessThanThresholdNumber = 25, + + /// + /// Enum OutSideHomeTerritory for value: outSideHomeTerritory + /// + [EnumMember(Value = "outSideHomeTerritory")] + OutSideHomeTerritory = 26, + + /// + /// Enum SuspendedCardsInTheWALLETAccountIsGreaterThanThreshold for value: suspendedCardsInTheWALLETAccountIsGreaterThanThreshold + /// + [EnumMember(Value = "suspendedCardsInTheWALLETAccountIsGreaterThanThreshold")] + SuspendedCardsInTheWALLETAccountIsGreaterThanThreshold = 27, + + /// + /// Enum SuspiciousActivity for value: suspiciousActivity + /// + [EnumMember(Value = "suspiciousActivity")] + SuspiciousActivity = 28, + + /// + /// Enum TheNumberOfProvisioningAttemptsAcrossAllCardsOnThisDeviceInTheLast24HoursExceedsTheThreshold for value: theNumberOfProvisioningAttemptsAcrossAllCardsOnThisDeviceInTheLast24HoursExceedsTheThreshold + /// + [EnumMember(Value = "theNumberOfProvisioningAttemptsAcrossAllCardsOnThisDeviceInTheLast24HoursExceedsTheThreshold")] + TheNumberOfProvisioningAttemptsAcrossAllCardsOnThisDeviceInTheLast24HoursExceedsTheThreshold = 29, + + /// + /// Enum TheWALLETAccountIntoWhichTheCardIsBeingProvisionedContainDistinctNamesGreaterThanThreshold for value: theWALLETAccountIntoWhichTheCardIsBeingProvisionedContainDistinctNamesGreaterThanThreshold + /// + [EnumMember(Value = "theWALLETAccountIntoWhichTheCardIsBeingProvisionedContainDistinctNamesGreaterThanThreshold")] + TheWALLETAccountIntoWhichTheCardIsBeingProvisionedContainDistinctNamesGreaterThanThreshold = 30, + + /// + /// Enum ThisAccountHasNotHadActivityWithinThresholdPeriod for value: thisAccountHasNotHadActivityWithinThresholdPeriod + /// + [EnumMember(Value = "thisAccountHasNotHadActivityWithinThresholdPeriod")] + ThisAccountHasNotHadActivityWithinThresholdPeriod = 31, + + /// + /// Enum TooManyDifferentCardholders for value: tooManyDifferentCardholders + /// + [EnumMember(Value = "tooManyDifferentCardholders")] + TooManyDifferentCardholders = 32, + + /// + /// Enum TooManyRecentAttempts for value: tooManyRecentAttempts + /// + [EnumMember(Value = "tooManyRecentAttempts")] + TooManyRecentAttempts = 33, + + /// + /// Enum TooManyRecentTokens for value: tooManyRecentTokens + /// + [EnumMember(Value = "tooManyRecentTokens")] + TooManyRecentTokens = 34, + + /// + /// Enum UnableToAssess for value: unableToAssess + /// + [EnumMember(Value = "unableToAssess")] + UnableToAssess = 35, + + /// + /// Enum Unknown for value: unknown + /// + [EnumMember(Value = "unknown")] + Unknown = 36, + + /// + /// Enum UserAccountWasCreatedWithinThresholdDays for value: userAccountWasCreatedWithinThresholdDays + /// + [EnumMember(Value = "userAccountWasCreatedWithinThresholdDays")] + UserAccountWasCreatedWithinThresholdDays = 37, + + /// + /// Enum UserDeviceReceivingEncryptedPaymentInstrumentDataIsDifferentThanTheOneThatIsProvisioningTheToken for value: userDeviceReceivingEncryptedPaymentInstrumentDataIsDifferentThanTheOneThatIsProvisioningTheToken + /// + [EnumMember(Value = "userDeviceReceivingEncryptedPaymentInstrumentDataIsDifferentThanTheOneThatIsProvisioningTheToken")] + UserDeviceReceivingEncryptedPaymentInstrumentDataIsDifferentThanTheOneThatIsProvisioningTheToken = 38, + + /// + /// Enum UsersAccountOnDeviceLessThanThresholdDays for value: usersAccountOnDeviceLessThanThresholdDays + /// + [EnumMember(Value = "usersAccountOnDeviceLessThanThresholdDays")] + UsersAccountOnDeviceLessThanThresholdDays = 39, + + /// + /// Enum WalletAccountCreatedWithinThresholdDays for value: walletAccountCreatedWithinThresholdDays + /// + [EnumMember(Value = "walletAccountCreatedWithinThresholdDays")] + WalletAccountCreatedWithinThresholdDays = 40, + + /// + /// Enum WalletAccountHolderNameOnFileDoesNotMatchCardholderEnteredName for value: walletAccountHolderNameOnFileDoesNotMatchCardholderEnteredName + /// + [EnumMember(Value = "walletAccountHolderNameOnFileDoesNotMatchCardholderEnteredName")] + WalletAccountHolderNameOnFileDoesNotMatchCardholderEnteredName = 41 + + } + + + + /// + /// A list of risk indicators triggered at the time of provisioning the network token. Some example values of risk indicators are: * **accountTooNewSinceLaunch** * **tooManyRecentAttempts** * **lowDeviceScore** * **lowAccountScore** + /// + /// A list of risk indicators triggered at the time of provisioning the network token. Some example values of risk indicators are: * **accountTooNewSinceLaunch** * **tooManyRecentAttempts** * **lowDeviceScore** * **lowAccountScore** + [DataMember(Name = "recommendationReasons", EmitDefaultValue = false)] + public List RecommendationReasons { get; set; } + /// + /// Initializes a new instance of the class. + /// + /// The confidence score of the wallet account, calculated by the wallet provider. A high score means that account is considered trustworthy. A low score means that the account is considered suspicious. Possible values: **1** to **5**.. + /// device. + /// The confidence score of the device, calculated by the wallet provider. A high score means that device is considered trustworthy. A low score means that the device is considered suspicious. Possible values: **1** to **5**.. + /// The method used for provisioning the network token. Possible values: **push**, **manual**.. + /// A list of risk indicators triggered at the time of provisioning the network token. Some example values of risk indicators are: * **accountTooNewSinceLaunch** * **tooManyRecentAttempts** * **lowDeviceScore** * **lowAccountScore** . + /// The type of wallet that the network token is associated with. Possible values: **applePay**, **googlePay**, **garminPay**.. + public Wallet(string accountScore = default(string), Device device = default(Device), string deviceScore = default(string), string provisioningMethod = default(string), List recommendationReasons = default(List), string type = default(string)) + { + this.AccountScore = accountScore; + this.Device = device; + this.DeviceScore = deviceScore; + this.ProvisioningMethod = provisioningMethod; + this.RecommendationReasons = recommendationReasons; + this.Type = type; + } + + /// + /// The confidence score of the wallet account, calculated by the wallet provider. A high score means that account is considered trustworthy. A low score means that the account is considered suspicious. Possible values: **1** to **5**. + /// + /// The confidence score of the wallet account, calculated by the wallet provider. A high score means that account is considered trustworthy. A low score means that the account is considered suspicious. Possible values: **1** to **5**. + [DataMember(Name = "accountScore", EmitDefaultValue = false)] + public string AccountScore { get; set; } + + /// + /// Gets or Sets Device + /// + [DataMember(Name = "device", EmitDefaultValue = false)] + public Device Device { get; set; } + + /// + /// The confidence score of the device, calculated by the wallet provider. A high score means that device is considered trustworthy. A low score means that the device is considered suspicious. Possible values: **1** to **5**. + /// + /// The confidence score of the device, calculated by the wallet provider. A high score means that device is considered trustworthy. A low score means that the device is considered suspicious. Possible values: **1** to **5**. + [DataMember(Name = "deviceScore", EmitDefaultValue = false)] + public string DeviceScore { get; set; } + + /// + /// The method used for provisioning the network token. Possible values: **push**, **manual**. + /// + /// The method used for provisioning the network token. Possible values: **push**, **manual**. + [DataMember(Name = "provisioningMethod", EmitDefaultValue = false)] + public string ProvisioningMethod { get; set; } + + /// + /// The type of wallet that the network token is associated with. Possible values: **applePay**, **googlePay**, **garminPay**. + /// + /// The type of wallet that the network token is associated with. Possible values: **applePay**, **googlePay**, **garminPay**. + [DataMember(Name = "type", EmitDefaultValue = false)] + [Obsolete("Deprecated since Configuration webhooks v2. Use name of the `tokenRequestor` instead.")] + public string Type { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class Wallet {\n"); + sb.Append(" AccountScore: ").Append(AccountScore).Append("\n"); + sb.Append(" Device: ").Append(Device).Append("\n"); + sb.Append(" DeviceScore: ").Append(DeviceScore).Append("\n"); + sb.Append(" ProvisioningMethod: ").Append(ProvisioningMethod).Append("\n"); + sb.Append(" RecommendationReasons: ").Append(RecommendationReasons).Append("\n"); + sb.Append(" Type: ").Append(Type).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as Wallet); + } + + /// + /// Returns true if Wallet instances are equal + /// + /// Instance of Wallet to be compared + /// Boolean + public bool Equals(Wallet input) + { + if (input == null) + { + return false; + } + return + ( + this.AccountScore == input.AccountScore || + (this.AccountScore != null && + this.AccountScore.Equals(input.AccountScore)) + ) && + ( + this.Device == input.Device || + (this.Device != null && + this.Device.Equals(input.Device)) + ) && + ( + this.DeviceScore == input.DeviceScore || + (this.DeviceScore != null && + this.DeviceScore.Equals(input.DeviceScore)) + ) && + ( + this.ProvisioningMethod == input.ProvisioningMethod || + (this.ProvisioningMethod != null && + this.ProvisioningMethod.Equals(input.ProvisioningMethod)) + ) && + ( + this.RecommendationReasons == input.RecommendationReasons || + this.RecommendationReasons.SequenceEqual(input.RecommendationReasons) + ) && + ( + this.Type == input.Type || + (this.Type != null && + this.Type.Equals(input.Type)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.AccountScore != null) + { + hashCode = (hashCode * 59) + this.AccountScore.GetHashCode(); + } + if (this.Device != null) + { + hashCode = (hashCode * 59) + this.Device.GetHashCode(); + } + if (this.DeviceScore != null) + { + hashCode = (hashCode * 59) + this.DeviceScore.GetHashCode(); + } + if (this.ProvisioningMethod != null) + { + hashCode = (hashCode * 59) + this.ProvisioningMethod.GetHashCode(); + } + hashCode = (hashCode * 59) + this.RecommendationReasons.GetHashCode(); + if (this.Type != null) + { + hashCode = (hashCode * 59) + this.Type.GetHashCode(); + } + return hashCode; + } + } + /// + /// To validate all properties of the instance + /// + /// Validation context + /// Validation Result + public IEnumerable Validate(ValidationContext validationContext) + { + yield break; + } + } + +} diff --git a/Adyen/Model/LegalEntityManagement/AcceptTermsOfServiceResponse.cs b/Adyen/Model/LegalEntityManagement/AcceptTermsOfServiceResponse.cs index 43914f22e..b91ac3008 100644 --- a/Adyen/Model/LegalEntityManagement/AcceptTermsOfServiceResponse.cs +++ b/Adyen/Model/LegalEntityManagement/AcceptTermsOfServiceResponse.cs @@ -33,9 +33,9 @@ namespace Adyen.Model.LegalEntityManagement public partial class AcceptTermsOfServiceResponse : IEquatable, IValidatableObject { /// - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** /// - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** [JsonConverter(typeof(StringEnumConverter))] public enum TypeEnum { @@ -91,15 +91,21 @@ public enum TypeEnum /// Enum AdyenPccr for value: adyenPccr /// [EnumMember(Value = "adyenPccr")] - AdyenPccr = 9 + AdyenPccr = 9, + + /// + /// Enum KycOnInvite for value: kycOnInvite + /// + [EnumMember(Value = "kycOnInvite")] + KycOnInvite = 10 } /// - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** /// - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** [DataMember(Name = "type", EmitDefaultValue = false)] public TypeEnum? Type { get; set; } /// @@ -110,7 +116,7 @@ public enum TypeEnum /// The IP address of the user that accepted the Terms of Service.. /// The language used for the Terms of Service document, specified by the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) language code. Possible value: **en** for English.. /// The unique identifier of the Terms of Service document.. - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** . + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** . public AcceptTermsOfServiceResponse(string acceptedBy = default(string), string id = default(string), string ipAddress = default(string), string language = default(string), string termsOfServiceDocumentId = default(string), TypeEnum? type = default(TypeEnum?)) { this.AcceptedBy = acceptedBy; diff --git a/Adyen/Model/LegalEntityManagement/AdditionalBankIdentification.cs b/Adyen/Model/LegalEntityManagement/AdditionalBankIdentification.cs index 8b0a48558..e39455e1e 100644 --- a/Adyen/Model/LegalEntityManagement/AdditionalBankIdentification.cs +++ b/Adyen/Model/LegalEntityManagement/AdditionalBankIdentification.cs @@ -33,9 +33,9 @@ namespace Adyen.Model.LegalEntityManagement public partial class AdditionalBankIdentification : IEquatable, IValidatableObject { /// - /// The type of additional bank identification, depending on the country. Possible values: * **gbSortCode**: The 6-digit [UK sort code](https://en.wikipedia.org/wiki/Sort_code), without separators or spaces * **usRoutingNumber**: The 9-digit [routing number](https://en.wikipedia.org/wiki/ABA_routing_transit_number), without separators or spaces. + /// The type of additional bank identification, depending on the country. Possible values: * **auBsbCode**: The 6-digit [Australian Bank State Branch (BSB) code](https://en.wikipedia.org/wiki/Bank_state_branch), without separators or spaces. * **caRoutingNumber**: The 9-digit [Canadian routing number](https://en.wikipedia.org/wiki/Routing_number_(Canada)), in EFT format, without separators or spaces. * **gbSortCode**: The 6-digit [UK sort code](https://en.wikipedia.org/wiki/Sort_code), without separators or spaces * **usRoutingNumber**: The 9-digit [routing number](https://en.wikipedia.org/wiki/ABA_routing_transit_number), without separators or spaces. /// - /// The type of additional bank identification, depending on the country. Possible values: * **gbSortCode**: The 6-digit [UK sort code](https://en.wikipedia.org/wiki/Sort_code), without separators or spaces * **usRoutingNumber**: The 9-digit [routing number](https://en.wikipedia.org/wiki/ABA_routing_transit_number), without separators or spaces. + /// The type of additional bank identification, depending on the country. Possible values: * **auBsbCode**: The 6-digit [Australian Bank State Branch (BSB) code](https://en.wikipedia.org/wiki/Bank_state_branch), without separators or spaces. * **caRoutingNumber**: The 9-digit [Canadian routing number](https://en.wikipedia.org/wiki/Routing_number_(Canada)), in EFT format, without separators or spaces. * **gbSortCode**: The 6-digit [UK sort code](https://en.wikipedia.org/wiki/Sort_code), without separators or spaces * **usRoutingNumber**: The 9-digit [routing number](https://en.wikipedia.org/wiki/ABA_routing_transit_number), without separators or spaces. [JsonConverter(typeof(StringEnumConverter))] public enum TypeEnum { @@ -49,22 +49,33 @@ public enum TypeEnum /// Enum UsRoutingNumber for value: usRoutingNumber /// [EnumMember(Value = "usRoutingNumber")] - UsRoutingNumber = 2 + UsRoutingNumber = 2, + + /// + /// Enum AuBsbCode for value: auBsbCode + /// + [EnumMember(Value = "auBsbCode")] + AuBsbCode = 3, + /// + /// Enum CaRoutingNumber for value: caRoutingNumber + /// + [EnumMember(Value = "caRoutingNumber")] + CaRoutingNumber = 4 + } - /// - /// The type of additional bank identification, depending on the country. Possible values: * **gbSortCode**: The 6-digit [UK sort code](https://en.wikipedia.org/wiki/Sort_code), without separators or spaces * **usRoutingNumber**: The 9-digit [routing number](https://en.wikipedia.org/wiki/ABA_routing_transit_number), without separators or spaces. + /// The type of additional bank identification, depending on the country. Possible values: * **auBsbCode**: The 6-digit [Australian Bank State Branch (BSB) code](https://en.wikipedia.org/wiki/Bank_state_branch), without separators or spaces. * **caRoutingNumber**: The 9-digit [Canadian routing number](https://en.wikipedia.org/wiki/Routing_number_(Canada)), in EFT format, without separators or spaces. * **gbSortCode**: The 6-digit [UK sort code](https://en.wikipedia.org/wiki/Sort_code), without separators or spaces * **usRoutingNumber**: The 9-digit [routing number](https://en.wikipedia.org/wiki/ABA_routing_transit_number), without separators or spaces. /// - /// The type of additional bank identification, depending on the country. Possible values: * **gbSortCode**: The 6-digit [UK sort code](https://en.wikipedia.org/wiki/Sort_code), without separators or spaces * **usRoutingNumber**: The 9-digit [routing number](https://en.wikipedia.org/wiki/ABA_routing_transit_number), without separators or spaces. + /// The type of additional bank identification, depending on the country. Possible values: * **auBsbCode**: The 6-digit [Australian Bank State Branch (BSB) code](https://en.wikipedia.org/wiki/Bank_state_branch), without separators or spaces. * **caRoutingNumber**: The 9-digit [Canadian routing number](https://en.wikipedia.org/wiki/Routing_number_(Canada)), in EFT format, without separators or spaces. * **gbSortCode**: The 6-digit [UK sort code](https://en.wikipedia.org/wiki/Sort_code), without separators or spaces * **usRoutingNumber**: The 9-digit [routing number](https://en.wikipedia.org/wiki/ABA_routing_transit_number), without separators or spaces. [DataMember(Name = "type", EmitDefaultValue = false)] public TypeEnum? Type { get; set; } /// /// Initializes a new instance of the class. /// /// The value of the additional bank identification.. - /// The type of additional bank identification, depending on the country. Possible values: * **gbSortCode**: The 6-digit [UK sort code](https://en.wikipedia.org/wiki/Sort_code), without separators or spaces * **usRoutingNumber**: The 9-digit [routing number](https://en.wikipedia.org/wiki/ABA_routing_transit_number), without separators or spaces.. + /// The type of additional bank identification, depending on the country. Possible values: * **auBsbCode**: The 6-digit [Australian Bank State Branch (BSB) code](https://en.wikipedia.org/wiki/Bank_state_branch), without separators or spaces. * **caRoutingNumber**: The 9-digit [Canadian routing number](https://en.wikipedia.org/wiki/Routing_number_(Canada)), in EFT format, without separators or spaces. * **gbSortCode**: The 6-digit [UK sort code](https://en.wikipedia.org/wiki/Sort_code), without separators or spaces * **usRoutingNumber**: The 9-digit [routing number](https://en.wikipedia.org/wiki/ABA_routing_transit_number), without separators or spaces.. public AdditionalBankIdentification(string code = default(string), TypeEnum? type = default(TypeEnum?)) { this.Code = code; diff --git a/Adyen/Model/LegalEntityManagement/Address.cs b/Adyen/Model/LegalEntityManagement/Address.cs index 838f8fee8..33f278fbd 100644 --- a/Adyen/Model/LegalEntityManagement/Address.cs +++ b/Adyen/Model/LegalEntityManagement/Address.cs @@ -42,7 +42,7 @@ protected Address() { } /// /// The name of the city. Required if `stateOrProvince` is provided. If you specify the city, you must also send `postalCode` and `street`.. /// The two-letter [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country code. (required). - /// Postal code. Required if `stateOrProvince` and/or `city` is provided.. + /// The postal code. Required if `stateOrProvince` and/or `city` is provided. When using alphanumeric postal codes, all letters must be uppercase. For example, 1234 AB or SW1A 1AA.. /// The two-letter ISO 3166-2 state or province code. For example, **CA** in the US. If you specify the state or province, you must also send `city`, `postalCode`, and `street`.. /// The name of the street, and the house or building number. Required if `stateOrProvince` and/or `city` is provided.. /// The apartment, unit, or suite number.. @@ -71,9 +71,9 @@ protected Address() { } public string Country { get; set; } /// - /// Postal code. Required if `stateOrProvince` and/or `city` is provided. + /// The postal code. Required if `stateOrProvince` and/or `city` is provided. When using alphanumeric postal codes, all letters must be uppercase. For example, 1234 AB or SW1A 1AA. /// - /// Postal code. Required if `stateOrProvince` and/or `city` is provided. + /// The postal code. Required if `stateOrProvince` and/or `city` is provided. When using alphanumeric postal codes, all letters must be uppercase. For example, 1234 AB or SW1A 1AA. [DataMember(Name = "postalCode", EmitDefaultValue = false)] public string PostalCode { get; set; } diff --git a/Adyen/Model/LegalEntityManagement/CalculateTermsOfServiceStatusResponse.cs b/Adyen/Model/LegalEntityManagement/CalculateTermsOfServiceStatusResponse.cs index 4cffb8946..9d9a0f1c8 100644 --- a/Adyen/Model/LegalEntityManagement/CalculateTermsOfServiceStatusResponse.cs +++ b/Adyen/Model/LegalEntityManagement/CalculateTermsOfServiceStatusResponse.cs @@ -90,7 +90,13 @@ public enum TermsOfServiceTypesEnum /// Enum AdyenPccr for value: adyenPccr /// [EnumMember(Value = "adyenPccr")] - AdyenPccr = 9 + AdyenPccr = 9, + + /// + /// Enum KycOnInvite for value: kycOnInvite + /// + [EnumMember(Value = "kycOnInvite")] + KycOnInvite = 10 } diff --git a/Adyen/Model/LegalEntityManagement/FinancialReport.cs b/Adyen/Model/LegalEntityManagement/FinancialReport.cs index 96a8d68e8..d16c30112 100644 --- a/Adyen/Model/LegalEntityManagement/FinancialReport.cs +++ b/Adyen/Model/LegalEntityManagement/FinancialReport.cs @@ -221,4 +221,4 @@ public override int GetHashCode() } } -} +} \ No newline at end of file diff --git a/Adyen/Model/LegalEntityManagement/GetTermsOfServiceDocumentRequest.cs b/Adyen/Model/LegalEntityManagement/GetTermsOfServiceDocumentRequest.cs index ea43ec103..33a5d6dec 100644 --- a/Adyen/Model/LegalEntityManagement/GetTermsOfServiceDocumentRequest.cs +++ b/Adyen/Model/LegalEntityManagement/GetTermsOfServiceDocumentRequest.cs @@ -33,9 +33,9 @@ namespace Adyen.Model.LegalEntityManagement public partial class GetTermsOfServiceDocumentRequest : IEquatable, IValidatableObject { /// - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** /// - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** [JsonConverter(typeof(StringEnumConverter))] public enum TypeEnum { @@ -91,15 +91,21 @@ public enum TypeEnum /// Enum AdyenPccr for value: adyenPccr /// [EnumMember(Value = "adyenPccr")] - AdyenPccr = 9 + AdyenPccr = 9, + + /// + /// Enum KycOnInvite for value: kycOnInvite + /// + [EnumMember(Value = "kycOnInvite")] + KycOnInvite = 10 } /// - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** /// - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** [DataMember(Name = "type", IsRequired = false, EmitDefaultValue = false)] public TypeEnum Type { get; set; } /// @@ -112,7 +118,7 @@ protected GetTermsOfServiceDocumentRequest() { } /// /// The language to be used for the Terms of Service document, specified by the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) language code. Possible value: **en** for English. (required). /// The requested format for the Terms of Service document. Default value: JSON. Possible values: **JSON**, **PDF**, or **TXT**.. - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** (required). + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** (required). public GetTermsOfServiceDocumentRequest(string language = default(string), string termsOfServiceDocumentFormat = default(string), TypeEnum type = default(TypeEnum)) { this.Language = language; diff --git a/Adyen/Model/LegalEntityManagement/GetTermsOfServiceDocumentResponse.cs b/Adyen/Model/LegalEntityManagement/GetTermsOfServiceDocumentResponse.cs index 356ad6924..b155cf761 100644 --- a/Adyen/Model/LegalEntityManagement/GetTermsOfServiceDocumentResponse.cs +++ b/Adyen/Model/LegalEntityManagement/GetTermsOfServiceDocumentResponse.cs @@ -33,9 +33,9 @@ namespace Adyen.Model.LegalEntityManagement public partial class GetTermsOfServiceDocumentResponse : IEquatable, IValidatableObject { /// - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** /// - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** [JsonConverter(typeof(StringEnumConverter))] public enum TypeEnum { @@ -91,15 +91,21 @@ public enum TypeEnum /// Enum AdyenPccr for value: adyenPccr /// [EnumMember(Value = "adyenPccr")] - AdyenPccr = 9 + AdyenPccr = 9, + + /// + /// Enum KycOnInvite for value: kycOnInvite + /// + [EnumMember(Value = "kycOnInvite")] + KycOnInvite = 10 } /// - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** /// - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** [DataMember(Name = "type", EmitDefaultValue = false)] public TypeEnum? Type { get; set; } /// @@ -110,7 +116,7 @@ public enum TypeEnum /// The language used for the Terms of Service document, specified by the two-letter [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) language code. Possible value: **en** for English.. /// The format of the Terms of Service document.. /// The unique identifier of the Terms of Service document.. - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** . + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** . public GetTermsOfServiceDocumentResponse(byte[] document = default(byte[]), string id = default(string), string language = default(string), string termsOfServiceDocumentFormat = default(string), string termsOfServiceDocumentId = default(string), TypeEnum? type = default(TypeEnum?)) { this.Document = document; diff --git a/Adyen/Model/LegalEntityManagement/TermsOfServiceAcceptanceInfo.cs b/Adyen/Model/LegalEntityManagement/TermsOfServiceAcceptanceInfo.cs index 1aa00d04a..09713eefb 100644 --- a/Adyen/Model/LegalEntityManagement/TermsOfServiceAcceptanceInfo.cs +++ b/Adyen/Model/LegalEntityManagement/TermsOfServiceAcceptanceInfo.cs @@ -33,9 +33,9 @@ namespace Adyen.Model.LegalEntityManagement public partial class TermsOfServiceAcceptanceInfo : IEquatable, IValidatableObject { /// - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** /// - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** [JsonConverter(typeof(StringEnumConverter))] public enum TypeEnum { @@ -91,15 +91,21 @@ public enum TypeEnum /// Enum AdyenPccr for value: adyenPccr /// [EnumMember(Value = "adyenPccr")] - AdyenPccr = 9 + AdyenPccr = 9, + + /// + /// Enum KycOnInvite for value: kycOnInvite + /// + [EnumMember(Value = "kycOnInvite")] + KycOnInvite = 10 } /// - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** /// - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** [DataMember(Name = "type", EmitDefaultValue = false)] public TypeEnum? Type { get; set; } /// @@ -109,7 +115,7 @@ public enum TypeEnum /// The unique identifier of the legal entity for which the Terms of Service are accepted.. /// The date when the Terms of Service were accepted, in ISO 8601 extended format. For example, 2022-12-18T10:15:30+01:00.. /// An Adyen-generated reference for the accepted Terms of Service.. - /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** . + /// The type of Terms of Service. Possible values: * **adyenForPlatformsManage** * **adyenIssuing** * **adyenForPlatformsAdvanced** * **adyenCapital** * **adyenAccount** * **adyenCard** * **adyenFranchisee** * **adyenPccr** * **adyenChargeCard** * **kycOnInvite** . /// The expiration date for the Terms of Service acceptance, in ISO 8601 extended format. For example, 2022-12-18T00:00:00+01:00.. public TermsOfServiceAcceptanceInfo(string acceptedBy = default(string), string acceptedFor = default(string), DateTime createdAt = default(DateTime), string id = default(string), TypeEnum? type = default(TypeEnum?), DateTime validTo = default(DateTime)) { diff --git a/Adyen/Model/Management/SplitConfigurationRule.cs b/Adyen/Model/Management/SplitConfigurationRule.cs index 40e6e5fb2..d82d51e76 100644 --- a/Adyen/Model/Management/SplitConfigurationRule.cs +++ b/Adyen/Model/Management/SplitConfigurationRule.cs @@ -55,8 +55,25 @@ public enum FundingSourceEnum /// Enum ANY for value: ANY /// [EnumMember(Value = "ANY")] - ANY = 3 + ANY = 3, + /// + /// Enum Charged for value: charged + /// + [EnumMember(Value = "charged")] + Charged = 4, + + /// + /// Enum DeferredDebit for value: deferred_debit + /// + [EnumMember(Value = "deferred_debit")] + DeferredDebit = 5, + + /// + /// Enum Prepaid for value: prepaid + /// + [EnumMember(Value = "prepaid")] + Prepaid = 6 } diff --git a/Adyen/Model/TransactionWebhooks/BalancePlatformNotificationResponse.cs b/Adyen/Model/TransactionWebhooks/BalancePlatformNotificationResponse.cs index 9a20bae19..79ef5c400 100644 --- a/Adyen/Model/TransactionWebhooks/BalancePlatformNotificationResponse.cs +++ b/Adyen/Model/TransactionWebhooks/BalancePlatformNotificationResponse.cs @@ -35,16 +35,16 @@ public partial class BalancePlatformNotificationResponse : IEquatable /// Initializes a new instance of the class. /// - /// Respond with any **2xx** HTTP status code to [accept the webhook](https://docs.adyen.com/development-resources/webhooks#accept-notifications).. + /// Respond with any **2xx** HTTP status code to [accept the webhook](https://docs.adyen.com/development-resources/webhooks/#accept-webhooks).. public BalancePlatformNotificationResponse(string notificationResponse = default(string)) { this.NotificationResponse = notificationResponse; } /// - /// Respond with any **2xx** HTTP status code to [accept the webhook](https://docs.adyen.com/development-resources/webhooks#accept-notifications). + /// Respond with any **2xx** HTTP status code to [accept the webhook](https://docs.adyen.com/development-resources/webhooks/#accept-webhooks). /// - /// Respond with any **2xx** HTTP status code to [accept the webhook](https://docs.adyen.com/development-resources/webhooks#accept-notifications). + /// Respond with any **2xx** HTTP status code to [accept the webhook](https://docs.adyen.com/development-resources/webhooks/#accept-webhooks). [DataMember(Name = "notificationResponse", EmitDefaultValue = false)] public string NotificationResponse { get; set; } diff --git a/Adyen/Model/TransactionWebhooks/BankCategoryData.cs b/Adyen/Model/TransactionWebhooks/BankCategoryData.cs index 9eb725b4a..81b2565d2 100644 --- a/Adyen/Model/TransactionWebhooks/BankCategoryData.cs +++ b/Adyen/Model/TransactionWebhooks/BankCategoryData.cs @@ -33,9 +33,9 @@ namespace Adyen.Model.TransactionWebhooks public partial class BankCategoryData : IEquatable, IValidatableObject { /// - /// The priority for the bank transfer. This sets the speed at which the transfer is sent and the fees that you have to pay. Required for transfers with `category` **bank**. Possible values: * **regular**: for normal, low-value transactions. * **fast**: a faster way to transfer funds, but the fees are higher. Recommended for high-priority, low-value transactions. * **wire**: the fastest way to transfer funds, but this has the highest fees. Recommended for high-priority, high-value transactions. * **instant**: for instant funds transfers in [SEPA countries](https://www.ecb.europa.eu/paym/integration/retail/sepa/html/index.en.html). * **crossBorder**: for high-value transfers to a recipient in a different country. * **internal**: for transfers to an Adyen-issued business bank account (by bank account number/IBAN). + /// The priority for the bank transfer. This sets the speed at which the transfer is sent and the fees that you have to pay. Required for transfers with `category` **bank**. Possible values: * **regular**: for normal, low-value transactions. * **fast**: a faster way to transfer funds, but the fees are higher. Recommended for high-priority, low-value transactions. * **wire**: the fastest way to transfer funds, but this has the highest fees. Recommended for high-priority, high-value transactions. * **instant**: for instant funds transfers within the United States and in [SEPA locations](https://www.ecb.europa.eu/paym/integration/retail/sepa/html/index.en.html). * **crossBorder**: for high-value transfers to a recipient in a different country. * **internal**: for transfers to an Adyen-issued business bank account (by bank account number/IBAN). /// - /// The priority for the bank transfer. This sets the speed at which the transfer is sent and the fees that you have to pay. Required for transfers with `category` **bank**. Possible values: * **regular**: for normal, low-value transactions. * **fast**: a faster way to transfer funds, but the fees are higher. Recommended for high-priority, low-value transactions. * **wire**: the fastest way to transfer funds, but this has the highest fees. Recommended for high-priority, high-value transactions. * **instant**: for instant funds transfers in [SEPA countries](https://www.ecb.europa.eu/paym/integration/retail/sepa/html/index.en.html). * **crossBorder**: for high-value transfers to a recipient in a different country. * **internal**: for transfers to an Adyen-issued business bank account (by bank account number/IBAN). + /// The priority for the bank transfer. This sets the speed at which the transfer is sent and the fees that you have to pay. Required for transfers with `category` **bank**. Possible values: * **regular**: for normal, low-value transactions. * **fast**: a faster way to transfer funds, but the fees are higher. Recommended for high-priority, low-value transactions. * **wire**: the fastest way to transfer funds, but this has the highest fees. Recommended for high-priority, high-value transactions. * **instant**: for instant funds transfers within the United States and in [SEPA locations](https://www.ecb.europa.eu/paym/integration/retail/sepa/html/index.en.html). * **crossBorder**: for high-value transfers to a recipient in a different country. * **internal**: for transfers to an Adyen-issued business bank account (by bank account number/IBAN). [JsonConverter(typeof(StringEnumConverter))] public enum PriorityEnum { @@ -79,9 +79,9 @@ public enum PriorityEnum /// - /// The priority for the bank transfer. This sets the speed at which the transfer is sent and the fees that you have to pay. Required for transfers with `category` **bank**. Possible values: * **regular**: for normal, low-value transactions. * **fast**: a faster way to transfer funds, but the fees are higher. Recommended for high-priority, low-value transactions. * **wire**: the fastest way to transfer funds, but this has the highest fees. Recommended for high-priority, high-value transactions. * **instant**: for instant funds transfers in [SEPA countries](https://www.ecb.europa.eu/paym/integration/retail/sepa/html/index.en.html). * **crossBorder**: for high-value transfers to a recipient in a different country. * **internal**: for transfers to an Adyen-issued business bank account (by bank account number/IBAN). + /// The priority for the bank transfer. This sets the speed at which the transfer is sent and the fees that you have to pay. Required for transfers with `category` **bank**. Possible values: * **regular**: for normal, low-value transactions. * **fast**: a faster way to transfer funds, but the fees are higher. Recommended for high-priority, low-value transactions. * **wire**: the fastest way to transfer funds, but this has the highest fees. Recommended for high-priority, high-value transactions. * **instant**: for instant funds transfers within the United States and in [SEPA locations](https://www.ecb.europa.eu/paym/integration/retail/sepa/html/index.en.html). * **crossBorder**: for high-value transfers to a recipient in a different country. * **internal**: for transfers to an Adyen-issued business bank account (by bank account number/IBAN). /// - /// The priority for the bank transfer. This sets the speed at which the transfer is sent and the fees that you have to pay. Required for transfers with `category` **bank**. Possible values: * **regular**: for normal, low-value transactions. * **fast**: a faster way to transfer funds, but the fees are higher. Recommended for high-priority, low-value transactions. * **wire**: the fastest way to transfer funds, but this has the highest fees. Recommended for high-priority, high-value transactions. * **instant**: for instant funds transfers in [SEPA countries](https://www.ecb.europa.eu/paym/integration/retail/sepa/html/index.en.html). * **crossBorder**: for high-value transfers to a recipient in a different country. * **internal**: for transfers to an Adyen-issued business bank account (by bank account number/IBAN). + /// The priority for the bank transfer. This sets the speed at which the transfer is sent and the fees that you have to pay. Required for transfers with `category` **bank**. Possible values: * **regular**: for normal, low-value transactions. * **fast**: a faster way to transfer funds, but the fees are higher. Recommended for high-priority, low-value transactions. * **wire**: the fastest way to transfer funds, but this has the highest fees. Recommended for high-priority, high-value transactions. * **instant**: for instant funds transfers within the United States and in [SEPA locations](https://www.ecb.europa.eu/paym/integration/retail/sepa/html/index.en.html). * **crossBorder**: for high-value transfers to a recipient in a different country. * **internal**: for transfers to an Adyen-issued business bank account (by bank account number/IBAN). [DataMember(Name = "priority", EmitDefaultValue = false)] public PriorityEnum? Priority { get; set; } /// @@ -109,7 +109,7 @@ public enum TypeEnum /// /// Initializes a new instance of the class. /// - /// The priority for the bank transfer. This sets the speed at which the transfer is sent and the fees that you have to pay. Required for transfers with `category` **bank**. Possible values: * **regular**: for normal, low-value transactions. * **fast**: a faster way to transfer funds, but the fees are higher. Recommended for high-priority, low-value transactions. * **wire**: the fastest way to transfer funds, but this has the highest fees. Recommended for high-priority, high-value transactions. * **instant**: for instant funds transfers in [SEPA countries](https://www.ecb.europa.eu/paym/integration/retail/sepa/html/index.en.html). * **crossBorder**: for high-value transfers to a recipient in a different country. * **internal**: for transfers to an Adyen-issued business bank account (by bank account number/IBAN).. + /// The priority for the bank transfer. This sets the speed at which the transfer is sent and the fees that you have to pay. Required for transfers with `category` **bank**. Possible values: * **regular**: for normal, low-value transactions. * **fast**: a faster way to transfer funds, but the fees are higher. Recommended for high-priority, low-value transactions. * **wire**: the fastest way to transfer funds, but this has the highest fees. Recommended for high-priority, high-value transactions. * **instant**: for instant funds transfers within the United States and in [SEPA locations](https://www.ecb.europa.eu/paym/integration/retail/sepa/html/index.en.html). * **crossBorder**: for high-value transfers to a recipient in a different country. * **internal**: for transfers to an Adyen-issued business bank account (by bank account number/IBAN).. /// **bank** (default to TypeEnum.Bank). public BankCategoryData(PriorityEnum? priority = default(PriorityEnum?), TypeEnum? type = TypeEnum.Bank) { diff --git a/Adyen/Model/TransactionWebhooks/IssuedCard.cs b/Adyen/Model/TransactionWebhooks/IssuedCard.cs index 35b807d17..7900f7bda 100644 --- a/Adyen/Model/TransactionWebhooks/IssuedCard.cs +++ b/Adyen/Model/TransactionWebhooks/IssuedCard.cs @@ -185,9 +185,10 @@ public enum TypeEnum /// relayedAuthorisationData. /// The identifier of the original payment. This ID is provided by the scheme and can be alphanumeric or numeric, depending on the scheme. The `schemeTraceID` should refer to an original `schemeUniqueTransactionID` provided in an earlier payment (not necessarily processed by Adyen). A `schemeTraceId` is typically available for authorization adjustments or recurring payments.. /// The unique identifier created by the scheme. This ID can be alphanumeric or numeric depending on the scheme.. + /// threeDSecure. /// **issuedCard** (default to TypeEnum.IssuedCard). /// The evaluation of the validation facts. See [validation checks](https://docs.adyen.com/issuing/validation-checks) for more information.. - public IssuedCard(string authorisationType = default(string), PanEntryModeEnum? panEntryMode = default(PanEntryModeEnum?), ProcessingTypeEnum? processingType = default(ProcessingTypeEnum?), RelayedAuthorisationData relayedAuthorisationData = default(RelayedAuthorisationData), string schemeTraceId = default(string), string schemeUniqueTransactionId = default(string), TypeEnum? type = TypeEnum.IssuedCard, List validationFacts = default(List)) + public IssuedCard(string authorisationType = default(string), PanEntryModeEnum? panEntryMode = default(PanEntryModeEnum?), ProcessingTypeEnum? processingType = default(ProcessingTypeEnum?), RelayedAuthorisationData relayedAuthorisationData = default(RelayedAuthorisationData), string schemeTraceId = default(string), string schemeUniqueTransactionId = default(string), ThreeDSecure threeDSecure = default(ThreeDSecure), TypeEnum? type = TypeEnum.IssuedCard, List validationFacts = default(List)) { this.AuthorisationType = authorisationType; this.PanEntryMode = panEntryMode; @@ -195,6 +196,7 @@ public enum TypeEnum this.RelayedAuthorisationData = relayedAuthorisationData; this.SchemeTraceId = schemeTraceId; this.SchemeUniqueTransactionId = schemeUniqueTransactionId; + this.ThreeDSecure = threeDSecure; this.Type = type; this.ValidationFacts = validationFacts; } @@ -226,6 +228,12 @@ public enum TypeEnum [DataMember(Name = "schemeUniqueTransactionId", EmitDefaultValue = false)] public string SchemeUniqueTransactionId { get; set; } + /// + /// Gets or Sets ThreeDSecure + /// + [DataMember(Name = "threeDSecure", EmitDefaultValue = false)] + public ThreeDSecure ThreeDSecure { get; set; } + /// /// The evaluation of the validation facts. See [validation checks](https://docs.adyen.com/issuing/validation-checks) for more information. /// @@ -247,6 +255,7 @@ public override string ToString() sb.Append(" RelayedAuthorisationData: ").Append(RelayedAuthorisationData).Append("\n"); sb.Append(" SchemeTraceId: ").Append(SchemeTraceId).Append("\n"); sb.Append(" SchemeUniqueTransactionId: ").Append(SchemeUniqueTransactionId).Append("\n"); + sb.Append(" ThreeDSecure: ").Append(ThreeDSecure).Append("\n"); sb.Append(" Type: ").Append(Type).Append("\n"); sb.Append(" ValidationFacts: ").Append(ValidationFacts).Append("\n"); sb.Append("}\n"); @@ -312,6 +321,11 @@ public bool Equals(IssuedCard input) (this.SchemeUniqueTransactionId != null && this.SchemeUniqueTransactionId.Equals(input.SchemeUniqueTransactionId)) ) && + ( + this.ThreeDSecure == input.ThreeDSecure || + (this.ThreeDSecure != null && + this.ThreeDSecure.Equals(input.ThreeDSecure)) + ) && ( this.Type == input.Type || this.Type.Equals(input.Type) @@ -351,6 +365,10 @@ public override int GetHashCode() { hashCode = (hashCode * 59) + this.SchemeUniqueTransactionId.GetHashCode(); } + if (this.ThreeDSecure != null) + { + hashCode = (hashCode * 59) + this.ThreeDSecure.GetHashCode(); + } hashCode = (hashCode * 59) + this.Type.GetHashCode(); if (this.ValidationFacts != null) { diff --git a/Adyen/Model/TransactionWebhooks/Resource.cs b/Adyen/Model/TransactionWebhooks/Resource.cs index d90729e37..4c51d2dfa 100644 --- a/Adyen/Model/TransactionWebhooks/Resource.cs +++ b/Adyen/Model/TransactionWebhooks/Resource.cs @@ -36,7 +36,7 @@ public partial class Resource : IEquatable, IValidatableObject /// Initializes a new instance of the class. /// /// The unique identifier of the balance platform.. - /// The date and time when the event was triggered, in ISO 8601 extended format. For example, **2020-12-18T10:15:30+01:00**.. + /// The date and time when the event was triggered, in ISO 8601 extended format. For example, **2025-03-19T10:15:30+01:00**.. /// The ID of the resource.. public Resource(string balancePlatform = default(string), DateTime creationDate = default(DateTime), string id = default(string)) { @@ -53,9 +53,9 @@ public partial class Resource : IEquatable, IValidatableObject public string BalancePlatform { get; set; } /// - /// The date and time when the event was triggered, in ISO 8601 extended format. For example, **2020-12-18T10:15:30+01:00**. + /// The date and time when the event was triggered, in ISO 8601 extended format. For example, **2025-03-19T10:15:30+01:00**. /// - /// The date and time when the event was triggered, in ISO 8601 extended format. For example, **2020-12-18T10:15:30+01:00**. + /// The date and time when the event was triggered, in ISO 8601 extended format. For example, **2025-03-19T10:15:30+01:00**. [DataMember(Name = "creationDate", EmitDefaultValue = false)] public DateTime CreationDate { get; set; } diff --git a/Adyen/Model/TransactionWebhooks/ThreeDSecure.cs b/Adyen/Model/TransactionWebhooks/ThreeDSecure.cs new file mode 100644 index 000000000..71b9254bb --- /dev/null +++ b/Adyen/Model/TransactionWebhooks/ThreeDSecure.cs @@ -0,0 +1,129 @@ +/* +* Transaction webhooks +* +* +* The version of the OpenAPI document: 4 +* +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using System.ComponentModel.DataAnnotations; +using OpenAPIDateConverter = Adyen.ApiSerialization.OpenAPIDateConverter; + +namespace Adyen.Model.TransactionWebhooks +{ + /// + /// ThreeDSecure + /// + [DataContract(Name = "ThreeDSecure")] + public partial class ThreeDSecure : IEquatable, IValidatableObject + { + /// + /// Initializes a new instance of the class. + /// + /// The transaction identifier for the Access Control Server. + public ThreeDSecure(string acsTransactionId = default(string)) + { + this.AcsTransactionId = acsTransactionId; + } + + /// + /// The transaction identifier for the Access Control Server + /// + /// The transaction identifier for the Access Control Server + [DataMember(Name = "acsTransactionId", EmitDefaultValue = false)] + public string AcsTransactionId { get; set; } + + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class ThreeDSecure {\n"); + sb.Append(" AcsTransactionId: ").Append(AcsTransactionId).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + /// + /// Returns the JSON string presentation of the object + /// + /// JSON string presentation of the object + public virtual string ToJson() + { + return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); + } + + /// + /// Returns true if objects are equal + /// + /// Object to be compared + /// Boolean + public override bool Equals(object input) + { + return this.Equals(input as ThreeDSecure); + } + + /// + /// Returns true if ThreeDSecure instances are equal + /// + /// Instance of ThreeDSecure to be compared + /// Boolean + public bool Equals(ThreeDSecure input) + { + if (input == null) + { + return false; + } + return + ( + this.AcsTransactionId == input.AcsTransactionId || + (this.AcsTransactionId != null && + this.AcsTransactionId.Equals(input.AcsTransactionId)) + ); + } + + /// + /// Gets the hash code + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + int hashCode = 41; + if (this.AcsTransactionId != null) + { + hashCode = (hashCode * 59) + this.AcsTransactionId.GetHashCode(); + } + return hashCode; + } + } + /// + /// To validate all properties of the instance + /// + /// Validation context + /// Validation Result + public IEnumerable Validate(ValidationContext validationContext) + { + yield break; + } + } + +} diff --git a/Adyen/Model/TransactionWebhooks/Transaction.cs b/Adyen/Model/TransactionWebhooks/Transaction.cs index 60ad7b697..9e0614204 100644 --- a/Adyen/Model/TransactionWebhooks/Transaction.cs +++ b/Adyen/Model/TransactionWebhooks/Transaction.cs @@ -73,7 +73,7 @@ protected Transaction() { } /// balanceAccount (required). /// The unique identifier of the balance platform. (required). /// The date the transaction was booked into the balance account. (required). - /// The date and time when the event was triggered, in ISO 8601 extended format. For example, **2020-12-18T10:15:30+01:00**.. + /// The date and time when the event was triggered, in ISO 8601 extended format. For example, **2025-03-19T10:15:30+01:00**.. /// The `description` from the `/transfers` request.. /// The unique identifier of the transaction. (required). /// paymentInstrument. @@ -131,9 +131,9 @@ protected Transaction() { } public DateTime BookingDate { get; set; } /// - /// The date and time when the event was triggered, in ISO 8601 extended format. For example, **2020-12-18T10:15:30+01:00**. + /// The date and time when the event was triggered, in ISO 8601 extended format. For example, **2025-03-19T10:15:30+01:00**. /// - /// The date and time when the event was triggered, in ISO 8601 extended format. For example, **2020-12-18T10:15:30+01:00**. + /// The date and time when the event was triggered, in ISO 8601 extended format. For example, **2025-03-19T10:15:30+01:00**. [DataMember(Name = "creationDate", EmitDefaultValue = false)] public DateTime CreationDate { get; set; } diff --git a/Adyen/Model/TransferWebhooks/AdditionalBankIdentification.cs b/Adyen/Model/TransferWebhooks/AdditionalBankIdentification.cs index 08e15d14c..a87e5399e 100644 --- a/Adyen/Model/TransferWebhooks/AdditionalBankIdentification.cs +++ b/Adyen/Model/TransferWebhooks/AdditionalBankIdentification.cs @@ -49,8 +49,20 @@ public enum TypeEnum /// Enum UsRoutingNumber for value: usRoutingNumber /// [EnumMember(Value = "usRoutingNumber")] - UsRoutingNumber = 2 + UsRoutingNumber = 2, + + /// + /// Enum AuBsbCode for value: auBsbCode + /// + [EnumMember(Value = "auBsbCode")] + AuBsbCode = 3, + /// + /// Enum CaRoutingNumber for value: caRoutingNumber + /// + [EnumMember(Value = "caRoutingNumber")] + CaRoutingNumber = 4 + } diff --git a/Adyen/Model/Transfers/AdditionalBankIdentification.cs b/Adyen/Model/Transfers/AdditionalBankIdentification.cs index fe6830f37..a25b56001 100644 --- a/Adyen/Model/Transfers/AdditionalBankIdentification.cs +++ b/Adyen/Model/Transfers/AdditionalBankIdentification.cs @@ -49,7 +49,19 @@ public enum TypeEnum /// Enum UsRoutingNumber for value: usRoutingNumber /// [EnumMember(Value = "usRoutingNumber")] - UsRoutingNumber = 2 + UsRoutingNumber = 2, + + /// + /// Enum AuBsbCode for value: auBsbCode + /// + [EnumMember(Value = "auBsbCode")] + AuBsbCode = 3, + + /// + /// Enum CaRoutingNumber for value: caRoutingNumber + /// + [EnumMember(Value = "caRoutingNumber")] + CaRoutingNumber = 4 } diff --git a/Adyen/Service/ApiException.cs b/Adyen/Service/ApiException.cs deleted file mode 100644 index 5c687f468..000000000 --- a/Adyen/Service/ApiException.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using Adyen.Model; - -namespace Adyen.Service -{ - public class ApiException:Exception - { - public int StatusCode { get; set; } - - public ApiError ApiError{ get; set; } - - public ApiException(int statusCode,string message) - :base(message) - { - StatusCode = statusCode; - } - } -} diff --git a/Adyen/ApiSerialization/Converter/JsonBase64Converter.cs b/Adyen/TerminalApi/ApiSerialization/Converter/JsonBase64Converter.cs similarity index 100% rename from Adyen/ApiSerialization/Converter/JsonBase64Converter.cs rename to Adyen/TerminalApi/ApiSerialization/Converter/JsonBase64Converter.cs diff --git a/Adyen/ApiSerialization/Converter/JsonConvertDeserializerWrapper.cs b/Adyen/TerminalApi/ApiSerialization/Converter/JsonConvertDeserializerWrapper.cs similarity index 100% rename from Adyen/ApiSerialization/Converter/JsonConvertDeserializerWrapper.cs rename to Adyen/TerminalApi/ApiSerialization/Converter/JsonConvertDeserializerWrapper.cs diff --git a/Adyen/ApiSerialization/Converter/JsonConvertSerializerWrapper.cs b/Adyen/TerminalApi/ApiSerialization/Converter/JsonConvertSerializerWrapper.cs similarity index 100% rename from Adyen/ApiSerialization/Converter/JsonConvertSerializerWrapper.cs rename to Adyen/TerminalApi/ApiSerialization/Converter/JsonConvertSerializerWrapper.cs diff --git a/Adyen/ApiSerialization/Converter/SaleToPoiMessageConverter.cs b/Adyen/TerminalApi/ApiSerialization/Converter/SaleToPoiMessageConverter.cs similarity index 100% rename from Adyen/ApiSerialization/Converter/SaleToPoiMessageConverter.cs rename to Adyen/TerminalApi/ApiSerialization/Converter/SaleToPoiMessageConverter.cs diff --git a/Adyen/ApiSerialization/Converter/SaleToPoiMessageSecuredConverter.cs b/Adyen/TerminalApi/ApiSerialization/Converter/SaleToPoiMessageSecuredConverter.cs similarity index 100% rename from Adyen/ApiSerialization/Converter/SaleToPoiMessageSecuredConverter.cs rename to Adyen/TerminalApi/ApiSerialization/Converter/SaleToPoiMessageSecuredConverter.cs diff --git a/Adyen/ApiSerialization/IMessagePayload.cs b/Adyen/TerminalApi/ApiSerialization/IMessagePayload.cs similarity index 100% rename from Adyen/ApiSerialization/IMessagePayload.cs rename to Adyen/TerminalApi/ApiSerialization/IMessagePayload.cs diff --git a/Adyen/ApiSerialization/IMessagePayloadSerializer.cs b/Adyen/TerminalApi/ApiSerialization/IMessagePayloadSerializer.cs similarity index 100% rename from Adyen/ApiSerialization/IMessagePayloadSerializer.cs rename to Adyen/TerminalApi/ApiSerialization/IMessagePayloadSerializer.cs diff --git a/Adyen/ApiSerialization/MessageHeaderSerializer.cs b/Adyen/TerminalApi/ApiSerialization/MessageHeaderSerializer.cs similarity index 100% rename from Adyen/ApiSerialization/MessageHeaderSerializer.cs rename to Adyen/TerminalApi/ApiSerialization/MessageHeaderSerializer.cs diff --git a/Adyen/ApiSerialization/MessagePayloadSerializer.cs b/Adyen/TerminalApi/ApiSerialization/MessagePayloadSerializer.cs similarity index 100% rename from Adyen/ApiSerialization/MessagePayloadSerializer.cs rename to Adyen/TerminalApi/ApiSerialization/MessagePayloadSerializer.cs diff --git a/Adyen/ApiSerialization/MessagePayloadSerializerFactory.cs b/Adyen/TerminalApi/ApiSerialization/MessagePayloadSerializerFactory.cs similarity index 100% rename from Adyen/ApiSerialization/MessagePayloadSerializerFactory.cs rename to Adyen/TerminalApi/ApiSerialization/MessagePayloadSerializerFactory.cs diff --git a/Adyen/ApiSerialization/OpenAPIDateConverter.cs b/Adyen/TerminalApi/ApiSerialization/OpenAPIDateConverter.cs similarity index 100% rename from Adyen/ApiSerialization/OpenAPIDateConverter.cs rename to Adyen/TerminalApi/ApiSerialization/OpenAPIDateConverter.cs diff --git a/Adyen/ApiSerialization/SaleToPoiMessageSecuredSerializer.cs b/Adyen/TerminalApi/ApiSerialization/SaleToPoiMessageSecuredSerializer.cs similarity index 100% rename from Adyen/ApiSerialization/SaleToPoiMessageSecuredSerializer.cs rename to Adyen/TerminalApi/ApiSerialization/SaleToPoiMessageSecuredSerializer.cs diff --git a/Adyen/ApiSerialization/SaleToPoiMessageSerializer.cs b/Adyen/TerminalApi/ApiSerialization/SaleToPoiMessageSerializer.cs similarity index 100% rename from Adyen/ApiSerialization/SaleToPoiMessageSerializer.cs rename to Adyen/TerminalApi/ApiSerialization/SaleToPoiMessageSerializer.cs diff --git a/Adyen/ApiSerialization/TypeHelper.cs b/Adyen/TerminalApi/ApiSerialization/TypeHelper.cs similarity index 100% rename from Adyen/ApiSerialization/TypeHelper.cs rename to Adyen/TerminalApi/ApiSerialization/TypeHelper.cs diff --git a/Adyen/BaseUrlConfig.cs b/Adyen/TerminalApi/BaseUrlConfig.cs similarity index 100% rename from Adyen/BaseUrlConfig.cs rename to Adyen/TerminalApi/BaseUrlConfig.cs diff --git a/Adyen/Client.cs b/Adyen/TerminalApi/Client.cs similarity index 97% rename from Adyen/Client.cs rename to Adyen/TerminalApi/Client.cs index 60a202ad9..4cc9717f1 100644 --- a/Adyen/Client.cs +++ b/Adyen/TerminalApi/Client.cs @@ -1,137 +1,137 @@ -using System; -using System.Net.Http; -using Adyen.Constants; -using Adyen.HttpClient; -using Adyen.HttpClient.Interfaces; -using Environment = Adyen.Model.Environment; - -namespace Adyen -{ - public class Client - { - public Config Config { get; set; } - - public string ApplicationName { get; set; } - - public delegate void CallbackLogHandler(string message); - - public event CallbackLogHandler LogCallback; - private static System.Net.Http.HttpClient _httpClient; - - [Obsolete("Providing username and password are obsolete, please use Config instead.")] - public Client(string username, string password, Environment environment, string liveEndpointUrlPrefix = null) - { - Config = new Config - { - Username = username, - Password = password, - Environment = environment - }; - SetEnvironment(environment, liveEndpointUrlPrefix, Config.TerminalApiRegion, Config.CloudApiEndPoint); - - HttpClient = new HttpClientWrapper(Config, GetHttpClient()); - } - - [Obsolete("Providing x-api-key is obsolete, please use Config instead.")] - public Client(string xapikey, Environment environment, string liveEndpointUrlPrefix = null) - { - Config = new Config - { - XApiKey = xapikey, - Environment = environment, - LiveEndpointUrlPrefix = liveEndpointUrlPrefix - }; - SetEnvironment(environment, Config.LiveEndpointUrlPrefix, Config.TerminalApiRegion, Config.CloudApiEndPoint); - HttpClient = new HttpClientWrapper(Config, GetHttpClient()); - } - - public Client(Config config) - { - Config = config; - SetEnvironment(config.Environment, config.LiveEndpointUrlPrefix, config.TerminalApiRegion, config.CloudApiEndPoint); - HttpClient = new HttpClientWrapper(config, GetHttpClient()); - } - - public Client(Config config, System.Net.Http.HttpClient httpClient) - { - Config = config; - SetEnvironment(config.Environment, config.LiveEndpointUrlPrefix, config.TerminalApiRegion, config.CloudApiEndPoint); - HttpClient = new HttpClientWrapper(config, httpClient); - } - - public Client(Config config, IHttpClientFactory factory, string clientName = null) - { - Config = config; - SetEnvironment(config.Environment, config.LiveEndpointUrlPrefix, config.TerminalApiRegion, config.CloudApiEndPoint); - HttpClient = clientName != null ? new HttpClientWrapper(config, factory.CreateClient(clientName)) : new HttpClientWrapper(Config, factory.CreateClient()); - } - - /// - /// Configures the environment. - /// - /// Specifies whether the is Test or Live. - /// The prefix for live endpoint URLs. Required for live configurations, see: https://docs.adyen.com/development-resources/live-endpoints". - /// Specifies the geographical region for the Terminal API integration, the passed is used to automatically determine this endpoint URL, see: https://docs.adyen.com/point-of-sale/design-your-integration/terminal-api/#live-endpoints. - /// Specifies the cloud endpoint for the Terminal API integration. This URL is where your SaleToPOIMessage(s) are sent. You can override this value for (mock) testing purposes. - public void SetEnvironment(Environment environment, string liveEndpointUrlPrefix = "", Region region = Region.EU, string cloudApiEndpoint = null) - { - Config.Environment = environment; - Config.LiveEndpointUrlPrefix = liveEndpointUrlPrefix; - Config.TerminalApiRegion = region; - Config.CloudApiEndPoint = cloudApiEndpoint; - Config.CloudApiEndPoint = GetCloudApiEndpoint(); - } - - /// - /// Retrieves the current terminal-api endpoint URL for sending the SaleToPoiMessage(s). - /// - /// The full terminal-api endpoint URL for the Cloud Terminal API integration. - /// Thrown if the specified is not part of . - public string GetCloudApiEndpoint() - { - // Check if the cloud API endpoint has already been set - if (Config.CloudApiEndPoint != null) - { - return Config.CloudApiEndPoint; - } - - // For LIVE environment, handle region mapping - if (Config.Environment == Environment.Live) - { - if (!RegionMapping.TERMINAL_API_ENDPOINTS_MAPPING.TryGetValue(Config.TerminalApiRegion, out string endpointUrl)) - { - throw new ArgumentOutOfRangeException($"Currently not supported: {Config.TerminalApiRegion}"); - } - return endpointUrl; - } - - // Default to test endpoint if the environment is TEST (default). - return ClientConfig.CloudApiEndPointTest; - } - - // Get a new HttpClient and set a timeout - private System.Net.Http.HttpClient GetHttpClient() - { - if (_httpClient == null) - { - _httpClient = new System.Net.Http.HttpClient(HttpClientExtensions.ConfigureHttpMessageHandler(Config)) - { - Timeout = TimeSpan.FromMilliseconds(Config.Timeout) - }; - } - return _httpClient; - } - - public IClient HttpClient { get; set; } - - public string LibraryVersion => ClientConfig.LibVersion; - - public void LogLine(string message) - { - if (LogCallback != null) - { - LogCallback(message); - } - } - } -} +using System; +using System.Net.Http; +using Adyen.Constants; +using Adyen.HttpClient; +using Adyen.HttpClient.Interfaces; +using Environment = Adyen.Model.Environment; + +namespace Adyen +{ + public class Client + { + public Config Config { get; set; } + + public string ApplicationName { get; set; } + + public delegate void CallbackLogHandler(string message); + + public event CallbackLogHandler LogCallback; + private static System.Net.Http.HttpClient _httpClient; + + [Obsolete("Providing username and password are obsolete, please use Config instead.")] + public Client(string username, string password, Environment environment, string liveEndpointUrlPrefix = null) + { + Config = new Config + { + Username = username, + Password = password, + Environment = environment + }; + SetEnvironment(environment, liveEndpointUrlPrefix, Config.TerminalApiRegion, Config.CloudApiEndPoint); + + HttpClient = new HttpClientWrapper(Config, GetHttpClient()); + } + + [Obsolete("Providing x-api-key is obsolete, please use Config instead.")] + public Client(string xapikey, Environment environment, string liveEndpointUrlPrefix = null) + { + Config = new Config + { + XApiKey = xapikey, + Environment = environment, + LiveEndpointUrlPrefix = liveEndpointUrlPrefix + }; + SetEnvironment(environment, Config.LiveEndpointUrlPrefix, Config.TerminalApiRegion, Config.CloudApiEndPoint); + HttpClient = new HttpClientWrapper(Config, GetHttpClient()); + } + + public Client(Config config) + { + Config = config; + SetEnvironment(config.Environment, config.LiveEndpointUrlPrefix, config.TerminalApiRegion, config.CloudApiEndPoint); + HttpClient = new HttpClientWrapper(config, GetHttpClient()); + } + + public Client(Config config, System.Net.Http.HttpClient httpClient) + { + Config = config; + SetEnvironment(config.Environment, config.LiveEndpointUrlPrefix, config.TerminalApiRegion, config.CloudApiEndPoint); + HttpClient = new HttpClientWrapper(config, httpClient); + } + + public Client(Config config, IHttpClientFactory factory, string clientName = null) + { + Config = config; + SetEnvironment(config.Environment, config.LiveEndpointUrlPrefix, config.TerminalApiRegion, config.CloudApiEndPoint); + HttpClient = clientName != null ? new HttpClientWrapper(config, factory.CreateClient(clientName)) : new HttpClientWrapper(Config, factory.CreateClient()); + } + + /// + /// Configures the environment. + /// + /// Specifies whether the is Test or Live. + /// The prefix for live endpoint URLs. Required for live configurations, see: https://docs.adyen.com/development-resources/live-endpoints". + /// Specifies the geographical region for the Terminal API integration, the passed is used to automatically determine this endpoint URL, see: https://docs.adyen.com/point-of-sale/design-your-integration/terminal-api/#live-endpoints. + /// Specifies the cloud endpoint for the Terminal API integration. This URL is where your SaleToPOIMessage(s) are sent. You can override this value for (mock) testing purposes. + public void SetEnvironment(Environment environment, string liveEndpointUrlPrefix = "", Region region = Region.EU, string cloudApiEndpoint = null) + { + Config.Environment = environment; + Config.LiveEndpointUrlPrefix = liveEndpointUrlPrefix; + Config.TerminalApiRegion = region; + Config.CloudApiEndPoint = cloudApiEndpoint; + Config.CloudApiEndPoint = GetCloudApiEndpoint(); + } + + /// + /// Retrieves the current terminal-api endpoint URL for sending the SaleToPoiMessage(s). + /// + /// The full terminal-api endpoint URL for the Cloud Terminal API integration. + /// Thrown if the specified is not part of . + public string GetCloudApiEndpoint() + { + // Check if the cloud API endpoint has already been set + if (Config.CloudApiEndPoint != null) + { + return Config.CloudApiEndPoint; + } + + // For LIVE environment, handle region mapping + if (Config.Environment == Environment.Live) + { + if (!RegionMapping.TERMINAL_API_ENDPOINTS_MAPPING.TryGetValue(Config.TerminalApiRegion, out string endpointUrl)) + { + throw new ArgumentOutOfRangeException($"Currently not supported: {Config.TerminalApiRegion}"); + } + return endpointUrl; + } + + // Default to test endpoint if the environment is TEST (default). + return ClientConfig.CloudApiEndPointTest; + } + + // Get a new HttpClient and set a timeout + private System.Net.Http.HttpClient GetHttpClient() + { + if (_httpClient == null) + { + _httpClient = new System.Net.Http.HttpClient(HttpClientExtensions.ConfigureHttpMessageHandler(Config)) + { + Timeout = TimeSpan.FromMilliseconds(Config.Timeout) + }; + } + return _httpClient; + } + + public IClient HttpClient { get; set; } + + public string LibraryVersion => ClientConfig.LibVersion; + + public void LogLine(string message) + { + if (LogCallback != null) + { + LogCallback(message); + } + } + } +} diff --git a/Adyen/Service/Resource/Terminal/TerminalApi.cs b/Adyen/TerminalApi/Clients/TerminalApi.cs similarity index 100% rename from Adyen/Service/Resource/Terminal/TerminalApi.cs rename to Adyen/TerminalApi/Clients/TerminalApi.cs diff --git a/Adyen/Service/Resource/Terminal/TerminalApiAsyncClient.cs b/Adyen/TerminalApi/Clients/TerminalApiAsyncClient.cs similarity index 100% rename from Adyen/Service/Resource/Terminal/TerminalApiAsyncClient.cs rename to Adyen/TerminalApi/Clients/TerminalApiAsyncClient.cs diff --git a/Adyen/Service/Resource/Terminal/TerminalApiLocal.cs b/Adyen/TerminalApi/Clients/TerminalApiLocal.cs similarity index 100% rename from Adyen/Service/Resource/Terminal/TerminalApiLocal.cs rename to Adyen/TerminalApi/Clients/TerminalApiLocal.cs diff --git a/Adyen/Service/Resource/Terminal/TerminalApiLocalClient.cs b/Adyen/TerminalApi/Clients/TerminalApiLocalClient.cs similarity index 100% rename from Adyen/Service/Resource/Terminal/TerminalApiLocalClient.cs rename to Adyen/TerminalApi/Clients/TerminalApiLocalClient.cs diff --git a/Adyen/Service/Resource/Terminal/TerminalApiSyncClient.cs b/Adyen/TerminalApi/Clients/TerminalApiSyncClient.cs similarity index 100% rename from Adyen/Service/Resource/Terminal/TerminalApiSyncClient.cs rename to Adyen/TerminalApi/Clients/TerminalApiSyncClient.cs diff --git a/Adyen/Config.cs b/Adyen/TerminalApi/Config.cs similarity index 94% rename from Adyen/Config.cs rename to Adyen/TerminalApi/Config.cs index 0f6ce4cf1..7daae70d9 100644 --- a/Adyen/Config.cs +++ b/Adyen/TerminalApi/Config.cs @@ -1,80 +1,81 @@ -using System; -using System.Net; -using Adyen.Constants; -using Environment = Adyen.Model.Environment; - -namespace Adyen -{ - public class Config - { - public string Username { get; set; } - public string Password { get; set; } - public bool HasPassword => !string.IsNullOrEmpty(Password); - - public Environment Environment { get; set; } - public string LiveEndpointUrlPrefix { get; set; } - public string ApplicationName { get; set; } - public IWebProxy Proxy { get; set; } - - /// - /// Your Adyen API key. - /// - public string XApiKey { get; set; } - public bool HasApiKey => !string.IsNullOrEmpty(XApiKey); - - /// - /// HttpConnection Timeout in milliseconds (e.g. the time required to send the request and receive the response). - /// In > NET6.0, we recommend configuring your own and pass it in the constructor. - /// The values shown here are defaults, if no is provided. - /// - public int Timeout { get; set; } = 60000; - - /// - /// Only The amount of time it should take to establish the TCP Connection. - /// This value is only used in when no default was passed in the constructor. - /// In > NET6.0, we recommend configuring your own and pass it in the constructor. - /// The values shown here are defaults, if no is provided. - /// - public TimeSpan ConnectTimeout { get; set; } = TimeSpan.FromSeconds(15); - - /// - /// Force reconnection to refresh DNS and avoid stale connections. - /// Once a connection exceeds the specified lifetime, it is no longer considered reusable and it is closed and removed from the pool. - /// This value is only used in when no default was passed in the constructor. - /// In > NET6.0, we recommend configuring your own and pass it in the constructor. - /// The values shown here are defaults, if no is provided. - /// - public TimeSpan PooledConnectionLifetime { get; set; } = TimeSpan.FromMinutes(5); - - /// - /// Close idle connections after the specified time. - /// This value is only used in when no default was passed in the constructor. - /// In > NET6.0, we recommend configuring your own and pass it in the constructor. - /// The values shown here are defaults, if no is provided. - /// - public TimeSpan PooledConnectionIdleTimeout { get; set; }= TimeSpan.FromMinutes(2); - - /// - /// Maximum number of concurrent TCP connections per server. - /// This value is only used in when no default was passed in the constructor. - /// In > NET6.0, we recommend configuring your own and pass it in the constructor. - /// The values shown here are defaults, if no is provided. - /// - public int MaxConnectionsPerServer { get; set; } = 2; - - /// - /// The url of the Cloud Terminal Api endpoint. - /// This value is populated when specifying the in . - /// - public string CloudApiEndPoint { get; set; } - - /// - /// The url of the Terminal Api endpoint, can be overriden if you want to send local terminal-api requests. - /// - public string LocalTerminalApiEndpoint { get; set; } - - public BaseUrlConfig BaseUrlConfig { get; set; } - - public Region TerminalApiRegion { get; set; } - } +using System; +using System.Net; +using Adyen.Constants; +using Environment = Adyen.Model.Environment; + +namespace Adyen +{ + public class Config + { + public string Username { get; set; } + public string Password { get; set; } + public bool HasPassword => !string.IsNullOrEmpty(Password); + + public Environment Environment { get; set; } + public string LiveEndpointUrlPrefix { get; set; } + public string ApplicationName { get; set; } + public IWebProxy Proxy { get; set; } + + /// + /// Your Adyen API key. + /// + public string XApiKey { get; set; } + + public bool HasApiKey => !string.IsNullOrEmpty(XApiKey); + + /// + /// HttpConnection Timeout in milliseconds (e.g. the time required to send the request and receive the response). + /// In > NET6.0, we recommend configuring your own and pass it in the constructor. + /// The values shown here are defaults, if no is provided. + /// + public int Timeout { get; set; } = 60000; + + /// + /// Only The amount of time it should take to establish the TCP Connection. + /// This value is only used in when no default was passed in the constructor. + /// In > NET6.0, we recommend configuring your own and pass it in the constructor. + /// The values shown here are defaults, if no is provided. + /// + public TimeSpan ConnectTimeout { get; set; } = TimeSpan.FromSeconds(15); + + /// + /// Force reconnection to refresh DNS and avoid stale connections. + /// Once a connection exceeds the specified lifetime, it is no longer considered reusable and it is closed and removed from the pool. + /// This value is only used in when no default was passed in the constructor. + /// In > NET6.0, we recommend configuring your own and pass it in the constructor. + /// The values shown here are defaults, if no is provided. + /// + public TimeSpan PooledConnectionLifetime { get; set; } = TimeSpan.FromMinutes(5); + + /// + /// Close idle connections after the specified time. + /// This value is only used in when no default was passed in the constructor. + /// In > NET6.0, we recommend configuring your own and pass it in the constructor. + /// The values shown here are defaults, if no is provided. + /// + public TimeSpan PooledConnectionIdleTimeout { get; set; } = TimeSpan.FromMinutes(2); + + /// + /// Maximum number of concurrent TCP connections per server. + /// This value is only used in when no default was passed in the constructor. + /// In > NET6.0, we recommend configuring your own and pass it in the constructor. + /// The values shown here are defaults, if no is provided. + /// + public int MaxConnectionsPerServer { get; set; } = 2; + + /// + /// The url of the Cloud Terminal Api endpoint. + /// This value is populated when specifying the in . + /// + public string CloudApiEndPoint { get; set; } + + /// + /// The url of the Terminal Api endpoint, can be overriden if you want to send local terminal-api requests. + /// + public string LocalTerminalApiEndpoint { get; set; } + + public BaseUrlConfig BaseUrlConfig { get; set; } + + public Region TerminalApiRegion { get; set; } + } } \ No newline at end of file diff --git a/Adyen/Constants/ApiConstants.cs b/Adyen/TerminalApi/Constants/ApiConstants.cs similarity index 100% rename from Adyen/Constants/ApiConstants.cs rename to Adyen/TerminalApi/Constants/ApiConstants.cs diff --git a/Adyen/TerminalApi/Constants/ClientConfig.cs b/Adyen/TerminalApi/Constants/ClientConfig.cs new file mode 100644 index 000000000..204125fef --- /dev/null +++ b/Adyen/TerminalApi/Constants/ClientConfig.cs @@ -0,0 +1,34 @@ +namespace Adyen.Constants +{ + public class ClientConfig + { + //Test cloud api endpoints + public const string CloudApiEndPointTest = "https://terminal-api-test.adyen.com"; + + //Live cloud api endpoints + public const string CloudApiEndPointEULive = "https://terminal-api-live.adyen.com"; + public const string CloudApiEndPointAULive = "https://terminal-api-live-au.adyen.com"; + public const string CloudApiEndPointUSLive = "https://terminal-api-live-us.adyen.com"; + public const string CloudApiEndPointAPSELive = "https://terminal-api-live-apse.adyen.com"; + + public const string NexoProtocolVersion = "3.0"; + + /// + /// Moved to . + /// + [Obsolete("Use Core.Client.Extensions.HttpRequestMessageExtensions.AdyenLibraryVersion/ instead.")] + public const string UserAgentSuffix = Core.Client.Extensions.HttpRequestMessageExtensions.AdyenLibraryName + "/"; + + /// + /// Moved to . + /// + [Obsolete("Use Core.Client.Extensions.HttpRequestMessageExtensions.AdyenLibraryVersion instead.")] + public const string LibName = Core.Client.Extensions.HttpRequestMessageExtensions.AdyenLibraryName; + + /// + /// Moved to . + /// + [Obsolete("Use Core.Client.Extensions.HttpRequestMessageExtensions.AdyenLibraryVersion instead.")] + public const string LibVersion = Core.Client.Extensions.HttpRequestMessageExtensions.AdyenLibraryVersion; + } +} diff --git a/Adyen/Constants/Region.cs b/Adyen/TerminalApi/Constants/Region.cs similarity index 100% rename from Adyen/Constants/Region.cs rename to Adyen/TerminalApi/Constants/Region.cs diff --git a/Adyen/Model/Environment.cs b/Adyen/TerminalApi/Environment.cs similarity index 100% rename from Adyen/Model/Environment.cs rename to Adyen/TerminalApi/Environment.cs diff --git a/Adyen/Exceptions/DeserializationException.cs b/Adyen/TerminalApi/Exceptions/DeserializationException.cs similarity index 100% rename from Adyen/Exceptions/DeserializationException.cs rename to Adyen/TerminalApi/Exceptions/DeserializationException.cs diff --git a/Adyen/Exceptions/ExceptionMessages.cs b/Adyen/TerminalApi/Exceptions/ExceptionMessages.cs similarity index 100% rename from Adyen/Exceptions/ExceptionMessages.cs rename to Adyen/TerminalApi/Exceptions/ExceptionMessages.cs diff --git a/Adyen/HttpClient/HttpClientException.cs b/Adyen/TerminalApi/HttpClient/HttpClientException.cs similarity index 100% rename from Adyen/HttpClient/HttpClientException.cs rename to Adyen/TerminalApi/HttpClient/HttpClientException.cs diff --git a/Adyen/HttpClient/HttpClientExtension.cs b/Adyen/TerminalApi/HttpClient/HttpClientExtension.cs similarity index 100% rename from Adyen/HttpClient/HttpClientExtension.cs rename to Adyen/TerminalApi/HttpClient/HttpClientExtension.cs diff --git a/Adyen/HttpClient/HttpClientWrapper.cs b/Adyen/TerminalApi/HttpClient/HttpClientWrapper.cs similarity index 89% rename from Adyen/HttpClient/HttpClientWrapper.cs rename to Adyen/TerminalApi/HttpClient/HttpClientWrapper.cs index c393a1169..c7b16621e 100644 --- a/Adyen/HttpClient/HttpClientWrapper.cs +++ b/Adyen/TerminalApi/HttpClient/HttpClientWrapper.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using System.Web; using Adyen.Constants; +using Adyen.Core.Client.Extensions; using Adyen.HttpClient.Interfaces; using Adyen.Model; @@ -68,11 +69,11 @@ public HttpRequestMessage GetHttpRequestMessage(string endpoint, string requestB if (!string.IsNullOrWhiteSpace(_config.ApplicationName)) { - httpRequestMessage.Headers.Add("UserAgent", $"{_config.ApplicationName} {ClientConfig.UserAgentSuffix}{ClientConfig.LibVersion}"); + httpRequestMessage.Headers.Add("UserAgent", $"{_config.ApplicationName} {HttpRequestMessageExtensions.AdyenLibraryName}/{HttpRequestMessageExtensions.AdyenLibraryVersion}"); } else { - httpRequestMessage.Headers.Add("UserAgent", $"{ClientConfig.UserAgentSuffix}{ClientConfig.LibVersion}"); + httpRequestMessage.Headers.Add("UserAgent", $"{HttpRequestMessageExtensions.AdyenLibraryName}/{HttpRequestMessageExtensions.AdyenLibraryVersion}"); } if (!string.IsNullOrWhiteSpace(requestOptions?.IdempotencyKey)) @@ -94,8 +95,8 @@ public HttpRequestMessage GetHttpRequestMessage(string endpoint, string requestB } // Add library name and version to request for analysis - httpRequestMessage.Headers.Add(ApiConstants.AdyenLibraryName, ClientConfig.LibName); - httpRequestMessage.Headers.Add(ApiConstants.AdyenLibraryVersion, ClientConfig.LibVersion); + httpRequestMessage.Headers.Add("adyen-library-name", HttpRequestMessageExtensions.AdyenLibraryName); + httpRequestMessage.Headers.Add("adyen-library-version", HttpRequestMessageExtensions.AdyenLibraryVersion); return httpRequestMessage; } diff --git a/Adyen/HttpClient/Interfaces/IClient.cs b/Adyen/TerminalApi/HttpClient/Interfaces/IClient.cs similarity index 100% rename from Adyen/HttpClient/Interfaces/IClient.cs rename to Adyen/TerminalApi/HttpClient/Interfaces/IClient.cs diff --git a/Adyen/Model/TerminalApi/AbortRequest.cs b/Adyen/TerminalApi/Models/AbortRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/AbortRequest.cs rename to Adyen/TerminalApi/Models/AbortRequest.cs diff --git a/Adyen/Service/AbstractService.cs b/Adyen/TerminalApi/Models/AbstractService.cs similarity index 97% rename from Adyen/Service/AbstractService.cs rename to Adyen/TerminalApi/Models/AbstractService.cs index 517317c16..b749dd706 100644 --- a/Adyen/Service/AbstractService.cs +++ b/Adyen/TerminalApi/Models/AbstractService.cs @@ -1,132 +1,132 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using Adyen.Exceptions; -using Environment = Adyen.Model.Environment; - -namespace Adyen.Service -{ - public class AbstractService - { - public Client Client { get; set; } - - private const string PaymentPrefix = "pal-"; - private const string CheckoutPrefix = "checkout-"; - - protected AbstractService(Client client) - { - Client = client; - } - - /// - /// Build the query string - /// - /// Key, value pairs - /// URL encoded query string - private protected static string ToQueryString(IDictionary queryParams) - { - if (queryParams == null || queryParams.Count == 0) - { - return string.Empty; - } - - var queryString = string.Join("&", - queryParams.Select(kvp => $"{kvp.Key}={HttpUtility.UrlEncode(kvp.Value)}")); - - if (!string.IsNullOrEmpty(queryString)) - { - return "?" + queryString; - } - - return string.Empty; - } - - /// - /// The base URL creation for the environment - /// - /// String - /// baseURL - private protected string CreateBaseUrl(string url) - { - var config = Client.Config; - return config.Environment == Environment.Live - ? ConstructLiveUrl(config, - url) - : ConstructTestUrl(config, - url); - } - - /// - /// Allow users to override the baseUrl in a test environment - /// - /// Config - /// String - /// baseUrl - private static string ConstructTestUrl(Config config, string url) - { - if (config.BaseUrlConfig == null) return url.Replace("-live", "-test"); - - var baseUrl = config.BaseUrlConfig.BaseUrl; - if (url.Contains(PaymentPrefix) - && !string.IsNullOrEmpty(config.BaseUrlConfig.PaymentUrl)) - { - baseUrl = config.BaseUrlConfig.PaymentUrl; - } else if (url.Contains(CheckoutPrefix) - && !string.IsNullOrEmpty(config.BaseUrlConfig.CheckoutUrl)) - { - baseUrl = config.BaseUrlConfig.CheckoutUrl; - } - - var urlPath = new Uri(url).AbsolutePath; - var returnUrl = new Uri(baseUrl + urlPath).ToString(); - - return returnUrl; - } - - /// - /// Construct live baseUrl - /// - /// Config - /// String - /// baseUrl - /// - private static string ConstructLiveUrl(Config config, string url) - { - // Change base url for Live environment - if (url.Contains(PaymentPrefix)) - { - if (config.LiveEndpointUrlPrefix == default) - { - throw new InvalidOperationException(ExceptionMessages.MissingLiveEndpointUrlPrefix); - } - - url = url.Replace("https://pal-test.adyen.com/pal/servlet/", - "https://" + config.LiveEndpointUrlPrefix + "-pal-live.adyenpayments.com/pal/servlet/"); - } - else if (url.Contains(CheckoutPrefix)) - { - if (config.LiveEndpointUrlPrefix == default) - { - throw new InvalidOperationException(ExceptionMessages.MissingLiveEndpointUrlPrefix); - } - - if (url.Contains("possdk")) - { - url = url.Replace("https://checkout-test.adyen.com/", - "https://" + config.LiveEndpointUrlPrefix + "-checkout-live.adyenpayments.com/"); - } - else - { - url = url.Replace("https://checkout-test.adyen.com/", - "https://" + config.LiveEndpointUrlPrefix + "-checkout-live.adyenpayments.com/checkout/"); - } - } - - - // If no prefix is required just replace "test" -> "live" - url = url.Replace("-test", "-live"); - return url; - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using Adyen.Exceptions; +using Environment = Adyen.Model.Environment; + +namespace Adyen.Service +{ + public class AbstractService + { + public Client Client { get; set; } + + private const string PaymentPrefix = "pal-"; + private const string CheckoutPrefix = "checkout-"; + + protected AbstractService(Client client) + { + Client = client; + } + + /// + /// Build the query string + /// + /// Key, value pairs + /// URL encoded query string + private protected static string ToQueryString(IDictionary queryParams) + { + if (queryParams == null || queryParams.Count == 0) + { + return string.Empty; + } + + var queryString = string.Join("&", + queryParams.Select(kvp => $"{kvp.Key}={HttpUtility.UrlEncode(kvp.Value)}")); + + if (!string.IsNullOrEmpty(queryString)) + { + return "?" + queryString; + } + + return string.Empty; + } + + /// + /// The base URL creation for the environment + /// + /// String + /// baseURL + private protected string CreateBaseUrl(string url) + { + var config = Client.Config; + return config.Environment == Environment.Live + ? ConstructLiveUrl(config, + url) + : ConstructTestUrl(config, + url); + } + + /// + /// Allow users to override the baseUrl in a test environment + /// + /// Config + /// String + /// baseUrl + private static string ConstructTestUrl(Config config, string url) + { + if (config.BaseUrlConfig == null) return url.Replace("-live", "-test"); + + var baseUrl = config.BaseUrlConfig.BaseUrl; + if (url.Contains(PaymentPrefix) + && !string.IsNullOrEmpty(config.BaseUrlConfig.PaymentUrl)) + { + baseUrl = config.BaseUrlConfig.PaymentUrl; + } else if (url.Contains(CheckoutPrefix) + && !string.IsNullOrEmpty(config.BaseUrlConfig.CheckoutUrl)) + { + baseUrl = config.BaseUrlConfig.CheckoutUrl; + } + + var urlPath = new Uri(url).AbsolutePath; + var returnUrl = new Uri(baseUrl + urlPath).ToString(); + + return returnUrl; + } + + /// + /// Construct live baseUrl + /// + /// Config + /// String + /// baseUrl + /// + private static string ConstructLiveUrl(Config config, string url) + { + // Change base url for Live environment + if (url.Contains(PaymentPrefix)) + { + if (config.LiveEndpointUrlPrefix == default) + { + throw new InvalidOperationException(ExceptionMessages.MissingLiveEndpointUrlPrefix); + } + + url = url.Replace("https://pal-test.adyen.com/pal/servlet/", + "https://" + config.LiveEndpointUrlPrefix + "-pal-live.adyenpayments.com/pal/servlet/"); + } + else if (url.Contains(CheckoutPrefix)) + { + if (config.LiveEndpointUrlPrefix == default) + { + throw new InvalidOperationException(ExceptionMessages.MissingLiveEndpointUrlPrefix); + } + + if (url.Contains("possdk")) + { + url = url.Replace("https://checkout-test.adyen.com/", + "https://" + config.LiveEndpointUrlPrefix + "-checkout-live.adyenpayments.com/"); + } + else + { + url = url.Replace("https://checkout-test.adyen.com/", + "https://" + config.LiveEndpointUrlPrefix + "-checkout-live.adyenpayments.com/checkout/"); + } + } + + + // If no prefix is required just replace "test" -> "live" + url = url.Replace("-test", "-live"); + return url; + } + } } \ No newline at end of file diff --git a/Adyen/Model/TerminalApi/AccountType.cs b/Adyen/TerminalApi/Models/AccountType.cs similarity index 100% rename from Adyen/Model/TerminalApi/AccountType.cs rename to Adyen/TerminalApi/Models/AccountType.cs diff --git a/Adyen/Model/TerminalApi/AdminRequest.cs b/Adyen/TerminalApi/Models/AdminRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/AdminRequest.cs rename to Adyen/TerminalApi/Models/AdminRequest.cs diff --git a/Adyen/Model/TerminalApi/AdminResponse.cs b/Adyen/TerminalApi/Models/AdminResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/AdminResponse.cs rename to Adyen/TerminalApi/Models/AdminResponse.cs diff --git a/Adyen/Model/TerminalApi/AlgorithmIdentifier.cs b/Adyen/TerminalApi/Models/AlgorithmIdentifier.cs similarity index 100% rename from Adyen/Model/TerminalApi/AlgorithmIdentifier.cs rename to Adyen/TerminalApi/Models/AlgorithmIdentifier.cs diff --git a/Adyen/Model/TerminalApi/AlgorithmType.cs b/Adyen/TerminalApi/Models/AlgorithmType.cs similarity index 100% rename from Adyen/Model/TerminalApi/AlgorithmType.cs rename to Adyen/TerminalApi/Models/AlgorithmType.cs diff --git a/Adyen/Model/TerminalApi/AlignmentType.cs b/Adyen/TerminalApi/Models/AlignmentType.cs similarity index 100% rename from Adyen/Model/TerminalApi/AlignmentType.cs rename to Adyen/TerminalApi/Models/AlignmentType.cs diff --git a/Adyen/Model/TerminalApi/AllowedProduct.cs b/Adyen/TerminalApi/Models/AllowedProduct.cs similarity index 100% rename from Adyen/Model/TerminalApi/AllowedProduct.cs rename to Adyen/TerminalApi/Models/AllowedProduct.cs diff --git a/Adyen/Model/TerminalApi/Amount.cs b/Adyen/TerminalApi/Models/Amount.cs similarity index 100% rename from Adyen/Model/TerminalApi/Amount.cs rename to Adyen/TerminalApi/Models/Amount.cs diff --git a/Adyen/Model/TerminalApi/AmountsReq.cs b/Adyen/TerminalApi/Models/AmountsReq.cs similarity index 100% rename from Adyen/Model/TerminalApi/AmountsReq.cs rename to Adyen/TerminalApi/Models/AmountsReq.cs diff --git a/Adyen/Model/TerminalApi/AmountsResp.cs b/Adyen/TerminalApi/Models/AmountsResp.cs similarity index 100% rename from Adyen/Model/TerminalApi/AmountsResp.cs rename to Adyen/TerminalApi/Models/AmountsResp.cs diff --git a/Adyen/Model/ApplicationInformation/ApplicationInfo.cs b/Adyen/TerminalApi/Models/ApplicationInformation/ApplicationInfo.cs similarity index 97% rename from Adyen/Model/ApplicationInformation/ApplicationInfo.cs rename to Adyen/TerminalApi/Models/ApplicationInformation/ApplicationInfo.cs index 0427270a9..c43d151ab 100644 --- a/Adyen/Model/ApplicationInformation/ApplicationInfo.cs +++ b/Adyen/TerminalApi/Models/ApplicationInformation/ApplicationInfo.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; @@ -21,8 +21,8 @@ public ApplicationInfo() { AdyenLibrary = new CommonField { - Name = ClientConfig.LibName, - Version = ClientConfig.LibVersion + Name = Adyen.Core.Client.Extensions.HttpRequestMessageExtensions.AdyenLibraryName, + Version = Core.Client.Extensions.HttpRequestMessageExtensions.AdyenLibraryName }; } @@ -178,4 +178,4 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali } } -} +} \ No newline at end of file diff --git a/Adyen/Model/ApplicationInformation/CommonField.cs b/Adyen/TerminalApi/Models/ApplicationInformation/CommonField.cs similarity index 99% rename from Adyen/Model/ApplicationInformation/CommonField.cs rename to Adyen/TerminalApi/Models/ApplicationInformation/CommonField.cs index e1884260c..1ff4bc3f1 100644 --- a/Adyen/Model/ApplicationInformation/CommonField.cs +++ b/Adyen/TerminalApi/Models/ApplicationInformation/CommonField.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; @@ -121,4 +121,4 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali yield break; } } -} +} \ No newline at end of file diff --git a/Adyen/Model/ApplicationInformation/ExternalPlatform.cs b/Adyen/TerminalApi/Models/ApplicationInformation/ExternalPlatform.cs similarity index 99% rename from Adyen/Model/ApplicationInformation/ExternalPlatform.cs rename to Adyen/TerminalApi/Models/ApplicationInformation/ExternalPlatform.cs index dfd53c0fd..b97ac85ab 100644 --- a/Adyen/Model/ApplicationInformation/ExternalPlatform.cs +++ b/Adyen/TerminalApi/Models/ApplicationInformation/ExternalPlatform.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; @@ -139,4 +139,4 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali } } -} +} \ No newline at end of file diff --git a/Adyen/Model/ApplicationInformation/MerchantDevice.cs b/Adyen/TerminalApi/Models/ApplicationInformation/MerchantDevice.cs similarity index 99% rename from Adyen/Model/ApplicationInformation/MerchantDevice.cs rename to Adyen/TerminalApi/Models/ApplicationInformation/MerchantDevice.cs index 799a29e69..bbcd6ebd7 100644 --- a/Adyen/Model/ApplicationInformation/MerchantDevice.cs +++ b/Adyen/TerminalApi/Models/ApplicationInformation/MerchantDevice.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; @@ -139,4 +139,4 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali } } -} +} \ No newline at end of file diff --git a/Adyen/Model/ApplicationInformation/ShopperInteractionDevice.cs b/Adyen/TerminalApi/Models/ApplicationInformation/ShopperInteractionDevice.cs similarity index 99% rename from Adyen/Model/ApplicationInformation/ShopperInteractionDevice.cs rename to Adyen/TerminalApi/Models/ApplicationInformation/ShopperInteractionDevice.cs index 57b152246..40a6ea25d 100644 --- a/Adyen/Model/ApplicationInformation/ShopperInteractionDevice.cs +++ b/Adyen/TerminalApi/Models/ApplicationInformation/ShopperInteractionDevice.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; @@ -138,4 +138,4 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali yield break; } } -} +} \ No newline at end of file diff --git a/Adyen/Model/TerminalApi/AreaSize.cs b/Adyen/TerminalApi/Models/AreaSize.cs similarity index 100% rename from Adyen/Model/TerminalApi/AreaSize.cs rename to Adyen/TerminalApi/Models/AreaSize.cs diff --git a/Adyen/Model/TerminalApi/AuthenticatedData.cs b/Adyen/TerminalApi/Models/AuthenticatedData.cs similarity index 100% rename from Adyen/Model/TerminalApi/AuthenticatedData.cs rename to Adyen/TerminalApi/Models/AuthenticatedData.cs diff --git a/Adyen/Model/TerminalApi/AuthenticationMethodType.cs b/Adyen/TerminalApi/Models/AuthenticationMethodType.cs similarity index 100% rename from Adyen/Model/TerminalApi/AuthenticationMethodType.cs rename to Adyen/TerminalApi/Models/AuthenticationMethodType.cs diff --git a/Adyen/Model/TerminalApi/BalanceInquiryRequest.cs b/Adyen/TerminalApi/Models/BalanceInquiryRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/BalanceInquiryRequest.cs rename to Adyen/TerminalApi/Models/BalanceInquiryRequest.cs diff --git a/Adyen/Model/TerminalApi/BalanceInquiryResponse.cs b/Adyen/TerminalApi/Models/BalanceInquiryResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/BalanceInquiryResponse.cs rename to Adyen/TerminalApi/Models/BalanceInquiryResponse.cs diff --git a/Adyen/Model/TerminalApi/BarcodeType.cs b/Adyen/TerminalApi/Models/BarcodeType.cs similarity index 100% rename from Adyen/Model/TerminalApi/BarcodeType.cs rename to Adyen/TerminalApi/Models/BarcodeType.cs diff --git a/Adyen/Model/TerminalApi/BatchRequest.cs b/Adyen/TerminalApi/Models/BatchRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/BatchRequest.cs rename to Adyen/TerminalApi/Models/BatchRequest.cs diff --git a/Adyen/Model/TerminalApi/BatchResponse.cs b/Adyen/TerminalApi/Models/BatchResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/BatchResponse.cs rename to Adyen/TerminalApi/Models/BatchResponse.cs diff --git a/Adyen/Model/TerminalApi/CapturedSignature.cs b/Adyen/TerminalApi/Models/CapturedSignature.cs similarity index 100% rename from Adyen/Model/TerminalApi/CapturedSignature.cs rename to Adyen/TerminalApi/Models/CapturedSignature.cs diff --git a/Adyen/Model/TerminalApi/CardAcquisitionRequest.cs b/Adyen/TerminalApi/Models/CardAcquisitionRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/CardAcquisitionRequest.cs rename to Adyen/TerminalApi/Models/CardAcquisitionRequest.cs diff --git a/Adyen/Model/TerminalApi/CardAcquisitionResponse.cs b/Adyen/TerminalApi/Models/CardAcquisitionResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/CardAcquisitionResponse.cs rename to Adyen/TerminalApi/Models/CardAcquisitionResponse.cs diff --git a/Adyen/Model/TerminalApi/CardAcquisitionTransaction.cs b/Adyen/TerminalApi/Models/CardAcquisitionTransaction.cs similarity index 100% rename from Adyen/Model/TerminalApi/CardAcquisitionTransaction.cs rename to Adyen/TerminalApi/Models/CardAcquisitionTransaction.cs diff --git a/Adyen/Model/TerminalApi/CardData.cs b/Adyen/TerminalApi/Models/CardData.cs similarity index 100% rename from Adyen/Model/TerminalApi/CardData.cs rename to Adyen/TerminalApi/Models/CardData.cs diff --git a/Adyen/Model/TerminalApi/CardReaderAPDURequest.cs b/Adyen/TerminalApi/Models/CardReaderAPDURequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/CardReaderAPDURequest.cs rename to Adyen/TerminalApi/Models/CardReaderAPDURequest.cs diff --git a/Adyen/Model/TerminalApi/CardReaderAPDUResponse.cs b/Adyen/TerminalApi/Models/CardReaderAPDUResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/CardReaderAPDUResponse.cs rename to Adyen/TerminalApi/Models/CardReaderAPDUResponse.cs diff --git a/Adyen/Model/TerminalApi/CardReaderInitRequest.cs b/Adyen/TerminalApi/Models/CardReaderInitRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/CardReaderInitRequest.cs rename to Adyen/TerminalApi/Models/CardReaderInitRequest.cs diff --git a/Adyen/Model/TerminalApi/CardReaderInitResponse.cs b/Adyen/TerminalApi/Models/CardReaderInitResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/CardReaderInitResponse.cs rename to Adyen/TerminalApi/Models/CardReaderInitResponse.cs diff --git a/Adyen/Model/TerminalApi/CardReaderPowerOffRequest.cs b/Adyen/TerminalApi/Models/CardReaderPowerOffRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/CardReaderPowerOffRequest.cs rename to Adyen/TerminalApi/Models/CardReaderPowerOffRequest.cs diff --git a/Adyen/Model/TerminalApi/CardReaderPowerOffResponse.cs b/Adyen/TerminalApi/Models/CardReaderPowerOffResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/CardReaderPowerOffResponse.cs rename to Adyen/TerminalApi/Models/CardReaderPowerOffResponse.cs diff --git a/Adyen/Model/TerminalApi/CardholderPIN.cs b/Adyen/TerminalApi/Models/CardholderPIN.cs similarity index 100% rename from Adyen/Model/TerminalApi/CardholderPIN.cs rename to Adyen/TerminalApi/Models/CardholderPIN.cs diff --git a/Adyen/Model/TerminalApi/CashHandlingDevice.cs b/Adyen/TerminalApi/Models/CashHandlingDevice.cs similarity index 100% rename from Adyen/Model/TerminalApi/CashHandlingDevice.cs rename to Adyen/TerminalApi/Models/CashHandlingDevice.cs diff --git a/Adyen/Model/TerminalApi/CharacterHeightType.cs b/Adyen/TerminalApi/Models/CharacterHeightType.cs similarity index 100% rename from Adyen/Model/TerminalApi/CharacterHeightType.cs rename to Adyen/TerminalApi/Models/CharacterHeightType.cs diff --git a/Adyen/Model/TerminalApi/CharacterStyleType.cs b/Adyen/TerminalApi/Models/CharacterStyleType.cs similarity index 100% rename from Adyen/Model/TerminalApi/CharacterStyleType.cs rename to Adyen/TerminalApi/Models/CharacterStyleType.cs diff --git a/Adyen/Model/TerminalApi/CharacterWidthType.cs b/Adyen/TerminalApi/Models/CharacterWidthType.cs similarity index 100% rename from Adyen/Model/TerminalApi/CharacterWidthType.cs rename to Adyen/TerminalApi/Models/CharacterWidthType.cs diff --git a/Adyen/Model/TerminalApi/CheckData.cs b/Adyen/TerminalApi/Models/CheckData.cs similarity index 100% rename from Adyen/Model/TerminalApi/CheckData.cs rename to Adyen/TerminalApi/Models/CheckData.cs diff --git a/Adyen/Model/TerminalApi/CheckTypeCodeType.cs b/Adyen/TerminalApi/Models/CheckTypeCodeType.cs similarity index 100% rename from Adyen/Model/TerminalApi/CheckTypeCodeType.cs rename to Adyen/TerminalApi/Models/CheckTypeCodeType.cs diff --git a/Adyen/Model/TerminalApi/CoinsOrBills.cs b/Adyen/TerminalApi/Models/CoinsOrBills.cs similarity index 100% rename from Adyen/Model/TerminalApi/CoinsOrBills.cs rename to Adyen/TerminalApi/Models/CoinsOrBills.cs diff --git a/Adyen/Model/TerminalApi/ColorType.cs b/Adyen/TerminalApi/Models/ColorType.cs similarity index 100% rename from Adyen/Model/TerminalApi/ColorType.cs rename to Adyen/TerminalApi/Models/ColorType.cs diff --git a/Adyen/Model/TerminalApi/ContentInformation.cs b/Adyen/TerminalApi/Models/ContentInformation.cs similarity index 100% rename from Adyen/Model/TerminalApi/ContentInformation.cs rename to Adyen/TerminalApi/Models/ContentInformation.cs diff --git a/Adyen/Model/TerminalApi/ContentType.cs b/Adyen/TerminalApi/Models/ContentType.cs similarity index 100% rename from Adyen/Model/TerminalApi/ContentType.cs rename to Adyen/TerminalApi/Models/ContentType.cs diff --git a/Adyen/Model/TerminalApi/CurrencyConversion.cs b/Adyen/TerminalApi/Models/CurrencyConversion.cs similarity index 100% rename from Adyen/Model/TerminalApi/CurrencyConversion.cs rename to Adyen/TerminalApi/Models/CurrencyConversion.cs diff --git a/Adyen/Model/TerminalApi/CustomerOrder.cs b/Adyen/TerminalApi/Models/CustomerOrder.cs similarity index 100% rename from Adyen/Model/TerminalApi/CustomerOrder.cs rename to Adyen/TerminalApi/Models/CustomerOrder.cs diff --git a/Adyen/Model/TerminalApi/CustomerOrderReqType.cs b/Adyen/TerminalApi/Models/CustomerOrderReqType.cs similarity index 100% rename from Adyen/Model/TerminalApi/CustomerOrderReqType.cs rename to Adyen/TerminalApi/Models/CustomerOrderReqType.cs diff --git a/Adyen/Model/TerminalApi/DeviceType.cs b/Adyen/TerminalApi/Models/DeviceType.cs similarity index 100% rename from Adyen/Model/TerminalApi/DeviceType.cs rename to Adyen/TerminalApi/Models/DeviceType.cs diff --git a/Adyen/Model/TerminalApi/DiagnosisRequest.cs b/Adyen/TerminalApi/Models/DiagnosisRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/DiagnosisRequest.cs rename to Adyen/TerminalApi/Models/DiagnosisRequest.cs diff --git a/Adyen/Model/TerminalApi/DiagnosisResponse.cs b/Adyen/TerminalApi/Models/DiagnosisResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/DiagnosisResponse.cs rename to Adyen/TerminalApi/Models/DiagnosisResponse.cs diff --git a/Adyen/Model/TerminalApi/DigestedData.cs b/Adyen/TerminalApi/Models/DigestedData.cs similarity index 100% rename from Adyen/Model/TerminalApi/DigestedData.cs rename to Adyen/TerminalApi/Models/DigestedData.cs diff --git a/Adyen/Model/TerminalApi/DisplayOutput.cs b/Adyen/TerminalApi/Models/DisplayOutput.cs similarity index 100% rename from Adyen/Model/TerminalApi/DisplayOutput.cs rename to Adyen/TerminalApi/Models/DisplayOutput.cs diff --git a/Adyen/Model/TerminalApi/DisplayRequest.cs b/Adyen/TerminalApi/Models/DisplayRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/DisplayRequest.cs rename to Adyen/TerminalApi/Models/DisplayRequest.cs diff --git a/Adyen/Model/TerminalApi/DisplayResponse.cs b/Adyen/TerminalApi/Models/DisplayResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/DisplayResponse.cs rename to Adyen/TerminalApi/Models/DisplayResponse.cs diff --git a/Adyen/Model/TerminalApi/DocumentQualifierType.cs b/Adyen/TerminalApi/Models/DocumentQualifierType.cs similarity index 100% rename from Adyen/Model/TerminalApi/DocumentQualifierType.cs rename to Adyen/TerminalApi/Models/DocumentQualifierType.cs diff --git a/Adyen/Model/TerminalApi/EnableServiceRequest.cs b/Adyen/TerminalApi/Models/EnableServiceRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/EnableServiceRequest.cs rename to Adyen/TerminalApi/Models/EnableServiceRequest.cs diff --git a/Adyen/Model/TerminalApi/EnableServiceResponse.cs b/Adyen/TerminalApi/Models/EnableServiceResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/EnableServiceResponse.cs rename to Adyen/TerminalApi/Models/EnableServiceResponse.cs diff --git a/Adyen/Model/TerminalApi/EncapsulatedContent.cs b/Adyen/TerminalApi/Models/EncapsulatedContent.cs similarity index 100% rename from Adyen/Model/TerminalApi/EncapsulatedContent.cs rename to Adyen/TerminalApi/Models/EncapsulatedContent.cs diff --git a/Adyen/Model/TerminalApi/EncryptedContent.cs b/Adyen/TerminalApi/Models/EncryptedContent.cs similarity index 100% rename from Adyen/Model/TerminalApi/EncryptedContent.cs rename to Adyen/TerminalApi/Models/EncryptedContent.cs diff --git a/Adyen/Model/TerminalApi/EntryModeType.cs b/Adyen/TerminalApi/Models/EntryModeType.cs similarity index 100% rename from Adyen/Model/TerminalApi/EntryModeType.cs rename to Adyen/TerminalApi/Models/EntryModeType.cs diff --git a/Adyen/Model/TerminalApi/EnvelopedData.cs b/Adyen/TerminalApi/Models/EnvelopedData.cs similarity index 100% rename from Adyen/Model/TerminalApi/EnvelopedData.cs rename to Adyen/TerminalApi/Models/EnvelopedData.cs diff --git a/Adyen/Model/TerminalApi/ErrorConditionType.cs b/Adyen/TerminalApi/Models/ErrorConditionType.cs similarity index 100% rename from Adyen/Model/TerminalApi/ErrorConditionType.cs rename to Adyen/TerminalApi/Models/ErrorConditionType.cs diff --git a/Adyen/Model/TerminalApi/EventNotification.cs b/Adyen/TerminalApi/Models/EventNotification.cs similarity index 100% rename from Adyen/Model/TerminalApi/EventNotification.cs rename to Adyen/TerminalApi/Models/EventNotification.cs diff --git a/Adyen/Model/TerminalApi/EventToNotifyType.cs b/Adyen/TerminalApi/Models/EventToNotifyType.cs similarity index 69% rename from Adyen/Model/TerminalApi/EventToNotifyType.cs rename to Adyen/TerminalApi/Models/EventToNotifyType.cs index 612cdca7b..6350f2988 100644 --- a/Adyen/Model/TerminalApi/EventToNotifyType.cs +++ b/Adyen/TerminalApi/Models/EventToNotifyType.cs @@ -1,7 +1,6 @@ namespace Adyen.Model.TerminalApi { /// - [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")] [System.SerializableAttribute] public enum EventToNotifyType { @@ -53,5 +52,14 @@ public enum EventToNotifyType /// Reject, + + /// Indicates that the terminal has established a network connection. + NetworkConnected, + + /// Indicates that the terminal has no longer a network connection. + NetworkDisconnected, + + /// Delivers the result (or timeout failure) of the Barcode scan. + ScanBarcodeResult, } -} \ No newline at end of file +} diff --git a/Adyen/Model/TerminalApi/ForceTypeModeType.cs b/Adyen/TerminalApi/Models/ForceTypeModeType.cs similarity index 100% rename from Adyen/Model/TerminalApi/ForceTypeModeType.cs rename to Adyen/TerminalApi/Models/ForceTypeModeType.cs diff --git a/Adyen/Model/TerminalApi/GenericProfileType.cs b/Adyen/TerminalApi/Models/GenericProfileType.cs similarity index 100% rename from Adyen/Model/TerminalApi/GenericProfileType.cs rename to Adyen/TerminalApi/Models/GenericProfileType.cs diff --git a/Adyen/Model/TerminalApi/GeographicCoordinates.cs b/Adyen/TerminalApi/Models/GeographicCoordinates.cs similarity index 100% rename from Adyen/Model/TerminalApi/GeographicCoordinates.cs rename to Adyen/TerminalApi/Models/GeographicCoordinates.cs diff --git a/Adyen/Model/TerminalApi/Geolocation.cs b/Adyen/TerminalApi/Models/Geolocation.cs similarity index 100% rename from Adyen/Model/TerminalApi/Geolocation.cs rename to Adyen/TerminalApi/Models/Geolocation.cs diff --git a/Adyen/Model/TerminalApi/GetTotalsRequest.cs b/Adyen/TerminalApi/Models/GetTotalsRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/GetTotalsRequest.cs rename to Adyen/TerminalApi/Models/GetTotalsRequest.cs diff --git a/Adyen/Model/TerminalApi/GetTotalsResponse.cs b/Adyen/TerminalApi/Models/GetTotalsResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/GetTotalsResponse.cs rename to Adyen/TerminalApi/Models/GetTotalsResponse.cs diff --git a/Adyen/Model/TerminalApi/GlobalStatusType.cs b/Adyen/TerminalApi/Models/GlobalStatusType.cs similarity index 100% rename from Adyen/Model/TerminalApi/GlobalStatusType.cs rename to Adyen/TerminalApi/Models/GlobalStatusType.cs diff --git a/Adyen/Model/TerminalApi/HostStatus.cs b/Adyen/TerminalApi/Models/HostStatus.cs similarity index 100% rename from Adyen/Model/TerminalApi/HostStatus.cs rename to Adyen/TerminalApi/Models/HostStatus.cs diff --git a/Adyen/Model/TerminalApi/ICCResetData.cs b/Adyen/TerminalApi/Models/ICCResetData.cs similarity index 100% rename from Adyen/Model/TerminalApi/ICCResetData.cs rename to Adyen/TerminalApi/Models/ICCResetData.cs diff --git a/Adyen/Model/TerminalApi/IdentificationSupportType.cs b/Adyen/TerminalApi/Models/IdentificationSupportType.cs similarity index 100% rename from Adyen/Model/TerminalApi/IdentificationSupportType.cs rename to Adyen/TerminalApi/Models/IdentificationSupportType.cs diff --git a/Adyen/Model/TerminalApi/IdentificationType.cs b/Adyen/TerminalApi/Models/IdentificationType.cs similarity index 100% rename from Adyen/Model/TerminalApi/IdentificationType.cs rename to Adyen/TerminalApi/Models/IdentificationType.cs diff --git a/Adyen/Model/TerminalApi/InfoQualifyType.cs b/Adyen/TerminalApi/Models/InfoQualifyType.cs similarity index 100% rename from Adyen/Model/TerminalApi/InfoQualifyType.cs rename to Adyen/TerminalApi/Models/InfoQualifyType.cs diff --git a/Adyen/Model/TerminalApi/Input.cs b/Adyen/TerminalApi/Models/Input.cs similarity index 100% rename from Adyen/Model/TerminalApi/Input.cs rename to Adyen/TerminalApi/Models/Input.cs diff --git a/Adyen/Model/TerminalApi/InputCommandType.cs b/Adyen/TerminalApi/Models/InputCommandType.cs similarity index 100% rename from Adyen/Model/TerminalApi/InputCommandType.cs rename to Adyen/TerminalApi/Models/InputCommandType.cs diff --git a/Adyen/Model/TerminalApi/InputData.cs b/Adyen/TerminalApi/Models/InputData.cs similarity index 100% rename from Adyen/Model/TerminalApi/InputData.cs rename to Adyen/TerminalApi/Models/InputData.cs diff --git a/Adyen/Model/TerminalApi/InputRequest.cs b/Adyen/TerminalApi/Models/InputRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/InputRequest.cs rename to Adyen/TerminalApi/Models/InputRequest.cs diff --git a/Adyen/Model/TerminalApi/InputResponse.cs b/Adyen/TerminalApi/Models/InputResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/InputResponse.cs rename to Adyen/TerminalApi/Models/InputResponse.cs diff --git a/Adyen/Model/TerminalApi/InputResult.cs b/Adyen/TerminalApi/Models/InputResult.cs similarity index 100% rename from Adyen/Model/TerminalApi/InputResult.cs rename to Adyen/TerminalApi/Models/InputResult.cs diff --git a/Adyen/Model/TerminalApi/InputUpdate.cs b/Adyen/TerminalApi/Models/InputUpdate.cs similarity index 100% rename from Adyen/Model/TerminalApi/InputUpdate.cs rename to Adyen/TerminalApi/Models/InputUpdate.cs diff --git a/Adyen/Model/TerminalApi/Instalment.cs b/Adyen/TerminalApi/Models/Instalment.cs similarity index 100% rename from Adyen/Model/TerminalApi/Instalment.cs rename to Adyen/TerminalApi/Models/Instalment.cs diff --git a/Adyen/Model/TerminalApi/IssuerAndSerialNumber.cs b/Adyen/TerminalApi/Models/IssuerAndSerialNumber.cs similarity index 100% rename from Adyen/Model/TerminalApi/IssuerAndSerialNumber.cs rename to Adyen/TerminalApi/Models/IssuerAndSerialNumber.cs diff --git a/Adyen/Model/TerminalApi/KEK.cs b/Adyen/TerminalApi/Models/KEK.cs similarity index 100% rename from Adyen/Model/TerminalApi/KEK.cs rename to Adyen/TerminalApi/Models/KEK.cs diff --git a/Adyen/Model/TerminalApi/KEKIdentifier.cs b/Adyen/TerminalApi/Models/KEKIdentifier.cs similarity index 100% rename from Adyen/Model/TerminalApi/KEKIdentifier.cs rename to Adyen/TerminalApi/Models/KEKIdentifier.cs diff --git a/Adyen/Model/TerminalApi/KeyTransport.cs b/Adyen/TerminalApi/Models/KeyTransport.cs similarity index 100% rename from Adyen/Model/TerminalApi/KeyTransport.cs rename to Adyen/TerminalApi/Models/KeyTransport.cs diff --git a/Adyen/Model/TerminalApi/LoginRequest.cs b/Adyen/TerminalApi/Models/LoginRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/LoginRequest.cs rename to Adyen/TerminalApi/Models/LoginRequest.cs diff --git a/Adyen/Model/TerminalApi/LoginResponse.cs b/Adyen/TerminalApi/Models/LoginResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/LoginResponse.cs rename to Adyen/TerminalApi/Models/LoginResponse.cs diff --git a/Adyen/Model/TerminalApi/LogoutRequest.cs b/Adyen/TerminalApi/Models/LogoutRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/LogoutRequest.cs rename to Adyen/TerminalApi/Models/LogoutRequest.cs diff --git a/Adyen/Model/TerminalApi/LogoutResponse.cs b/Adyen/TerminalApi/Models/LogoutResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/LogoutResponse.cs rename to Adyen/TerminalApi/Models/LogoutResponse.cs diff --git a/Adyen/Model/TerminalApi/LoyaltyAccount.cs b/Adyen/TerminalApi/Models/LoyaltyAccount.cs similarity index 100% rename from Adyen/Model/TerminalApi/LoyaltyAccount.cs rename to Adyen/TerminalApi/Models/LoyaltyAccount.cs diff --git a/Adyen/Model/TerminalApi/LoyaltyAccountID.cs b/Adyen/TerminalApi/Models/LoyaltyAccountID.cs similarity index 100% rename from Adyen/Model/TerminalApi/LoyaltyAccountID.cs rename to Adyen/TerminalApi/Models/LoyaltyAccountID.cs diff --git a/Adyen/Model/TerminalApi/LoyaltyAccountReq.cs b/Adyen/TerminalApi/Models/LoyaltyAccountReq.cs similarity index 100% rename from Adyen/Model/TerminalApi/LoyaltyAccountReq.cs rename to Adyen/TerminalApi/Models/LoyaltyAccountReq.cs diff --git a/Adyen/Model/TerminalApi/LoyaltyAccountStatus.cs b/Adyen/TerminalApi/Models/LoyaltyAccountStatus.cs similarity index 100% rename from Adyen/Model/TerminalApi/LoyaltyAccountStatus.cs rename to Adyen/TerminalApi/Models/LoyaltyAccountStatus.cs diff --git a/Adyen/Model/TerminalApi/LoyaltyAcquirerData.cs b/Adyen/TerminalApi/Models/LoyaltyAcquirerData.cs similarity index 100% rename from Adyen/Model/TerminalApi/LoyaltyAcquirerData.cs rename to Adyen/TerminalApi/Models/LoyaltyAcquirerData.cs diff --git a/Adyen/Model/TerminalApi/LoyaltyAmount.cs b/Adyen/TerminalApi/Models/LoyaltyAmount.cs similarity index 100% rename from Adyen/Model/TerminalApi/LoyaltyAmount.cs rename to Adyen/TerminalApi/Models/LoyaltyAmount.cs diff --git a/Adyen/Model/TerminalApi/LoyaltyData.cs b/Adyen/TerminalApi/Models/LoyaltyData.cs similarity index 100% rename from Adyen/Model/TerminalApi/LoyaltyData.cs rename to Adyen/TerminalApi/Models/LoyaltyData.cs diff --git a/Adyen/Model/TerminalApi/LoyaltyHandlingType.cs b/Adyen/TerminalApi/Models/LoyaltyHandlingType.cs similarity index 100% rename from Adyen/Model/TerminalApi/LoyaltyHandlingType.cs rename to Adyen/TerminalApi/Models/LoyaltyHandlingType.cs diff --git a/Adyen/Model/TerminalApi/LoyaltyRequest.cs b/Adyen/TerminalApi/Models/LoyaltyRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/LoyaltyRequest.cs rename to Adyen/TerminalApi/Models/LoyaltyRequest.cs diff --git a/Adyen/Model/TerminalApi/LoyaltyResponse.cs b/Adyen/TerminalApi/Models/LoyaltyResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/LoyaltyResponse.cs rename to Adyen/TerminalApi/Models/LoyaltyResponse.cs diff --git a/Adyen/Model/TerminalApi/LoyaltyResult.cs b/Adyen/TerminalApi/Models/LoyaltyResult.cs similarity index 100% rename from Adyen/Model/TerminalApi/LoyaltyResult.cs rename to Adyen/TerminalApi/Models/LoyaltyResult.cs diff --git a/Adyen/Model/TerminalApi/LoyaltyTotals.cs b/Adyen/TerminalApi/Models/LoyaltyTotals.cs similarity index 100% rename from Adyen/Model/TerminalApi/LoyaltyTotals.cs rename to Adyen/TerminalApi/Models/LoyaltyTotals.cs diff --git a/Adyen/Model/TerminalApi/LoyaltyTransaction.cs b/Adyen/TerminalApi/Models/LoyaltyTransaction.cs similarity index 100% rename from Adyen/Model/TerminalApi/LoyaltyTransaction.cs rename to Adyen/TerminalApi/Models/LoyaltyTransaction.cs diff --git a/Adyen/Model/TerminalApi/LoyaltyTransactionType.cs b/Adyen/TerminalApi/Models/LoyaltyTransactionType.cs similarity index 100% rename from Adyen/Model/TerminalApi/LoyaltyTransactionType.cs rename to Adyen/TerminalApi/Models/LoyaltyTransactionType.cs diff --git a/Adyen/Model/TerminalApi/LoyaltyUnitType.cs b/Adyen/TerminalApi/Models/LoyaltyUnitType.cs similarity index 100% rename from Adyen/Model/TerminalApi/LoyaltyUnitType.cs rename to Adyen/TerminalApi/Models/LoyaltyUnitType.cs diff --git a/Adyen/Model/TerminalApi/MenuEntry.cs b/Adyen/TerminalApi/Models/MenuEntry.cs similarity index 100% rename from Adyen/Model/TerminalApi/MenuEntry.cs rename to Adyen/TerminalApi/Models/MenuEntry.cs diff --git a/Adyen/Model/TerminalApi/MenuEntryTagType.cs b/Adyen/TerminalApi/Models/MenuEntryTagType.cs similarity index 100% rename from Adyen/Model/TerminalApi/MenuEntryTagType.cs rename to Adyen/TerminalApi/Models/MenuEntryTagType.cs diff --git a/Adyen/Model/TerminalApi/MessageCategoryType.cs b/Adyen/TerminalApi/Models/MessageCategoryType.cs similarity index 100% rename from Adyen/Model/TerminalApi/MessageCategoryType.cs rename to Adyen/TerminalApi/Models/MessageCategoryType.cs diff --git a/Adyen/Model/TerminalApi/MessageClassType.cs b/Adyen/TerminalApi/Models/MessageClassType.cs similarity index 100% rename from Adyen/Model/TerminalApi/MessageClassType.cs rename to Adyen/TerminalApi/Models/MessageClassType.cs diff --git a/Adyen/Model/TerminalApi/MessageHeader.cs b/Adyen/TerminalApi/Models/MessageHeader.cs similarity index 100% rename from Adyen/Model/TerminalApi/MessageHeader.cs rename to Adyen/TerminalApi/Models/MessageHeader.cs diff --git a/Adyen/Model/TerminalApi/MessageReference.cs b/Adyen/TerminalApi/Models/MessageReference.cs similarity index 100% rename from Adyen/Model/TerminalApi/MessageReference.cs rename to Adyen/TerminalApi/Models/MessageReference.cs diff --git a/Adyen/Model/TerminalApi/MessageType.cs b/Adyen/TerminalApi/Models/MessageType.cs similarity index 100% rename from Adyen/Model/TerminalApi/MessageType.cs rename to Adyen/TerminalApi/Models/MessageType.cs diff --git a/Adyen/Model/TerminalApi/MobileData.cs b/Adyen/TerminalApi/Models/MobileData.cs similarity index 100% rename from Adyen/Model/TerminalApi/MobileData.cs rename to Adyen/TerminalApi/Models/MobileData.cs diff --git a/Adyen/Model/TerminalApi/NamedKeyEncryptedData.cs b/Adyen/TerminalApi/Models/NamedKeyEncryptedData.cs similarity index 100% rename from Adyen/Model/TerminalApi/NamedKeyEncryptedData.cs rename to Adyen/TerminalApi/Models/NamedKeyEncryptedData.cs diff --git a/Adyen/Model/TerminalApi/OriginalPOITransaction.cs b/Adyen/TerminalApi/Models/OriginalPOITransaction.cs similarity index 100% rename from Adyen/Model/TerminalApi/OriginalPOITransaction.cs rename to Adyen/TerminalApi/Models/OriginalPOITransaction.cs diff --git a/Adyen/Model/TerminalApi/OutputBarcode.cs b/Adyen/TerminalApi/Models/OutputBarcode.cs similarity index 100% rename from Adyen/Model/TerminalApi/OutputBarcode.cs rename to Adyen/TerminalApi/Models/OutputBarcode.cs diff --git a/Adyen/Model/TerminalApi/OutputContent.cs b/Adyen/TerminalApi/Models/OutputContent.cs similarity index 100% rename from Adyen/Model/TerminalApi/OutputContent.cs rename to Adyen/TerminalApi/Models/OutputContent.cs diff --git a/Adyen/Model/TerminalApi/OutputFormatType.cs b/Adyen/TerminalApi/Models/OutputFormatType.cs similarity index 100% rename from Adyen/Model/TerminalApi/OutputFormatType.cs rename to Adyen/TerminalApi/Models/OutputFormatType.cs diff --git a/Adyen/Model/TerminalApi/OutputResult.cs b/Adyen/TerminalApi/Models/OutputResult.cs similarity index 100% rename from Adyen/Model/TerminalApi/OutputResult.cs rename to Adyen/TerminalApi/Models/OutputResult.cs diff --git a/Adyen/Model/TerminalApi/OutputText.cs b/Adyen/TerminalApi/Models/OutputText.cs similarity index 100% rename from Adyen/Model/TerminalApi/OutputText.cs rename to Adyen/TerminalApi/Models/OutputText.cs diff --git a/Adyen/Model/TerminalApi/PINFormatType.cs b/Adyen/TerminalApi/Models/PINFormatType.cs similarity index 100% rename from Adyen/Model/TerminalApi/PINFormatType.cs rename to Adyen/TerminalApi/Models/PINFormatType.cs diff --git a/Adyen/Model/TerminalApi/PINRequest.cs b/Adyen/TerminalApi/Models/PINRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/PINRequest.cs rename to Adyen/TerminalApi/Models/PINRequest.cs diff --git a/Adyen/Model/TerminalApi/PINRequestType.cs b/Adyen/TerminalApi/Models/PINRequestType.cs similarity index 100% rename from Adyen/Model/TerminalApi/PINRequestType.cs rename to Adyen/TerminalApi/Models/PINRequestType.cs diff --git a/Adyen/Model/TerminalApi/PINResponse.cs b/Adyen/TerminalApi/Models/PINResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/PINResponse.cs rename to Adyen/TerminalApi/Models/PINResponse.cs diff --git a/Adyen/Model/TerminalApi/POIData.cs b/Adyen/TerminalApi/Models/POIData.cs similarity index 100% rename from Adyen/Model/TerminalApi/POIData.cs rename to Adyen/TerminalApi/Models/POIData.cs diff --git a/Adyen/Model/TerminalApi/POIProfile.cs b/Adyen/TerminalApi/Models/POIProfile.cs similarity index 100% rename from Adyen/Model/TerminalApi/POIProfile.cs rename to Adyen/TerminalApi/Models/POIProfile.cs diff --git a/Adyen/Model/TerminalApi/POISoftware.cs b/Adyen/TerminalApi/Models/POISoftware.cs similarity index 100% rename from Adyen/Model/TerminalApi/POISoftware.cs rename to Adyen/TerminalApi/Models/POISoftware.cs diff --git a/Adyen/Model/TerminalApi/POIStatus.cs b/Adyen/TerminalApi/Models/POIStatus.cs similarity index 100% rename from Adyen/Model/TerminalApi/POIStatus.cs rename to Adyen/TerminalApi/Models/POIStatus.cs diff --git a/Adyen/Model/TerminalApi/POISystemData.cs b/Adyen/TerminalApi/Models/POISystemData.cs similarity index 100% rename from Adyen/Model/TerminalApi/POISystemData.cs rename to Adyen/TerminalApi/Models/POISystemData.cs diff --git a/Adyen/Model/TerminalApi/POITerminalData.cs b/Adyen/TerminalApi/Models/POITerminalData.cs similarity index 100% rename from Adyen/Model/TerminalApi/POITerminalData.cs rename to Adyen/TerminalApi/Models/POITerminalData.cs diff --git a/Adyen/Model/TerminalApi/Parameter.cs b/Adyen/TerminalApi/Models/Parameter.cs similarity index 100% rename from Adyen/Model/TerminalApi/Parameter.cs rename to Adyen/TerminalApi/Models/Parameter.cs diff --git a/Adyen/Model/TerminalApi/PaymentAccountReq.cs b/Adyen/TerminalApi/Models/PaymentAccountReq.cs similarity index 100% rename from Adyen/Model/TerminalApi/PaymentAccountReq.cs rename to Adyen/TerminalApi/Models/PaymentAccountReq.cs diff --git a/Adyen/Model/TerminalApi/PaymentAccountStatus.cs b/Adyen/TerminalApi/Models/PaymentAccountStatus.cs similarity index 100% rename from Adyen/Model/TerminalApi/PaymentAccountStatus.cs rename to Adyen/TerminalApi/Models/PaymentAccountStatus.cs diff --git a/Adyen/Model/TerminalApi/PaymentAcquirerData.cs b/Adyen/TerminalApi/Models/PaymentAcquirerData.cs similarity index 100% rename from Adyen/Model/TerminalApi/PaymentAcquirerData.cs rename to Adyen/TerminalApi/Models/PaymentAcquirerData.cs diff --git a/Adyen/Model/TerminalApi/PaymentData.cs b/Adyen/TerminalApi/Models/PaymentData.cs similarity index 100% rename from Adyen/Model/TerminalApi/PaymentData.cs rename to Adyen/TerminalApi/Models/PaymentData.cs diff --git a/Adyen/Model/TerminalApi/PaymentInstrumentData.cs b/Adyen/TerminalApi/Models/PaymentInstrumentData.cs similarity index 100% rename from Adyen/Model/TerminalApi/PaymentInstrumentData.cs rename to Adyen/TerminalApi/Models/PaymentInstrumentData.cs diff --git a/Adyen/Model/TerminalApi/PaymentInstrumentType.cs b/Adyen/TerminalApi/Models/PaymentInstrumentType.cs similarity index 100% rename from Adyen/Model/TerminalApi/PaymentInstrumentType.cs rename to Adyen/TerminalApi/Models/PaymentInstrumentType.cs diff --git a/Adyen/Model/TerminalApi/PaymentReceipt.cs b/Adyen/TerminalApi/Models/PaymentReceipt.cs similarity index 100% rename from Adyen/Model/TerminalApi/PaymentReceipt.cs rename to Adyen/TerminalApi/Models/PaymentReceipt.cs diff --git a/Adyen/Model/TerminalApi/PaymentRequest.cs b/Adyen/TerminalApi/Models/PaymentRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/PaymentRequest.cs rename to Adyen/TerminalApi/Models/PaymentRequest.cs diff --git a/Adyen/Model/TerminalApi/PaymentResponse.cs b/Adyen/TerminalApi/Models/PaymentResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/PaymentResponse.cs rename to Adyen/TerminalApi/Models/PaymentResponse.cs diff --git a/Adyen/Model/TerminalApi/PaymentResult.cs b/Adyen/TerminalApi/Models/PaymentResult.cs similarity index 100% rename from Adyen/Model/TerminalApi/PaymentResult.cs rename to Adyen/TerminalApi/Models/PaymentResult.cs diff --git a/Adyen/Model/TerminalApi/PaymentToken.cs b/Adyen/TerminalApi/Models/PaymentToken.cs similarity index 100% rename from Adyen/Model/TerminalApi/PaymentToken.cs rename to Adyen/TerminalApi/Models/PaymentToken.cs diff --git a/Adyen/Model/TerminalApi/PaymentTotals.cs b/Adyen/TerminalApi/Models/PaymentTotals.cs similarity index 100% rename from Adyen/Model/TerminalApi/PaymentTotals.cs rename to Adyen/TerminalApi/Models/PaymentTotals.cs diff --git a/Adyen/Model/TerminalApi/PaymentTransaction.cs b/Adyen/TerminalApi/Models/PaymentTransaction.cs similarity index 100% rename from Adyen/Model/TerminalApi/PaymentTransaction.cs rename to Adyen/TerminalApi/Models/PaymentTransaction.cs diff --git a/Adyen/Model/TerminalApi/PaymentType.cs b/Adyen/TerminalApi/Models/PaymentType.cs similarity index 100% rename from Adyen/Model/TerminalApi/PaymentType.cs rename to Adyen/TerminalApi/Models/PaymentType.cs diff --git a/Adyen/Model/TerminalApi/PerformedTransaction.cs b/Adyen/TerminalApi/Models/PerformedTransaction.cs similarity index 100% rename from Adyen/Model/TerminalApi/PerformedTransaction.cs rename to Adyen/TerminalApi/Models/PerformedTransaction.cs diff --git a/Adyen/Model/TerminalApi/PeriodUnitType.cs b/Adyen/TerminalApi/Models/PeriodUnitType.cs similarity index 100% rename from Adyen/Model/TerminalApi/PeriodUnitType.cs rename to Adyen/TerminalApi/Models/PeriodUnitType.cs diff --git a/Adyen/Model/TerminalApi/PredefinedContent.cs b/Adyen/TerminalApi/Models/PredefinedContent.cs similarity index 100% rename from Adyen/Model/TerminalApi/PredefinedContent.cs rename to Adyen/TerminalApi/Models/PredefinedContent.cs diff --git a/Adyen/Model/TerminalApi/PrintOutput.cs b/Adyen/TerminalApi/Models/PrintOutput.cs similarity index 100% rename from Adyen/Model/TerminalApi/PrintOutput.cs rename to Adyen/TerminalApi/Models/PrintOutput.cs diff --git a/Adyen/Model/TerminalApi/PrintRequest.cs b/Adyen/TerminalApi/Models/PrintRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/PrintRequest.cs rename to Adyen/TerminalApi/Models/PrintRequest.cs diff --git a/Adyen/Model/TerminalApi/PrintResponse.cs b/Adyen/TerminalApi/Models/PrintResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/PrintResponse.cs rename to Adyen/TerminalApi/Models/PrintResponse.cs diff --git a/Adyen/Model/TerminalApi/PrinterStatusType.cs b/Adyen/TerminalApi/Models/PrinterStatusType.cs similarity index 100% rename from Adyen/Model/TerminalApi/PrinterStatusType.cs rename to Adyen/TerminalApi/Models/PrinterStatusType.cs diff --git a/Adyen/Model/TerminalApi/Rebates.cs b/Adyen/TerminalApi/Models/Rebates.cs similarity index 100% rename from Adyen/Model/TerminalApi/Rebates.cs rename to Adyen/TerminalApi/Models/Rebates.cs diff --git a/Adyen/Model/TerminalApi/RecipientIdentifier.cs b/Adyen/TerminalApi/Models/RecipientIdentifier.cs similarity index 100% rename from Adyen/Model/TerminalApi/RecipientIdentifier.cs rename to Adyen/TerminalApi/Models/RecipientIdentifier.cs diff --git a/Adyen/Model/TerminalApi/ReconciliationRequest.cs b/Adyen/TerminalApi/Models/ReconciliationRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/ReconciliationRequest.cs rename to Adyen/TerminalApi/Models/ReconciliationRequest.cs diff --git a/Adyen/Model/TerminalApi/ReconciliationResponse.cs b/Adyen/TerminalApi/Models/ReconciliationResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/ReconciliationResponse.cs rename to Adyen/TerminalApi/Models/ReconciliationResponse.cs diff --git a/Adyen/Model/TerminalApi/ReconciliationType.cs b/Adyen/TerminalApi/Models/ReconciliationType.cs similarity index 100% rename from Adyen/Model/TerminalApi/ReconciliationType.cs rename to Adyen/TerminalApi/Models/ReconciliationType.cs diff --git a/Adyen/Model/TerminalApi/RelativeDistinguishedName.cs b/Adyen/TerminalApi/Models/RelativeDistinguishedName.cs similarity index 100% rename from Adyen/Model/TerminalApi/RelativeDistinguishedName.cs rename to Adyen/TerminalApi/Models/RelativeDistinguishedName.cs diff --git a/Adyen/Model/TerminalApi/RepeatedMessageResponse.cs b/Adyen/TerminalApi/Models/RepeatedMessageResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/RepeatedMessageResponse.cs rename to Adyen/TerminalApi/Models/RepeatedMessageResponse.cs diff --git a/Adyen/Model/TerminalApi/RepeatedResponseMessageBody.cs b/Adyen/TerminalApi/Models/RepeatedResponseMessageBody.cs similarity index 100% rename from Adyen/Model/TerminalApi/RepeatedResponseMessageBody.cs rename to Adyen/TerminalApi/Models/RepeatedResponseMessageBody.cs diff --git a/Adyen/Model/TerminalApi/Message/SaleToPOIRequest.cs b/Adyen/TerminalApi/Models/Requests/SaleToPOIRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/Message/SaleToPOIRequest.cs rename to Adyen/TerminalApi/Models/Requests/SaleToPOIRequest.cs diff --git a/Adyen/Model/TerminalApi/Message/SaleToPoiRequestSecured.cs b/Adyen/TerminalApi/Models/Requests/SaleToPoiRequestSecured.cs similarity index 100% rename from Adyen/Model/TerminalApi/Message/SaleToPoiRequestSecured.cs rename to Adyen/TerminalApi/Models/Requests/SaleToPoiRequestSecured.cs diff --git a/Adyen/Model/TerminalApi/Message/SaleToPoiResponseSecured.cs b/Adyen/TerminalApi/Models/Requests/SaleToPoiResponseSecured.cs similarity index 100% rename from Adyen/Model/TerminalApi/Message/SaleToPoiResponseSecured.cs rename to Adyen/TerminalApi/Models/Requests/SaleToPoiResponseSecured.cs diff --git a/Adyen/Model/TerminalApi/Response.cs b/Adyen/TerminalApi/Models/Response.cs similarity index 100% rename from Adyen/Model/TerminalApi/Response.cs rename to Adyen/TerminalApi/Models/Response.cs diff --git a/Adyen/Model/TerminalApi/ResponseModeType.cs b/Adyen/TerminalApi/Models/ResponseModeType.cs similarity index 100% rename from Adyen/Model/TerminalApi/ResponseModeType.cs rename to Adyen/TerminalApi/Models/ResponseModeType.cs diff --git a/Adyen/Model/TerminalApi/ResultType.cs b/Adyen/TerminalApi/Models/ResultType.cs similarity index 100% rename from Adyen/Model/TerminalApi/ResultType.cs rename to Adyen/TerminalApi/Models/ResultType.cs diff --git a/Adyen/Model/TerminalApi/ReversalReasonType.cs b/Adyen/TerminalApi/Models/ReversalReasonType.cs similarity index 100% rename from Adyen/Model/TerminalApi/ReversalReasonType.cs rename to Adyen/TerminalApi/Models/ReversalReasonType.cs diff --git a/Adyen/Model/TerminalApi/ReversalRequest.cs b/Adyen/TerminalApi/Models/ReversalRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/ReversalRequest.cs rename to Adyen/TerminalApi/Models/ReversalRequest.cs diff --git a/Adyen/Model/TerminalApi/ReversalResponse.cs b/Adyen/TerminalApi/Models/ReversalResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/ReversalResponse.cs rename to Adyen/TerminalApi/Models/ReversalResponse.cs diff --git a/Adyen/Model/TerminalApi/SaleData.cs b/Adyen/TerminalApi/Models/SaleData.cs similarity index 100% rename from Adyen/Model/TerminalApi/SaleData.cs rename to Adyen/TerminalApi/Models/SaleData.cs diff --git a/Adyen/Model/TerminalApi/SaleItem.cs b/Adyen/TerminalApi/Models/SaleItem.cs similarity index 100% rename from Adyen/Model/TerminalApi/SaleItem.cs rename to Adyen/TerminalApi/Models/SaleItem.cs diff --git a/Adyen/Model/TerminalApi/SaleItemRebate.cs b/Adyen/TerminalApi/Models/SaleItemRebate.cs similarity index 100% rename from Adyen/Model/TerminalApi/SaleItemRebate.cs rename to Adyen/TerminalApi/Models/SaleItemRebate.cs diff --git a/Adyen/Model/TerminalApi/SaleProfile.cs b/Adyen/TerminalApi/Models/SaleProfile.cs similarity index 100% rename from Adyen/Model/TerminalApi/SaleProfile.cs rename to Adyen/TerminalApi/Models/SaleProfile.cs diff --git a/Adyen/Model/TerminalApi/SaleSoftware.cs b/Adyen/TerminalApi/Models/SaleSoftware.cs similarity index 100% rename from Adyen/Model/TerminalApi/SaleSoftware.cs rename to Adyen/TerminalApi/Models/SaleSoftware.cs diff --git a/Adyen/Model/TerminalApi/SaleTerminalData.cs b/Adyen/TerminalApi/Models/SaleTerminalData.cs similarity index 100% rename from Adyen/Model/TerminalApi/SaleTerminalData.cs rename to Adyen/TerminalApi/Models/SaleTerminalData.cs diff --git a/Adyen/Model/Terminal/SaleToAcquirerData.cs b/Adyen/TerminalApi/Models/SaleToAcquirerData.cs similarity index 100% rename from Adyen/Model/Terminal/SaleToAcquirerData.cs rename to Adyen/TerminalApi/Models/SaleToAcquirerData.cs diff --git a/Adyen/Model/TerminalApi/SaleToIssuerData.cs b/Adyen/TerminalApi/Models/SaleToIssuerData.cs similarity index 100% rename from Adyen/Model/TerminalApi/SaleToIssuerData.cs rename to Adyen/TerminalApi/Models/SaleToIssuerData.cs diff --git a/Adyen/Model/TerminalApi/SaleToPOIMessage.cs b/Adyen/TerminalApi/Models/SaleToPOIMessage.cs similarity index 100% rename from Adyen/Model/TerminalApi/SaleToPOIMessage.cs rename to Adyen/TerminalApi/Models/SaleToPOIMessage.cs diff --git a/Adyen/Model/TerminalApi/SaleToPOIResponse.cs b/Adyen/TerminalApi/Models/SaleToPOIResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/SaleToPOIResponse.cs rename to Adyen/TerminalApi/Models/SaleToPOIResponse.cs diff --git a/Adyen/Model/TerminalApi/SensitiveCardData.cs b/Adyen/TerminalApi/Models/SensitiveCardData.cs similarity index 100% rename from Adyen/Model/TerminalApi/SensitiveCardData.cs rename to Adyen/TerminalApi/Models/SensitiveCardData.cs diff --git a/Adyen/Model/TerminalApi/SensitiveMobileData.cs b/Adyen/TerminalApi/Models/SensitiveMobileData.cs similarity index 100% rename from Adyen/Model/TerminalApi/SensitiveMobileData.cs rename to Adyen/TerminalApi/Models/SensitiveMobileData.cs diff --git a/Adyen/Model/TerminalApi/SignaturePoint.cs b/Adyen/TerminalApi/Models/SignaturePoint.cs similarity index 100% rename from Adyen/Model/TerminalApi/SignaturePoint.cs rename to Adyen/TerminalApi/Models/SignaturePoint.cs diff --git a/Adyen/Model/TerminalApi/SignedData.cs b/Adyen/TerminalApi/Models/SignedData.cs similarity index 100% rename from Adyen/Model/TerminalApi/SignedData.cs rename to Adyen/TerminalApi/Models/SignedData.cs diff --git a/Adyen/Model/TerminalApi/Signer.cs b/Adyen/TerminalApi/Models/Signer.cs similarity index 100% rename from Adyen/Model/TerminalApi/Signer.cs rename to Adyen/TerminalApi/Models/Signer.cs diff --git a/Adyen/Model/TerminalApi/SignerIdentifier.cs b/Adyen/TerminalApi/Models/SignerIdentifier.cs similarity index 100% rename from Adyen/Model/TerminalApi/SignerIdentifier.cs rename to Adyen/TerminalApi/Models/SignerIdentifier.cs diff --git a/Adyen/Model/TerminalApi/SoundActionType.cs b/Adyen/TerminalApi/Models/SoundActionType.cs similarity index 100% rename from Adyen/Model/TerminalApi/SoundActionType.cs rename to Adyen/TerminalApi/Models/SoundActionType.cs diff --git a/Adyen/Model/TerminalApi/SoundContent.cs b/Adyen/TerminalApi/Models/SoundContent.cs similarity index 100% rename from Adyen/Model/TerminalApi/SoundContent.cs rename to Adyen/TerminalApi/Models/SoundContent.cs diff --git a/Adyen/Model/TerminalApi/SoundFormatType.cs b/Adyen/TerminalApi/Models/SoundFormatType.cs similarity index 100% rename from Adyen/Model/TerminalApi/SoundFormatType.cs rename to Adyen/TerminalApi/Models/SoundFormatType.cs diff --git a/Adyen/Model/TerminalApi/SoundRequest.cs b/Adyen/TerminalApi/Models/SoundRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/SoundRequest.cs rename to Adyen/TerminalApi/Models/SoundRequest.cs diff --git a/Adyen/Model/TerminalApi/SoundResponse.cs b/Adyen/TerminalApi/Models/SoundResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/SoundResponse.cs rename to Adyen/TerminalApi/Models/SoundResponse.cs diff --git a/Adyen/Model/Terminal/Split.cs b/Adyen/TerminalApi/Models/Split.cs similarity index 100% rename from Adyen/Model/Terminal/Split.cs rename to Adyen/TerminalApi/Models/Split.cs diff --git a/Adyen/Model/Terminal/SplitItem.cs b/Adyen/TerminalApi/Models/SplitItem.cs similarity index 100% rename from Adyen/Model/Terminal/SplitItem.cs rename to Adyen/TerminalApi/Models/SplitItem.cs diff --git a/Adyen/Model/Terminal/SplitItemType.cs b/Adyen/TerminalApi/Models/SplitItemType.cs similarity index 100% rename from Adyen/Model/Terminal/SplitItemType.cs rename to Adyen/TerminalApi/Models/SplitItemType.cs diff --git a/Adyen/Model/TerminalApi/SponsoredMerchant.cs b/Adyen/TerminalApi/Models/SponsoredMerchant.cs similarity index 100% rename from Adyen/Model/TerminalApi/SponsoredMerchant.cs rename to Adyen/TerminalApi/Models/SponsoredMerchant.cs diff --git a/Adyen/Model/TerminalApi/StoredValueAccountID.cs b/Adyen/TerminalApi/Models/StoredValueAccountID.cs similarity index 100% rename from Adyen/Model/TerminalApi/StoredValueAccountID.cs rename to Adyen/TerminalApi/Models/StoredValueAccountID.cs diff --git a/Adyen/Model/TerminalApi/StoredValueAccountStatus.cs b/Adyen/TerminalApi/Models/StoredValueAccountStatus.cs similarity index 100% rename from Adyen/Model/TerminalApi/StoredValueAccountStatus.cs rename to Adyen/TerminalApi/Models/StoredValueAccountStatus.cs diff --git a/Adyen/Model/TerminalApi/StoredValueAccountType.cs b/Adyen/TerminalApi/Models/StoredValueAccountType.cs similarity index 100% rename from Adyen/Model/TerminalApi/StoredValueAccountType.cs rename to Adyen/TerminalApi/Models/StoredValueAccountType.cs diff --git a/Adyen/Model/TerminalApi/StoredValueData.cs b/Adyen/TerminalApi/Models/StoredValueData.cs similarity index 100% rename from Adyen/Model/TerminalApi/StoredValueData.cs rename to Adyen/TerminalApi/Models/StoredValueData.cs diff --git a/Adyen/Model/TerminalApi/StoredValueRequest.cs b/Adyen/TerminalApi/Models/StoredValueRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/StoredValueRequest.cs rename to Adyen/TerminalApi/Models/StoredValueRequest.cs diff --git a/Adyen/Model/TerminalApi/StoredValueResponse.cs b/Adyen/TerminalApi/Models/StoredValueResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/StoredValueResponse.cs rename to Adyen/TerminalApi/Models/StoredValueResponse.cs diff --git a/Adyen/Model/TerminalApi/StoredValueResult.cs b/Adyen/TerminalApi/Models/StoredValueResult.cs similarity index 100% rename from Adyen/Model/TerminalApi/StoredValueResult.cs rename to Adyen/TerminalApi/Models/StoredValueResult.cs diff --git a/Adyen/Model/TerminalApi/StoredValueTransactionType.cs b/Adyen/TerminalApi/Models/StoredValueTransactionType.cs similarity index 100% rename from Adyen/Model/TerminalApi/StoredValueTransactionType.cs rename to Adyen/TerminalApi/Models/StoredValueTransactionType.cs diff --git a/Adyen/Model/TerminalApi/TerminalEnvironmentType.cs b/Adyen/TerminalApi/Models/TerminalEnvironmentType.cs similarity index 100% rename from Adyen/Model/TerminalApi/TerminalEnvironmentType.cs rename to Adyen/TerminalApi/Models/TerminalEnvironmentType.cs diff --git a/Adyen/Model/TerminalApi/TokenRequestedType.cs b/Adyen/TerminalApi/Models/TokenRequestedType.cs similarity index 100% rename from Adyen/Model/TerminalApi/TokenRequestedType.cs rename to Adyen/TerminalApi/Models/TokenRequestedType.cs diff --git a/Adyen/Model/TerminalApi/TotalDetailsType.cs b/Adyen/TerminalApi/Models/TotalDetailsType.cs similarity index 100% rename from Adyen/Model/TerminalApi/TotalDetailsType.cs rename to Adyen/TerminalApi/Models/TotalDetailsType.cs diff --git a/Adyen/Model/TerminalApi/TotalFilter.cs b/Adyen/TerminalApi/Models/TotalFilter.cs similarity index 100% rename from Adyen/Model/TerminalApi/TotalFilter.cs rename to Adyen/TerminalApi/Models/TotalFilter.cs diff --git a/Adyen/Model/TerminalApi/TrackData.cs b/Adyen/TerminalApi/Models/TrackData.cs similarity index 100% rename from Adyen/Model/TerminalApi/TrackData.cs rename to Adyen/TerminalApi/Models/TrackData.cs diff --git a/Adyen/Model/TerminalApi/TrackFormatType.cs b/Adyen/TerminalApi/Models/TrackFormatType.cs similarity index 100% rename from Adyen/Model/TerminalApi/TrackFormatType.cs rename to Adyen/TerminalApi/Models/TrackFormatType.cs diff --git a/Adyen/Model/TerminalApi/TransactionActionType.cs b/Adyen/TerminalApi/Models/TransactionActionType.cs similarity index 100% rename from Adyen/Model/TerminalApi/TransactionActionType.cs rename to Adyen/TerminalApi/Models/TransactionActionType.cs diff --git a/Adyen/Model/TerminalApi/TransactionConditions.cs b/Adyen/TerminalApi/Models/TransactionConditions.cs similarity index 100% rename from Adyen/Model/TerminalApi/TransactionConditions.cs rename to Adyen/TerminalApi/Models/TransactionConditions.cs diff --git a/Adyen/Model/TerminalApi/TransactionIdentification.cs b/Adyen/TerminalApi/Models/TransactionIdentification.cs similarity index 100% rename from Adyen/Model/TerminalApi/TransactionIdentification.cs rename to Adyen/TerminalApi/Models/TransactionIdentification.cs diff --git a/Adyen/Model/TerminalApi/TransactionStatusRequest.cs b/Adyen/TerminalApi/Models/TransactionStatusRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/TransactionStatusRequest.cs rename to Adyen/TerminalApi/Models/TransactionStatusRequest.cs diff --git a/Adyen/Model/TerminalApi/TransactionStatusResponse.cs b/Adyen/TerminalApi/Models/TransactionStatusResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/TransactionStatusResponse.cs rename to Adyen/TerminalApi/Models/TransactionStatusResponse.cs diff --git a/Adyen/Model/TerminalApi/TransactionToPerform.cs b/Adyen/TerminalApi/Models/TransactionToPerform.cs similarity index 100% rename from Adyen/Model/TerminalApi/TransactionToPerform.cs rename to Adyen/TerminalApi/Models/TransactionToPerform.cs diff --git a/Adyen/Model/TerminalApi/TransactionTotals.cs b/Adyen/TerminalApi/Models/TransactionTotals.cs similarity index 100% rename from Adyen/Model/TerminalApi/TransactionTotals.cs rename to Adyen/TerminalApi/Models/TransactionTotals.cs diff --git a/Adyen/Model/TerminalApi/TransactionType.cs b/Adyen/TerminalApi/Models/TransactionType.cs similarity index 100% rename from Adyen/Model/TerminalApi/TransactionType.cs rename to Adyen/TerminalApi/Models/TransactionType.cs diff --git a/Adyen/Model/TerminalApi/TransmitRequest.cs b/Adyen/TerminalApi/Models/TransmitRequest.cs similarity index 100% rename from Adyen/Model/TerminalApi/TransmitRequest.cs rename to Adyen/TerminalApi/Models/TransmitRequest.cs diff --git a/Adyen/Model/TerminalApi/TransmitResponse.cs b/Adyen/TerminalApi/Models/TransmitResponse.cs similarity index 100% rename from Adyen/Model/TerminalApi/TransmitResponse.cs rename to Adyen/TerminalApi/Models/TransmitResponse.cs diff --git a/Adyen/Model/TerminalApi/UTMCoordinates.cs b/Adyen/TerminalApi/Models/UTMCoordinates.cs similarity index 100% rename from Adyen/Model/TerminalApi/UTMCoordinates.cs rename to Adyen/TerminalApi/Models/UTMCoordinates.cs diff --git a/Adyen/Model/TerminalApi/UnitOfMeasureType.cs b/Adyen/TerminalApi/Models/UnitOfMeasureType.cs similarity index 100% rename from Adyen/Model/TerminalApi/UnitOfMeasureType.cs rename to Adyen/TerminalApi/Models/UnitOfMeasureType.cs diff --git a/Adyen/Model/TerminalApi/VersionType.cs b/Adyen/TerminalApi/Models/VersionType.cs similarity index 100% rename from Adyen/Model/TerminalApi/VersionType.cs rename to Adyen/TerminalApi/Models/VersionType.cs diff --git a/Adyen/Model/RequestOptions.cs b/Adyen/TerminalApi/RequestOptions.cs similarity index 100% rename from Adyen/Model/RequestOptions.cs rename to Adyen/TerminalApi/RequestOptions.cs diff --git a/Adyen/Security/AesEncryptor.cs b/Adyen/TerminalApi/Security/AesEncryptor.cs similarity index 100% rename from Adyen/Security/AesEncryptor.cs rename to Adyen/TerminalApi/Security/AesEncryptor.cs diff --git a/Adyen/Security/EncryptionCredentialDetails.cs b/Adyen/TerminalApi/Security/EncryptionCredentialDetails.cs similarity index 100% rename from Adyen/Security/EncryptionCredentialDetails.cs rename to Adyen/TerminalApi/Security/EncryptionCredentialDetails.cs diff --git a/Adyen/Security/EncryptionDerivedKey.cs b/Adyen/TerminalApi/Security/EncryptionDerivedKey.cs similarity index 100% rename from Adyen/Security/EncryptionDerivedKey.cs rename to Adyen/TerminalApi/Security/EncryptionDerivedKey.cs diff --git a/Adyen/Security/EncryptionDerivedKeyGenerator.cs b/Adyen/TerminalApi/Security/EncryptionDerivedKeyGenerator.cs similarity index 100% rename from Adyen/Security/EncryptionDerivedKeyGenerator.cs rename to Adyen/TerminalApi/Security/EncryptionDerivedKeyGenerator.cs diff --git a/Adyen/Security/Exceptions/NexoCryptoException.cs b/Adyen/TerminalApi/Security/Exceptions/NexoCryptoException.cs similarity index 100% rename from Adyen/Security/Exceptions/NexoCryptoException.cs rename to Adyen/TerminalApi/Security/Exceptions/NexoCryptoException.cs diff --git a/Adyen/Security/Extension/ArrayExtension.cs b/Adyen/TerminalApi/Security/Extension/ArrayExtension.cs similarity index 100% rename from Adyen/Security/Extension/ArrayExtension.cs rename to Adyen/TerminalApi/Security/Extension/ArrayExtension.cs diff --git a/Adyen/Security/HmacSha256Wrapper.cs b/Adyen/TerminalApi/Security/HmacSha256Wrapper.cs similarity index 100% rename from Adyen/Security/HmacSha256Wrapper.cs rename to Adyen/TerminalApi/Security/HmacSha256Wrapper.cs diff --git a/Adyen/Security/IvModGenerator.cs b/Adyen/TerminalApi/Security/IvModGenerator.cs similarity index 100% rename from Adyen/Security/IvModGenerator.cs rename to Adyen/TerminalApi/Security/IvModGenerator.cs diff --git a/Adyen/Security/SaleToPoiMessageSecured.cs b/Adyen/TerminalApi/Security/SaleToPoiMessageSecured.cs similarity index 100% rename from Adyen/Security/SaleToPoiMessageSecured.cs rename to Adyen/TerminalApi/Security/SaleToPoiMessageSecured.cs diff --git a/Adyen/Security/SaleToPoiMessageSecuredEncryptor.cs b/Adyen/TerminalApi/Security/SaleToPoiMessageSecuredEncryptor.cs similarity index 100% rename from Adyen/Security/SaleToPoiMessageSecuredEncryptor.cs rename to Adyen/TerminalApi/Security/SaleToPoiMessageSecuredEncryptor.cs diff --git a/Adyen/Security/SecurityTrailer.cs b/Adyen/TerminalApi/Security/SecurityTrailer.cs similarity index 100% rename from Adyen/Security/SecurityTrailer.cs rename to Adyen/TerminalApi/Security/SecurityTrailer.cs diff --git a/Adyen/Security/TerminalCommonNameValidator.cs b/Adyen/TerminalApi/Security/TerminalCommonNameValidator.cs similarity index 100% rename from Adyen/Security/TerminalCommonNameValidator.cs rename to Adyen/TerminalApi/Security/TerminalCommonNameValidator.cs diff --git a/Adyen/Service/ServiceResource.cs b/Adyen/TerminalApi/Services/ServiceResource.cs similarity index 97% rename from Adyen/Service/ServiceResource.cs rename to Adyen/TerminalApi/Services/ServiceResource.cs index d5ffc3384..fce890f9a 100644 --- a/Adyen/Service/ServiceResource.cs +++ b/Adyen/TerminalApi/Services/ServiceResource.cs @@ -1,46 +1,46 @@ -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Adyen.Model; -using Newtonsoft.Json; - -namespace Adyen.Service -{ - public class ServiceResource - { - private readonly AbstractService _abstractService; - protected string Endpoint; - - public ServiceResource(AbstractService abstractService, string endpoint) - { - _abstractService = abstractService; - Endpoint = endpoint; - } - - public string Request(string json, RequestOptions requestOptions = null, HttpMethod httpMethod = null) - { - var clientInterface = _abstractService.Client.HttpClient; - return clientInterface.Request(Endpoint, json, requestOptions, httpMethod); - } - - public T Request(string json, RequestOptions requestOptions = null, HttpMethod httpMethod = null) - { - var clientInterface = _abstractService.Client.HttpClient; - var jsonResponse = clientInterface.Request(Endpoint, json, requestOptions, httpMethod); - return JsonConvert.DeserializeObject(jsonResponse); - } - - public async Task RequestAsync(string json, RequestOptions requestOptions = null, HttpMethod httpMethod = null, CancellationToken cancellationToken = default) - { - var clientInterface = _abstractService.Client.HttpClient; - return await clientInterface.RequestAsync(Endpoint, json, requestOptions, httpMethod, cancellationToken).ConfigureAwait(false); - } - - public async Task RequestAsync(string json, RequestOptions requestOptions = null, HttpMethod httpMethod = null, CancellationToken cancellationToken = default) - { - var clientInterface = _abstractService.Client.HttpClient; - var jsonResponse = await clientInterface.RequestAsync(Endpoint, json, requestOptions, httpMethod, cancellationToken).ConfigureAwait(false); - return JsonConvert.DeserializeObject(jsonResponse); - } - } +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Adyen.Model; +using Newtonsoft.Json; + +namespace Adyen.Service +{ + public class ServiceResource + { + private readonly AbstractService _abstractService; + protected string Endpoint; + + public ServiceResource(AbstractService abstractService, string endpoint) + { + _abstractService = abstractService; + Endpoint = endpoint; + } + + public string Request(string json, RequestOptions requestOptions = null, HttpMethod httpMethod = null) + { + var clientInterface = _abstractService.Client.HttpClient; + return clientInterface.Request(Endpoint, json, requestOptions, httpMethod); + } + + public T Request(string json, RequestOptions requestOptions = null, HttpMethod httpMethod = null) + { + var clientInterface = _abstractService.Client.HttpClient; + var jsonResponse = clientInterface.Request(Endpoint, json, requestOptions, httpMethod); + return JsonConvert.DeserializeObject(jsonResponse); + } + + public async Task RequestAsync(string json, RequestOptions requestOptions = null, HttpMethod httpMethod = null, CancellationToken cancellationToken = default) + { + var clientInterface = _abstractService.Client.HttpClient; + return await clientInterface.RequestAsync(Endpoint, json, requestOptions, httpMethod, cancellationToken).ConfigureAwait(false); + } + + public async Task RequestAsync(string json, RequestOptions requestOptions = null, HttpMethod httpMethod = null, CancellationToken cancellationToken = default) + { + var clientInterface = _abstractService.Client.HttpClient; + var jsonResponse = await clientInterface.RequestAsync(Endpoint, json, requestOptions, httpMethod, cancellationToken).ConfigureAwait(false); + return JsonConvert.DeserializeObject(jsonResponse); + } + } } \ No newline at end of file diff --git a/Adyen/Service/TerminalApiAsyncService.cs b/Adyen/TerminalApi/Services/TerminalApiAsyncService.cs similarity index 100% rename from Adyen/Service/TerminalApiAsyncService.cs rename to Adyen/TerminalApi/Services/TerminalApiAsyncService.cs diff --git a/Adyen/Service/TerminalApiLocalService.cs b/Adyen/TerminalApi/Services/TerminalApiLocalService.cs similarity index 97% rename from Adyen/Service/TerminalApiLocalService.cs rename to Adyen/TerminalApi/Services/TerminalApiLocalService.cs index b21b955a4..b1ce906db 100644 --- a/Adyen/Service/TerminalApiLocalService.cs +++ b/Adyen/TerminalApi/Services/TerminalApiLocalService.cs @@ -38,7 +38,7 @@ public interface ITerminalApiLocalService /// /// Example: /// handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true }; - /// var httpClient = new (, new (handler)); + /// var httpClient = new (, new (handler)); /// var terminalApiLocalService = new (httpClient, , , ); /// /// . @@ -52,7 +52,7 @@ public interface ITerminalApiLocalService /// /// Example: /// handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true }; - /// var httpClient = new (, new (handler)); + /// var httpClient = new (, new (handler)); /// var terminalApiLocalService = new (httpClient, , , ); /// /// . diff --git a/Adyen/Service/TerminalApiSyncService.cs b/Adyen/TerminalApi/Services/TerminalApiSyncService.cs similarity index 100% rename from Adyen/Service/TerminalApiSyncService.cs rename to Adyen/TerminalApi/Services/TerminalApiSyncService.cs diff --git a/Adyen/Service/TerminalCloudApi.cs b/Adyen/TerminalApi/Services/TerminalCloudApi.cs similarity index 100% rename from Adyen/Service/TerminalCloudApi.cs rename to Adyen/TerminalApi/Services/TerminalCloudApi.cs diff --git a/Adyen/Service/TerminalLocalApi.cs b/Adyen/TerminalApi/Services/TerminalLocalApi.cs similarity index 100% rename from Adyen/Service/TerminalLocalApi.cs rename to Adyen/TerminalApi/Services/TerminalLocalApi.cs diff --git a/Adyen/Service/TerminalLocalApiUnencrypted.cs b/Adyen/TerminalApi/Services/TerminalLocalApiUnencrypted.cs similarity index 100% rename from Adyen/Service/TerminalLocalApiUnencrypted.cs rename to Adyen/TerminalApi/Services/TerminalLocalApiUnencrypted.cs diff --git a/Adyen/Util/TerminalApi/AdditionalResponse.cs b/Adyen/TerminalApi/Utilities/AdditionalResponse.cs similarity index 100% rename from Adyen/Util/TerminalApi/AdditionalResponse.cs rename to Adyen/TerminalApi/Utilities/AdditionalResponse.cs diff --git a/Adyen/Util/TerminalApi/CardAcquisitionUtil.cs b/Adyen/TerminalApi/Utilities/CardAcquisitionUtil.cs similarity index 100% rename from Adyen/Util/TerminalApi/CardAcquisitionUtil.cs rename to Adyen/TerminalApi/Utilities/CardAcquisitionUtil.cs diff --git a/Adyen/Util/ByteArrayConverter.cs b/Adyen/Util/ByteArrayConverter.cs deleted file mode 100644 index e10849389..000000000 --- a/Adyen/Util/ByteArrayConverter.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Text; -using Newtonsoft.Json; - -namespace Adyen.Util -{ - internal class ByteArrayConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - return objectType == typeof(byte[]); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - if (reader.TokenType == JsonToken.Null) - return null; - return Encoding.UTF8.GetBytes((string)reader.Value); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - byte[] bytes = (byte[])value; - writer.WriteValue(Encoding.UTF8.GetString(bytes)); - } - - } -} \ No newline at end of file diff --git a/Adyen/Util/JsonOperation.cs b/Adyen/Util/JsonOperation.cs deleted file mode 100644 index cfee35d6f..000000000 --- a/Adyen/Util/JsonOperation.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Newtonsoft.Json; - -namespace Adyen.Util -{ - public class JsonOperation - { - /// - /// Deserialize to an object T - /// - /// - /// - /// - public static T Deserialize(string response) - { - var jsonSettings = new JsonSerializerSettings(); - jsonSettings.Converters.Add(new ByteArrayConverter()); - - return JsonConvert.DeserializeObject(response, jsonSettings); - } - - public static string SerializeRequest(object request) - { - var jsonSettings = new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore, - DefaultValueHandling = DefaultValueHandling.Include, - }; - jsonSettings.Converters.Add(new ByteArrayConverter()); - return JsonConvert.SerializeObject(request, Formatting.None, jsonSettings); - } - } -} diff --git a/Adyen/Util/Util.cs b/Adyen/Util/Util.cs deleted file mode 100644 index 710fd1a85..000000000 --- a/Adyen/Util/Util.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Adyen.Util -{ - public class Util - { - public static string CalculateSessionValidity() - { - //+1day - var dateTime=DateTime.Now.AddDays(1); - return String.Format("{0:s}", dateTime); - - } - } -} diff --git a/Adyen/Webhooks/BalancePlatformWebhookHandler.cs b/Adyen/Webhooks/BalancePlatformWebhookHandler.cs index 1e4a72b4e..03b6fcf7d 100644 --- a/Adyen/Webhooks/BalancePlatformWebhookHandler.cs +++ b/Adyen/Webhooks/BalancePlatformWebhookHandler.cs @@ -1,243 +1,243 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Runtime.Serialization; -using Adyen.Model.AcsWebhooks; -using Adyen.Model.ReportWebhooks; -using Adyen.Model.ConfigurationWebhooks; -using Adyen.Model.NegativeBalanceWarningWebhooks; -using Adyen.Model.TransactionWebhooks; -using Adyen.Model.TransferWebhooks; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace Adyen.Webhooks -{ - public class BalancePlatformWebhookHandler - { - /// - /// Deserializes to a generic banking webhook from the . Use this either to catch the - /// webhook type if unknown or if the explicit type is not required. - /// - /// The json payload of the webhook. - /// The parsed webhook packaged in a dynamic object. - /// Throws when json is invalid. - public dynamic GetGenericBalancePlatformWebhook(string jsonPayload) - { - if (GetAuthenticationNotificationRequest(jsonPayload, out AuthenticationNotificationRequest authenticationNotificationRequest)) - { - return authenticationNotificationRequest; - } - - if (GetAccountHolderNotificationRequest(jsonPayload, out AccountHolderNotificationRequest accountHolderNotificationRequest)) - { - return accountHolderNotificationRequest; - } - - if (GetBalanceAccountNotificationRequest(jsonPayload, out BalanceAccountNotificationRequest balanceAccountNotificationRequest)) - { - return balanceAccountNotificationRequest; - } - - if (GetCardOrderNotificationRequest(jsonPayload, out CardOrderNotificationRequest cardOrderNotificationRequest)) - { - return cardOrderNotificationRequest; - } - - if (GetNegativeBalanceCompensationWarningNotificationRequest(jsonPayload, out NegativeBalanceCompensationWarningNotificationRequest negativeBalanceCompensationWarningNotificationRequest)) - { - return negativeBalanceCompensationWarningNotificationRequest; - } - - if (GetPaymentNotificationRequest(jsonPayload, out PaymentNotificationRequest paymentNotificationRequest)) - { - return paymentNotificationRequest; - } - - if (GetSweepConfigurationNotificationRequest(jsonPayload, out SweepConfigurationNotificationRequest sweepConfigurationNotificationRequest)) - { - return sweepConfigurationNotificationRequest; - } - - if (GetReportNotificationRequest(jsonPayload, out ReportNotificationRequest reportNotificationRequest)) - { - return reportNotificationRequest; - } - if (GetTransferNotificationRequest(jsonPayload, out TransferNotificationRequest transferNotificationRequest)) - { - return transferNotificationRequest; - } - if (GetTransactionNotificationRequestV4(jsonPayload, out TransactionNotificationRequestV4 transactionNotificationRequestV4)) - { - return transactionNotificationRequestV4; - } - throw new JsonReaderException("Could not parse webhook"); - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetAuthenticationNotificationRequest(string jsonPayload, out AuthenticationNotificationRequest result) - { - result = null; - if (!ContainsValue(jsonPayload)) return false; - result = JsonConvert.DeserializeObject(jsonPayload); - return true; - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetAccountHolderNotificationRequest(string jsonPayload, out AccountHolderNotificationRequest result) - { - result = null; - if (!ContainsValue(jsonPayload)) return false; - result = JsonConvert.DeserializeObject(jsonPayload); - return true; - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetBalanceAccountNotificationRequest(string jsonPayload, out BalanceAccountNotificationRequest result) - { - result = null; - if (!ContainsValue(jsonPayload)) return false; - result = JsonConvert.DeserializeObject(jsonPayload); - return true; - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetCardOrderNotificationRequest(string jsonPayload, out CardOrderNotificationRequest result) - { - result = null; - if (!ContainsValue(jsonPayload)) return false; - result = JsonConvert.DeserializeObject(jsonPayload); - return true; - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetPaymentNotificationRequest(string jsonPayload, out PaymentNotificationRequest result) // This class-name is confusing as it's referring to a paymentInstrument -> Rename to PaymentInstrumentNotificationRequest? - { - result = null; - if (!ContainsValue(jsonPayload)) return false; - result = JsonConvert.DeserializeObject(jsonPayload); - return true; - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetSweepConfigurationNotificationRequest(string jsonPayload, out SweepConfigurationNotificationRequest result) - { - result = null; - if (!ContainsValue(jsonPayload)) return false; - result = JsonConvert.DeserializeObject(jsonPayload); - return true; - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook.> - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetReportNotificationRequest(string jsonPayload, out ReportNotificationRequest result) - { - result = null; - if (!ContainsValue(jsonPayload)) return false; - result = JsonConvert.DeserializeObject(jsonPayload); - return true; - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetTransferNotificationRequest(string jsonPayload, out TransferNotificationRequest result) - { - result = null; - if (!ContainsValue(jsonPayload)) return false; - result = JsonConvert.DeserializeObject(jsonPayload); - return true; - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetTransactionNotificationRequestV4(string jsonPayload, out TransactionNotificationRequestV4 result) - { - result = null; - if (!ContainsValue(jsonPayload)) return false; - result = JsonConvert.DeserializeObject(jsonPayload); - return true; - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetNegativeBalanceCompensationWarningNotificationRequest(string jsonPayload, out NegativeBalanceCompensationWarningNotificationRequest result) - { - result = null; - if (!ContainsValue(jsonPayload)) return false; - result = JsonConvert.DeserializeObject(jsonPayload); - return true; - } - - /// - /// Check if the JSON pyload contains TypeEnum value. - /// - /// The string JSON payload. - /// The TypeEnum of the class. - /// True if the enum is present. - private static bool ContainsValue(string jsonPayload) where T : struct, IConvertible - { - // Retrieve type from payload - JToken typeToken = JObject.Parse(jsonPayload).GetValue("type"); - string type = typeToken?.Value(); - List memberInfos = typeof(T).GetTypeInfo().DeclaredMembers.ToList(); - return memberInfos.Any(memberInfo => memberInfo?.GetCustomAttribute()?.Value == type); - } - } -} \ No newline at end of file +// using System; +// using System.Collections.Generic; +// using System.Linq; +// using System.Reflection; +// using System.Runtime.Serialization; +// using Adyen.Model.AcsWebhooks; +// using Adyen.Model.ReportWebhooks; +// using Adyen.Model.ConfigurationWebhooks; +// using Adyen.Model.NegativeBalanceWarningWebhooks; +// using Adyen.Model.TransactionWebhooks; +// using Adyen.Model.TransferWebhooks; +// using Newtonsoft.Json; +// using Newtonsoft.Json.Linq; +// +// namespace Adyen.Webhooks +// { +// public class BalancePlatformWebhookHandler +// { +// /// +// /// Deserializes to a generic banking webhook from the . Use this either to catch the +// /// webhook type if unknown or if the explicit type is not required. +// /// +// /// The json payload of the webhook. +// /// The parsed webhook packaged in a dynamic object. +// /// Throws when json is invalid. +// public dynamic GetGenericBalancePlatformWebhook(string jsonPayload) +// { +// if (GetAuthenticationNotificationRequest(jsonPayload, out AuthenticationNotificationRequest authenticationNotificationRequest)) +// { +// return authenticationNotificationRequest; +// } +// +// if (GetAccountHolderNotificationRequest(jsonPayload, out AccountHolderNotificationRequest accountHolderNotificationRequest)) +// { +// return accountHolderNotificationRequest; +// } +// +// if (GetBalanceAccountNotificationRequest(jsonPayload, out BalanceAccountNotificationRequest balanceAccountNotificationRequest)) +// { +// return balanceAccountNotificationRequest; +// } +// +// if (GetCardOrderNotificationRequest(jsonPayload, out CardOrderNotificationRequest cardOrderNotificationRequest)) +// { +// return cardOrderNotificationRequest; +// } +// +// if (GetNegativeBalanceCompensationWarningNotificationRequest(jsonPayload, out NegativeBalanceCompensationWarningNotificationRequest negativeBalanceCompensationWarningNotificationRequest)) +// { +// return negativeBalanceCompensationWarningNotificationRequest; +// } +// +// if (GetPaymentNotificationRequest(jsonPayload, out PaymentNotificationRequest paymentNotificationRequest)) +// { +// return paymentNotificationRequest; +// } +// +// if (GetSweepConfigurationNotificationRequest(jsonPayload, out SweepConfigurationNotificationRequest sweepConfigurationNotificationRequest)) +// { +// return sweepConfigurationNotificationRequest; +// } +// +// if (GetReportNotificationRequest(jsonPayload, out ReportNotificationRequest reportNotificationRequest)) +// { +// return reportNotificationRequest; +// } +// if (GetTransferNotificationRequest(jsonPayload, out TransferNotificationRequest transferNotificationRequest)) +// { +// return transferNotificationRequest; +// } +// if (GetTransactionNotificationRequestV4(jsonPayload, out TransactionNotificationRequestV4 transactionNotificationRequestV4)) +// { +// return transactionNotificationRequestV4; +// } +// throw new JsonReaderException("Could not parse webhook"); +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetAuthenticationNotificationRequest(string jsonPayload, out AuthenticationNotificationRequest result) +// { +// result = null; +// if (!ContainsValue(jsonPayload)) return false; +// result = JsonConvert.DeserializeObject(jsonPayload); +// return true; +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetAccountHolderNotificationRequest(string jsonPayload, out AccountHolderNotificationRequest result) +// { +// result = null; +// if (!ContainsValue(jsonPayload)) return false; +// result = JsonConvert.DeserializeObject(jsonPayload); +// return true; +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetBalanceAccountNotificationRequest(string jsonPayload, out BalanceAccountNotificationRequest result) +// { +// result = null; +// if (!ContainsValue(jsonPayload)) return false; +// result = JsonConvert.DeserializeObject(jsonPayload); +// return true; +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetCardOrderNotificationRequest(string jsonPayload, out CardOrderNotificationRequest result) +// { +// result = null; +// if (!ContainsValue(jsonPayload)) return false; +// result = JsonConvert.DeserializeObject(jsonPayload); +// return true; +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetPaymentNotificationRequest(string jsonPayload, out PaymentNotificationRequest result) // This class-name is confusing as it's referring to a paymentInstrument -> Rename to PaymentInstrumentNotificationRequest? +// { +// result = null; +// if (!ContainsValue(jsonPayload)) return false; +// result = JsonConvert.DeserializeObject(jsonPayload); +// return true; +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetSweepConfigurationNotificationRequest(string jsonPayload, out SweepConfigurationNotificationRequest result) +// { +// result = null; +// if (!ContainsValue(jsonPayload)) return false; +// result = JsonConvert.DeserializeObject(jsonPayload); +// return true; +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook.> +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetReportNotificationRequest(string jsonPayload, out ReportNotificationRequest result) +// { +// result = null; +// if (!ContainsValue(jsonPayload)) return false; +// result = JsonConvert.DeserializeObject(jsonPayload); +// return true; +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetTransferNotificationRequest(string jsonPayload, out TransferNotificationRequest result) +// { +// result = null; +// if (!ContainsValue(jsonPayload)) return false; +// result = JsonConvert.DeserializeObject(jsonPayload); +// return true; +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetTransactionNotificationRequestV4(string jsonPayload, out TransactionNotificationRequestV4 result) +// { +// result = null; +// if (!ContainsValue(jsonPayload)) return false; +// result = JsonConvert.DeserializeObject(jsonPayload); +// return true; +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetNegativeBalanceCompensationWarningNotificationRequest(string jsonPayload, out NegativeBalanceCompensationWarningNotificationRequest result) +// { +// result = null; +// if (!ContainsValue(jsonPayload)) return false; +// result = JsonConvert.DeserializeObject(jsonPayload); +// return true; +// } +// +// /// +// /// Check if the JSON pyload contains TypeEnum value. +// /// +// /// The string JSON payload. +// /// The TypeEnum of the class. +// /// True if the enum is present. +// private static bool ContainsValue(string jsonPayload) where T : struct, IConvertible +// { +// // Retrieve type from payload +// JToken typeToken = JObject.Parse(jsonPayload).GetValue("type"); +// string type = typeToken?.Value(); +// List memberInfos = typeof(T).GetTypeInfo().DeclaredMembers.ToList(); +// return memberInfos.Any(memberInfo => memberInfo?.GetCustomAttribute()?.Value == type); +// } +// } +// } \ No newline at end of file diff --git a/Adyen/Webhooks/ClassicPlatformWebhookHandler.cs b/Adyen/Webhooks/ClassicPlatformWebhookHandler.cs index f488adab7..71e108871 100644 --- a/Adyen/Webhooks/ClassicPlatformWebhookHandler.cs +++ b/Adyen/Webhooks/ClassicPlatformWebhookHandler.cs @@ -1,386 +1,386 @@ -using Adyen.Model.PlatformsWebhooks; -using Newtonsoft.Json; - -namespace Adyen.Webhooks -{ - public class ClassicPlatformWebhookHandler - { - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetAccountCreateNotification(string jsonPayload, out AccountCreateNotification result) - { - result = null; - try - { - result = JsonConvert.DeserializeObject(jsonPayload); - return "ACCOUNT_CREATED".Equals(result.EventType); - } - catch (JsonSerializationException) - { - return false; - } - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetAccountCloseNotification(string jsonPayload, out AccountCloseNotification result) - { - result = null; - try - { - result = JsonConvert.DeserializeObject(jsonPayload); - return "ACCOUNT_CLOSED".Equals(result.EventType); - } - catch (JsonSerializationException) - { - return false; - } - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetAccountFundsBelowThresholdNotification(string jsonPayload, out AccountFundsBelowThresholdNotification result) - { - result = null; - try - { - result = JsonConvert.DeserializeObject(jsonPayload); - return "ACCOUNT_FUNDS_BELOW_THRESHOLD".Equals(result.EventType); - } - catch (JsonSerializationException) - { - return false; - } - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetAccountHolderCreateNotification(string jsonPayload, out AccountHolderCreateNotification result) - { - result = null; - try - { - result = JsonConvert.DeserializeObject(jsonPayload); - return "ACCOUNT_HOLDER_CREATED".Equals(result.EventType); - } - catch (JsonSerializationException) - { - return false; - } - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetAccountHolderPayoutNotification(string jsonPayload, out AccountHolderPayoutNotification result) - { - result = null; - try - { - result = JsonConvert.DeserializeObject(jsonPayload); - return "ACCOUNT_HOLDER_PAYOUT".Equals(result.EventType); - } - catch (JsonSerializationException) - { - return false; - } - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetAccountHolderStatusChangeNotification(string jsonPayload, out AccountHolderStatusChangeNotification result) - { - result = null; - try - { - result = JsonConvert.DeserializeObject(jsonPayload); - return "ACCOUNT_HOLDER_STATUS_CHANGE".Equals(result.EventType); - } - catch (JsonSerializationException) - { - return false; - } - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetAccountHolderUpcomingDeadlineNotification(string jsonPayload, out AccountHolderUpcomingDeadlineNotification result) - { - result = null; - try - { - result = JsonConvert.DeserializeObject(jsonPayload); - return "ACCOUNT_HOLDER_UPCOMING_DEADLINE".Equals(result.EventType); - } - catch (JsonSerializationException) - { - return false; - } - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetAccountHolderUpdateNotification(string jsonPayload, out AccountHolderUpdateNotification result) - { - result = null; - try - { - result = JsonConvert.DeserializeObject(jsonPayload); - return "ACCOUNT_HOLDER_UPDATED".Equals(result.EventType); - } - catch (JsonSerializationException) - { - return false; - } - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetAccountHolderVerificationNotification(string jsonPayload, out AccountHolderVerificationNotification result) - { - result = null; - try - { - result = JsonConvert.DeserializeObject(jsonPayload); - return "ACCOUNT_HOLDER_VERIFICATION".Equals(result.EventType); - } - catch (JsonSerializationException) - { - return false; - } - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetAccountUpdateNotification(string jsonPayload, out AccountUpdateNotification result) - { - result = null; - try - { - result = JsonConvert.DeserializeObject(jsonPayload); - return "ACCOUNT_UPDATED".Equals(result.EventType); - } - catch (JsonSerializationException) - { - return false; - } - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetBeneficiarySetupNotification(string jsonPayload, out BeneficiarySetupNotification result) - { - result = null; - try - { - result = JsonConvert.DeserializeObject(jsonPayload); - return "BENEFICIARY_SETUP".Equals(result.EventType); - } - catch (JsonSerializationException) - { - return false; - } - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetCompensateNegativeBalanceNotification(string jsonPayload, out CompensateNegativeBalanceNotification result) - { - result = null; - try - { - result = JsonConvert.DeserializeObject(jsonPayload); - return "COMPENSATE_NEGATIVE_BALANCE".Equals(result.EventType); - } - catch (JsonSerializationException) - { - return false; - } - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetDirectDebitInitiatedNotification(string jsonPayload, out DirectDebitInitiatedNotification result) - { - result = null; - try - { - result = JsonConvert.DeserializeObject(jsonPayload); - return "DIRECT_DEBIT_INITIATED".Equals(result.EventType); - } - catch (JsonSerializationException) - { - return false; - } - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetPaymentFailureNotification(string jsonPayload, out PaymentFailureNotification result) - { - result = null; - try - { - result = JsonConvert.DeserializeObject(jsonPayload); - return "PAYMENT_FAILURE".Equals(result.EventType); - } - catch (JsonSerializationException) - { - return false; - } - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetRefundFundsTransferNotification(string jsonPayload, out RefundFundsTransferNotification result) - { - result = null; - try - { - result = JsonConvert.DeserializeObject(jsonPayload); - return "REFUND_FUNDS_TRANSFER".Equals(result.EventType); - } - catch (JsonSerializationException) - { - return false; - } - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetReportAvailableNotification(string jsonPayload, out ReportAvailableNotification result) - { - result = null; - try - { - result = JsonConvert.DeserializeObject(jsonPayload); - return "REPORT_AVAILABLE".Equals(result.EventType); - } - catch (JsonSerializationException) - { - return false; - } - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetScheduledRefundsNotification(string jsonPayload, out ScheduledRefundsNotification result) - { - result = null; - try - { - result = JsonConvert.DeserializeObject(jsonPayload); - return "SCHEDULED_REFUNDS".Equals(result.EventType); - } - catch (JsonSerializationException) - { - return false; - } - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetTransferFundsNotification(string jsonPayload, out TransferFundsNotification result) - { - result = null; - try - { - result = JsonConvert.DeserializeObject(jsonPayload); - return "TRANSFER_FUNDS".Equals(result.EventType); - } - catch (JsonSerializationException) - { - return false; - } - } - } -} \ No newline at end of file +// using Adyen.Model.PlatformsWebhooks; +// using Newtonsoft.Json; +// +// namespace Adyen.Webhooks +// { +// public class ClassicPlatformWebhookHandler +// { +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetAccountCreateNotification(string jsonPayload, out AccountCreateNotification result) +// { +// result = null; +// try +// { +// result = JsonConvert.DeserializeObject(jsonPayload); +// return "ACCOUNT_CREATED".Equals(result.EventType); +// } +// catch (JsonSerializationException) +// { +// return false; +// } +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetAccountCloseNotification(string jsonPayload, out AccountCloseNotification result) +// { +// result = null; +// try +// { +// result = JsonConvert.DeserializeObject(jsonPayload); +// return "ACCOUNT_CLOSED".Equals(result.EventType); +// } +// catch (JsonSerializationException) +// { +// return false; +// } +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetAccountFundsBelowThresholdNotification(string jsonPayload, out AccountFundsBelowThresholdNotification result) +// { +// result = null; +// try +// { +// result = JsonConvert.DeserializeObject(jsonPayload); +// return "ACCOUNT_FUNDS_BELOW_THRESHOLD".Equals(result.EventType); +// } +// catch (JsonSerializationException) +// { +// return false; +// } +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetAccountHolderCreateNotification(string jsonPayload, out AccountHolderCreateNotification result) +// { +// result = null; +// try +// { +// result = JsonConvert.DeserializeObject(jsonPayload); +// return "ACCOUNT_HOLDER_CREATED".Equals(result.EventType); +// } +// catch (JsonSerializationException) +// { +// return false; +// } +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetAccountHolderPayoutNotification(string jsonPayload, out AccountHolderPayoutNotification result) +// { +// result = null; +// try +// { +// result = JsonConvert.DeserializeObject(jsonPayload); +// return "ACCOUNT_HOLDER_PAYOUT".Equals(result.EventType); +// } +// catch (JsonSerializationException) +// { +// return false; +// } +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetAccountHolderStatusChangeNotification(string jsonPayload, out AccountHolderStatusChangeNotification result) +// { +// result = null; +// try +// { +// result = JsonConvert.DeserializeObject(jsonPayload); +// return "ACCOUNT_HOLDER_STATUS_CHANGE".Equals(result.EventType); +// } +// catch (JsonSerializationException) +// { +// return false; +// } +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetAccountHolderUpcomingDeadlineNotification(string jsonPayload, out AccountHolderUpcomingDeadlineNotification result) +// { +// result = null; +// try +// { +// result = JsonConvert.DeserializeObject(jsonPayload); +// return "ACCOUNT_HOLDER_UPCOMING_DEADLINE".Equals(result.EventType); +// } +// catch (JsonSerializationException) +// { +// return false; +// } +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetAccountHolderUpdateNotification(string jsonPayload, out AccountHolderUpdateNotification result) +// { +// result = null; +// try +// { +// result = JsonConvert.DeserializeObject(jsonPayload); +// return "ACCOUNT_HOLDER_UPDATED".Equals(result.EventType); +// } +// catch (JsonSerializationException) +// { +// return false; +// } +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetAccountHolderVerificationNotification(string jsonPayload, out AccountHolderVerificationNotification result) +// { +// result = null; +// try +// { +// result = JsonConvert.DeserializeObject(jsonPayload); +// return "ACCOUNT_HOLDER_VERIFICATION".Equals(result.EventType); +// } +// catch (JsonSerializationException) +// { +// return false; +// } +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetAccountUpdateNotification(string jsonPayload, out AccountUpdateNotification result) +// { +// result = null; +// try +// { +// result = JsonConvert.DeserializeObject(jsonPayload); +// return "ACCOUNT_UPDATED".Equals(result.EventType); +// } +// catch (JsonSerializationException) +// { +// return false; +// } +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetBeneficiarySetupNotification(string jsonPayload, out BeneficiarySetupNotification result) +// { +// result = null; +// try +// { +// result = JsonConvert.DeserializeObject(jsonPayload); +// return "BENEFICIARY_SETUP".Equals(result.EventType); +// } +// catch (JsonSerializationException) +// { +// return false; +// } +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetCompensateNegativeBalanceNotification(string jsonPayload, out CompensateNegativeBalanceNotification result) +// { +// result = null; +// try +// { +// result = JsonConvert.DeserializeObject(jsonPayload); +// return "COMPENSATE_NEGATIVE_BALANCE".Equals(result.EventType); +// } +// catch (JsonSerializationException) +// { +// return false; +// } +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetDirectDebitInitiatedNotification(string jsonPayload, out DirectDebitInitiatedNotification result) +// { +// result = null; +// try +// { +// result = JsonConvert.DeserializeObject(jsonPayload); +// return "DIRECT_DEBIT_INITIATED".Equals(result.EventType); +// } +// catch (JsonSerializationException) +// { +// return false; +// } +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetPaymentFailureNotification(string jsonPayload, out PaymentFailureNotification result) +// { +// result = null; +// try +// { +// result = JsonConvert.DeserializeObject(jsonPayload); +// return "PAYMENT_FAILURE".Equals(result.EventType); +// } +// catch (JsonSerializationException) +// { +// return false; +// } +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetRefundFundsTransferNotification(string jsonPayload, out RefundFundsTransferNotification result) +// { +// result = null; +// try +// { +// result = JsonConvert.DeserializeObject(jsonPayload); +// return "REFUND_FUNDS_TRANSFER".Equals(result.EventType); +// } +// catch (JsonSerializationException) +// { +// return false; +// } +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetReportAvailableNotification(string jsonPayload, out ReportAvailableNotification result) +// { +// result = null; +// try +// { +// result = JsonConvert.DeserializeObject(jsonPayload); +// return "REPORT_AVAILABLE".Equals(result.EventType); +// } +// catch (JsonSerializationException) +// { +// return false; +// } +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetScheduledRefundsNotification(string jsonPayload, out ScheduledRefundsNotification result) +// { +// result = null; +// try +// { +// result = JsonConvert.DeserializeObject(jsonPayload); +// return "SCHEDULED_REFUNDS".Equals(result.EventType); +// } +// catch (JsonSerializationException) +// { +// return false; +// } +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetTransferFundsNotification(string jsonPayload, out TransferFundsNotification result) +// { +// result = null; +// try +// { +// result = JsonConvert.DeserializeObject(jsonPayload); +// return "TRANSFER_FUNDS".Equals(result.EventType); +// } +// catch (JsonSerializationException) +// { +// return false; +// } +// } +// } +// } \ No newline at end of file diff --git a/Adyen/Util/HMACValidator.cs b/Adyen/Webhooks/HmacValidatorUtility.cs similarity index 64% rename from Adyen/Util/HMACValidator.cs rename to Adyen/Webhooks/HmacValidatorUtility.cs index b3da7fc34..515356490 100644 --- a/Adyen/Util/HMACValidator.cs +++ b/Adyen/Webhooks/HmacValidatorUtility.cs @@ -1,123 +1,146 @@ -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Text; -using Adyen.Model.Notification; - -namespace Adyen.Util -{ - public class HmacValidator - { - private const string HmacSignature = "hmacSignature"; - - // Computes the Base64 encoded signature using the HMAC algorithm with the HMACSHA256 hashing function. - public string CalculateHmac(string payload, string hmacKey) - { - byte[] key = PackH(hmacKey); - byte[] data = Encoding.UTF8.GetBytes(payload); - - try - { - using (HMACSHA256 hmac = new HMACSHA256(key)) - { - // Compute the hmac on input data bytes - byte[] rawHmac = hmac.ComputeHash(data); - - // Base64-encode the hmac - return Convert.ToBase64String(rawHmac); - } - } - catch (Exception e) - { - throw new Exception("Failed to generate HMAC : " + e.Message); - } - } - - public string CalculateHmac(NotificationRequestItem notificationRequestItem, string hmacKey) - { - var notificationRequestItemData = GetDataToSign(notificationRequestItem); - return CalculateHmac(notificationRequestItemData, hmacKey); - } - - private byte[] PackH(string hex) - { - if ((hex.Length % 2) == 1) - { - hex += '0'; - } - - byte[] bytes = new byte[hex.Length / 2]; - for (int i = 0; i < hex.Length; i += 2) - { - bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); - } - - return bytes; - } - - public string GetDataToSign(NotificationRequestItem notificationRequestItem) - { - var amount = notificationRequestItem.Amount; - var signedDataList = new List - { - notificationRequestItem.PspReference, - notificationRequestItem.OriginalReference, - notificationRequestItem.MerchantAccountCode, - notificationRequestItem.MerchantReference, - Convert.ToString(amount.Value), - amount.Currency, - notificationRequestItem.EventCode, - notificationRequestItem.Success.ToString().ToLower() - }; - return String.Join(":", signedDataList); - } - - /// - /// Validates a regular webhook with the given . - /// - /// . - /// The HMAC key, retrieved from the Adyen Customer Area. - /// A return value indicates the HMAC validation succeeded. - public bool IsValidHmac(NotificationRequestItem notificationRequestItem, string hmacKey) - { - if (notificationRequestItem.AdditionalData == null) - { - return false; - } - - if (!notificationRequestItem.AdditionalData.ContainsKey(HmacSignature)) - { - return false; - } - var expectedSign = CalculateHmac(notificationRequestItem, hmacKey); - var merchantSign = notificationRequestItem.AdditionalData[HmacSignature]; - return string.Equals(expectedSign, merchantSign); - } - - - /// - /// Validates a balance platform and management webhook payload with the given and . - /// - /// The HMAC signature, retrieved from the request header. - /// The HMAC key, retrieved from the Adyen Customer Area. - /// The webhook payload. - /// A return value indicates the HMAC validation succeeded. - public bool IsValidWebhook(string hmacSignature, string hmacKey, string payload) - { - var calculatedSign = CalculateHmac(payload, hmacKey); - return TimeSafeEquals(Encoding.UTF8.GetBytes(hmacSignature), Encoding.UTF8.GetBytes(calculatedSign)); - } - - /// - /// This method compares two bytestrings in constant time based on length of shortest bytestring to prevent timing attacks. - /// - /// True if different. - private static bool TimeSafeEquals(byte[] a, byte[] b) - { - uint diff = (uint)a.Length ^ (uint)b.Length; - for (int i = 0; i < a.Length && i < b.Length; i++) { diff |= (uint)(a[i] ^ b[i]); } - return diff == 0; - } - } -} - +using System.Security.Cryptography; +using System.Text; +using Adyen.Webhooks.Models; + +namespace Adyen.Webhooks +{ + /// + /// Utility class to help verify hmac signatures from incoming webhooks. + /// + public class HmacValidatorUtility + { + private const string HmacSignature = "hmacSignature"; + + /// + /// Computes the Base64 encoded signature using the HMAC algorithm with the HMAC-SHA256 hashing function. + /// + /// The JSON payload. + /// The secret ADYEN_HMAC_KEY, retrieved from the Adyen Customer Area. + /// The HMAC string for the payload. + /// + public string CalculateHmac(string payload, string hmacKey) + { + byte[] key = HexadecimalToBytes(hmacKey); + byte[] data = Encoding.UTF8.GetBytes(payload); + + try + { + using (HMACSHA256 hmac = new HMACSHA256(key)) + { + // Compute the hmac on input data bytes + byte[] rawHmac = hmac.ComputeHash(data); + + // Base64-encode the hmac + return Convert.ToBase64String(rawHmac); + } + } + catch (Exception e) + { + throw new Exception("Failed to generate HMAC : " + e.Message); + } + } + + /// + /// Calculates HMAC for . + /// + /// . + /// The secret ADYEN_HMAC_KEY, retrieved from the Adyen Customer Area. + /// The HMAC string. + public string CalculateHmac(NotificationRequestItem notificationRequestItem, string hmacKey) + { + var notificationRequestItemData = GetDataToSign(notificationRequestItem); + return CalculateHmac(notificationRequestItemData, hmacKey); + } + + /// + /// Converts a hexadecimal into a byte array. + /// + /// The hexadecimal string. + /// An array of bytes that repesents the hexadecimalString. + private byte[] HexadecimalToBytes(string hexadecimalString) + { + if ((hexadecimalString.Length % 2) == 1) + { + hexadecimalString += '0'; + } + + byte[] bytes = new byte[hexadecimalString.Length / 2]; + for (int i = 0; i < hexadecimalString.Length; i += 2) + { + bytes[i / 2] = Convert.ToByte(hexadecimalString.Substring(i, 2), 16); + } + + return bytes; + } + + /// + /// Gets the data to sign. + /// + /// . + /// String joined by a colon. + public string GetDataToSign(NotificationRequestItem notificationRequestItem) + { + var amount = notificationRequestItem.Amount; + var signedDataList = new List + { + notificationRequestItem.PspReference, + notificationRequestItem.OriginalReference, + notificationRequestItem.MerchantAccountCode, + notificationRequestItem.MerchantReference, + Convert.ToString(amount.Value), + amount.Currency, + notificationRequestItem.EventCode, + notificationRequestItem.Success.ToString().ToLower() + }; + return String.Join(":", signedDataList); + } + + /// + /// Validates a regular webhook with the given . + /// + /// . + /// The secret ADYEN_HMAC_KEY, retrieved from the Adyen Customer Area. + /// Returns true indicates that the HMAC validation succeeded. + public bool IsValidHmac(NotificationRequestItem notificationRequestItem, string hmacKey) + { + if (notificationRequestItem.AdditionalData == null) + { + return false; + } + + if (!notificationRequestItem.AdditionalData.ContainsKey(HmacSignature)) + { + return false; + } + var expectedSign = CalculateHmac(notificationRequestItem, hmacKey); + var merchantSign = notificationRequestItem.AdditionalData[HmacSignature]; + return string.Equals(expectedSign, merchantSign); + } + + + /// + /// Validates a balance platform and management webhook payload with the given and . + /// + /// The HMAC signature, retrieved from the request header. + /// The HMAC key, retrieved from the Adyen Customer Area. + /// The webhook payload. + /// A return value indicates the HMAC validation succeeded. + public bool IsValidWebhook(string hmacSignature, string hmacKey, string payload) + { + var calculatedSign = CalculateHmac(payload, hmacKey); + return TimeSafeEquals(Encoding.UTF8.GetBytes(hmacSignature), Encoding.UTF8.GetBytes(calculatedSign)); + } + + /// + /// This method compares two bytestrings in constant time based on length of shortest bytestring to prevent timing attacks. + /// + /// True if different. + private static bool TimeSafeEquals(byte[] a, byte[] b) + { + uint diff = (uint)a.Length ^ (uint)b.Length; + for (int i = 0; i < a.Length && i < b.Length; i++) { diff |= (uint)(a[i] ^ b[i]); } + return diff == 0; + } + } +} + diff --git a/Adyen/Webhooks/ManagementWebhookHandler.cs b/Adyen/Webhooks/ManagementWebhookHandler.cs index fbc8b1ee8..b362905c0 100644 --- a/Adyen/Webhooks/ManagementWebhookHandler.cs +++ b/Adyen/Webhooks/ManagementWebhookHandler.cs @@ -1,147 +1,147 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Runtime.Serialization; -using Adyen.Model.ManagementWebhooks; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace Adyen.Webhooks -{ - public class ManagementWebhookHandler - { - /// - /// Deserializes to a generic management webhook from the . Use this either to catch the - /// webhook type if unknown or if the explicit type is not required. - /// - /// The json payload of the webhook. - /// The parsed webhook packaged in a dynamic object. - /// Throws when json is invalid. - public dynamic GetGenericManagementWebhook(string jsonPayload) - { - if (GetMerchantCreatedNotificationRequest(jsonPayload, out var merchantCreatedNotificationRequest)) - { - return merchantCreatedNotificationRequest; - } - - if (GetMerchantUpdatedNotificationRequest(jsonPayload, out var merchantUpdatedNotificationRequest)) - { - return merchantUpdatedNotificationRequest; - } - - if (GetPaymentMethodCreatedNotificationRequest(jsonPayload, out var paymentMethodCreatedNotificationRequest)) - { - return paymentMethodCreatedNotificationRequest; - } - - if (GetPaymentMethodRequestRemovedNotificationRequest(jsonPayload, out var PaymentMethodRequestRemovedNotificationRequest)) - { - return PaymentMethodRequestRemovedNotificationRequest; - } - - if (GetPaymentMethodScheduledForRemovalNotificationRequest(jsonPayload, out var PaymentMethodScheduledForRemovalNotificationRequest)) - { - return PaymentMethodScheduledForRemovalNotificationRequest; - } - - throw new JsonReaderException("Could not parse webhook"); - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetMerchantCreatedNotificationRequest(string jsonPayload, out MerchantCreatedNotificationRequest result) - { - result = null; - if (!ContainsValue(jsonPayload)) return false; - result = JsonConvert.DeserializeObject(jsonPayload); - return true; - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetMerchantUpdatedNotificationRequest(string jsonPayload, out MerchantUpdatedNotificationRequest result) - { - result = null; - if (!ContainsValue(jsonPayload)) return false; - result = JsonConvert.DeserializeObject(jsonPayload); - return true; - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetPaymentMethodCreatedNotificationRequest(string jsonPayload, out PaymentMethodCreatedNotificationRequest result) - { - result = null; - if (!ContainsValue(jsonPayload)) return false; - result = JsonConvert.DeserializeObject(jsonPayload); - return true; - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetPaymentMethodRequestRemovedNotificationRequest(string jsonPayload, out PaymentMethodRequestRemovedNotificationRequest result) - { - result = null; - if (!ContainsValue(jsonPayload)) return false; - result = JsonConvert.DeserializeObject(jsonPayload); - return true; - } - - /// - /// Deserializes from the . - /// - /// The json payload of the webhook. - /// . - /// A return value indicates whether the deserialization succeeded. - /// Throws when json is invalid. - public bool GetPaymentMethodScheduledForRemovalNotificationRequest(string jsonPayload, out PaymentMethodScheduledForRemovalNotificationRequest result) - { - result = null; - if (!ContainsValue(jsonPayload)) return false; - result = JsonConvert.DeserializeObject(jsonPayload); - return true; - } - - // Check if the contains TypeEnum value - private static bool ContainsValue(string jsonPayload) where T : struct, IConvertible - { - // Retrieve type from payload - JToken typeToken = JObject.Parse(jsonPayload).GetValue("type"); - string type = typeToken?.Value(); - - // Search for type in .TypeEnum - List list = new List(); - var members = typeof(T) - .GetTypeInfo() - .DeclaredMembers; - foreach (var member in members) - { - var val = member?.GetCustomAttribute(false)?.Value; - if (!string.IsNullOrEmpty(val)) - list.Add(val); - } - - return list.Contains(type); - } - } -} \ No newline at end of file +// using System; +// using System.Collections.Generic; +// using System.Reflection; +// using System.Runtime.Serialization; +// using Adyen.Model.ManagementWebhooks; +// using Newtonsoft.Json; +// using Newtonsoft.Json.Linq; +// +// namespace Adyen.Webhooks +// { +// public class ManagementWebhookHandler +// { +// /// +// /// Deserializes to a generic management webhook from the . Use this either to catch the +// /// webhook type if unknown or if the explicit type is not required. +// /// +// /// The json payload of the webhook. +// /// The parsed webhook packaged in a dynamic object. +// /// Throws when json is invalid. +// public dynamic GetGenericManagementWebhook(string jsonPayload) +// { +// if (GetMerchantCreatedNotificationRequest(jsonPayload, out var merchantCreatedNotificationRequest)) +// { +// return merchantCreatedNotificationRequest; +// } +// +// if (GetMerchantUpdatedNotificationRequest(jsonPayload, out var merchantUpdatedNotificationRequest)) +// { +// return merchantUpdatedNotificationRequest; +// } +// +// if (GetPaymentMethodCreatedNotificationRequest(jsonPayload, out var paymentMethodCreatedNotificationRequest)) +// { +// return paymentMethodCreatedNotificationRequest; +// } +// +// if (GetPaymentMethodRequestRemovedNotificationRequest(jsonPayload, out var PaymentMethodRequestRemovedNotificationRequest)) +// { +// return PaymentMethodRequestRemovedNotificationRequest; +// } +// +// if (GetPaymentMethodScheduledForRemovalNotificationRequest(jsonPayload, out var PaymentMethodScheduledForRemovalNotificationRequest)) +// { +// return PaymentMethodScheduledForRemovalNotificationRequest; +// } +// +// throw new JsonReaderException("Could not parse webhook"); +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetMerchantCreatedNotificationRequest(string jsonPayload, out MerchantCreatedNotificationRequest result) +// { +// result = null; +// if (!ContainsValue(jsonPayload)) return false; +// result = JsonConvert.DeserializeObject(jsonPayload); +// return true; +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetMerchantUpdatedNotificationRequest(string jsonPayload, out MerchantUpdatedNotificationRequest result) +// { +// result = null; +// if (!ContainsValue(jsonPayload)) return false; +// result = JsonConvert.DeserializeObject(jsonPayload); +// return true; +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetPaymentMethodCreatedNotificationRequest(string jsonPayload, out PaymentMethodCreatedNotificationRequest result) +// { +// result = null; +// if (!ContainsValue(jsonPayload)) return false; +// result = JsonConvert.DeserializeObject(jsonPayload); +// return true; +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetPaymentMethodRequestRemovedNotificationRequest(string jsonPayload, out PaymentMethodRequestRemovedNotificationRequest result) +// { +// result = null; +// if (!ContainsValue(jsonPayload)) return false; +// result = JsonConvert.DeserializeObject(jsonPayload); +// return true; +// } +// +// /// +// /// Deserializes from the . +// /// +// /// The json payload of the webhook. +// /// . +// /// A return value indicates whether the deserialization succeeded. +// /// Throws when json is invalid. +// public bool GetPaymentMethodScheduledForRemovalNotificationRequest(string jsonPayload, out PaymentMethodScheduledForRemovalNotificationRequest result) +// { +// result = null; +// if (!ContainsValue(jsonPayload)) return false; +// result = JsonConvert.DeserializeObject(jsonPayload); +// return true; +// } +// +// // Check if the contains TypeEnum value +// private static bool ContainsValue(string jsonPayload) where T : struct, IConvertible +// { +// // Retrieve type from payload +// JToken typeToken = JObject.Parse(jsonPayload).GetValue("type"); +// string type = typeToken?.Value(); +// +// // Search for type in .TypeEnum +// List list = new List(); +// var members = typeof(T) +// .GetTypeInfo() +// .DeclaredMembers; +// foreach (var member in members) +// { +// var val = member?.GetCustomAttribute(false)?.Value; +// if (!string.IsNullOrEmpty(val)) +// list.Add(val); +// } +// +// return list.Contains(type); +// } +// } +// } \ No newline at end of file diff --git a/Adyen/Webhooks/Models/Amount.cs b/Adyen/Webhooks/Models/Amount.cs new file mode 100644 index 000000000..05fcc574a --- /dev/null +++ b/Adyen/Webhooks/Models/Amount.cs @@ -0,0 +1,29 @@ +namespace Adyen.Webhooks.Models +{ + /// + /// The Amount, used for webhooks + /// + public class Amount + { + /// + /// The three-character [ISO currency code](https://docs.adyen.com/development-resources/currency-codes#currency-codes). + /// + public string Currency { get; set; } + + /// + /// The amount of the transaction, in [minor units](https://docs.adyen.com/development-resources/currency-codes#minor-units). + /// + public long? Value { get; set; } + + /// + /// Initializes a new instance of the class for webhooks. + /// + /// The three-character [ISO currency code](https://docs.adyen.com/development-resources/currency-codes#currency-codes). + /// The amount of the transaction, in [minor units](https://docs.adyen.com/development-resources/currency-codes#minor-units). + public Amount(string currency = default(string), long? value = default(long?)) + { + this.Currency = currency; + this.Value = value; + } + } +} \ No newline at end of file diff --git a/Adyen/Model/Notification/NotificationRequest.cs b/Adyen/Webhooks/Models/NotificationRequest.cs similarity index 84% rename from Adyen/Model/Notification/NotificationRequest.cs rename to Adyen/Webhooks/Models/NotificationRequest.cs index 038da3cba..64b6c0b32 100644 --- a/Adyen/Model/Notification/NotificationRequest.cs +++ b/Adyen/Webhooks/Models/NotificationRequest.cs @@ -1,61 +1,61 @@ -#region License -// /* -// * ###### -// * ###### -// * ############ ####( ###### #####. ###### ############ ############ -// * ############# #####( ###### #####. ###### ############# ############# -// * ###### #####( ###### #####. ###### ##### ###### ##### ###### -// * ###### ###### #####( ###### #####. ###### ##### ##### ##### ###### -// * ###### ###### #####( ###### #####. ###### ##### ##### ###### -// * ############# ############# ############# ############# ##### ###### -// * ############ ############ ############# ############ ##### ###### -// * ###### -// * ############# -// * ############ -// * -// * Adyen Dotnet API Library -// * -// * Copyright (c) 2020 Adyen B.V. -// * This file is open source and available under the MIT license. -// * See the LICENSE file for more info. -// */ -#endregion - -using Newtonsoft.Json; -using System.Collections.Generic; -using System.Text; - -namespace Adyen.Model.Notification -{ - public class NotificationRequest - { - public string Live { get; set; } - - [JsonProperty("NotificationItems")] - public List NotificationItemContainers { get; set; } - - /// - /// Returns the string presentation of the object - /// - /// String presentation of the object - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append("class NotificationRequest {\n"); - sb.Append(" Live: ").Append(this.Live).Append("\n"); - sb.Append(" NotificationItemContainers: ").Append(NotificationItemContainers).Append("\n"); - sb.Append("}\n"); - return sb.ToString(); - } - - - /// - /// Returns the JSON string presentation of the object - /// - /// JSON string presentation of the object - public string ToJson() - { - return JsonConvert.SerializeObject(this, Formatting.Indented); - } - } -} +#region License +// /* +// * ###### +// * ###### +// * ############ ####( ###### #####. ###### ############ ############ +// * ############# #####( ###### #####. ###### ############# ############# +// * ###### #####( ###### #####. ###### ##### ###### ##### ###### +// * ###### ###### #####( ###### #####. ###### ##### ##### ##### ###### +// * ###### ###### #####( ###### #####. ###### ##### ##### ###### +// * ############# ############# ############# ############# ##### ###### +// * ############ ############ ############# ############ ##### ###### +// * ###### +// * ############# +// * ############ +// * +// * Adyen Dotnet API Library +// * +// * Copyright (c) 2020 Adyen B.V. +// * This file is open source and available under the MIT license. +// * See the LICENSE file for more info. +// */ +#endregion + +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text; + +namespace Adyen.Webhooks.Models +{ + public class NotificationRequest + { + public string Live { get; set; } + + [JsonProperty("NotificationItems")] + public List NotificationItemContainers { get; set; } + + /// + /// Returns the string presentation of the object. + /// + /// String presentation of the object. + public override string ToString() + { + var sb = new StringBuilder(); + sb.Append("class NotificationRequest {\n"); + sb.Append(" Live: ").Append(this.Live).Append("\n"); + sb.Append(" NotificationItemContainers: ").Append(NotificationItemContainers).Append("\n"); + sb.Append("}\n"); + return sb.ToString(); + } + + + /// + /// Returns the JSON string presentation of the object. + /// + /// JSON string presentation of the object. + public string ToJson() + { + return JsonConvert.SerializeObject(this, Formatting.Indented); + } + } +} diff --git a/Adyen/Model/Notification/NotificationRequestConst.cs b/Adyen/Webhooks/Models/NotificationRequestConstants.cs similarity index 96% rename from Adyen/Model/Notification/NotificationRequestConst.cs rename to Adyen/Webhooks/Models/NotificationRequestConstants.cs index bcbe9af48..796c14dd9 100644 --- a/Adyen/Model/Notification/NotificationRequestConst.cs +++ b/Adyen/Webhooks/Models/NotificationRequestConstants.cs @@ -1,68 +1,68 @@ -#region License -// /* -// * ###### -// * ###### -// * ############ ####( ###### #####. ###### ############ ############ -// * ############# #####( ###### #####. ###### ############# ############# -// * ###### #####( ###### #####. ###### ##### ###### ##### ###### -// * ###### ###### #####( ###### #####. ###### ##### ##### ##### ###### -// * ###### ###### #####( ###### #####. ###### ##### ##### ###### -// * ############# ############# ############# ############# ##### ###### -// * ############ ############ ############# ############ ##### ###### -// * ###### -// * ############# -// * ############ -// * -// * Adyen Dotnet API Library -// * -// * Copyright (c) 2020 Adyen B.V. -// * This file is open source and available under the MIT license. -// * See the LICENSE file for more info. -// */ -#endregion - -namespace Adyen.Model.Notification -{ - public class NotificationRequestConst - { - //Event codes - public const string EventCodeAuthorisation = "AUTHORISATION"; - public const string EventCodeAuthorisationAdjustment = "AUTHORISATION_ADJUSTMENT"; - public const string EventCodeCancellation = "CANCELLATION"; - public const string EventCodeCancelOrRefund = "CANCEL_OR_REFUND"; - public const string EventCodeCapture = "CAPTURE"; - public const string EventCodeCaptureFailed = "CAPTURE_FAILED"; - public const string EventCodeChargeback = "CHARGEBACK"; - public const string EventCodeChargebackReversed = "CHARGEBACK_REVERSED"; - public const string EventCodeHandledExternally = "HANDLED_EXTERNALLY"; - public const string EventCodeManualReviewAccept = "MANUAL_REVIEW_ACCEPT"; - public const string EventCodeManualReviewReject = "MANUAL_REVIEW_REJECT"; - public const string EventCodeNotificationOfChargeback = "NOTIFICATION_OF_CHARGEBACK"; - public const string EventCodeNotificationOfFraud = "NOTIFICATION_OF_FRAUD"; - public const string EventCodeOfferClosed = "OFFER_CLOSED"; - public const string EventCodeOrderClosed = "ORDER_CLOSED"; - public const string EventCodeOrderOpened = "ORDER_OPENED"; - public const string EventCodePaidoutReversed = "PAIDOUT_REVERSED"; - public const string EventCodePayoutDecline = "PAYOUT_DECLINE"; - public const string EventCodePayoutExpire = "PAYOUT_EXPIRE"; - public const string EventCodePayoutThirdparty = "PAYOUT_THIRDPARTY"; - public const string EventCodePending = "PENDING"; - public const string EventCodePostponedRefund = "POSTPONED_REFUND"; - public const string EventCodePrearbitrationLost = "PREARBITRATION_LOST"; - public const string EventCodePrearbitrationWon = "PREARBITRATION_WON"; - public const string EventCodeProcessRetry = "PROCESS_RETRY"; - public const string EventCodeRecurringContract = "RECURRING_CONTRACT"; - public const string EventCodeRefund = "REFUND"; - public const string EventCodeRefundedReversed = "REFUNDED_REVERSED"; - public const string EventCodeRefundFailed = "REFUND_FAILED"; - public const string EventCodeRefundWithData = "REFUND_WITH_DATA"; - public const string EventCodeReportAvailable = "REPORT_AVAILABLE"; - public const string EventCodeRequestForInformation = "REQUEST_FOR_INFORMATION"; - public const string EventCodeSecondChargeback = "SECOND_CHARGEBACK"; - public const string EventCodeVoidPendingRefund = "VOID_PENDING_REFUND"; - - //Additional Data - public const string AdditionalDataTotalFraudScore = "totalFraudScore"; - public const string AdditionalDataFraudCheckPattern = "fraudCheck-(\\d+)-([A-Za-z0-9]+)"; - } +#region License +// /* +// * ###### +// * ###### +// * ############ ####( ###### #####. ###### ############ ############ +// * ############# #####( ###### #####. ###### ############# ############# +// * ###### #####( ###### #####. ###### ##### ###### ##### ###### +// * ###### ###### #####( ###### #####. ###### ##### ##### ##### ###### +// * ###### ###### #####( ###### #####. ###### ##### ##### ###### +// * ############# ############# ############# ############# ##### ###### +// * ############ ############ ############# ############ ##### ###### +// * ###### +// * ############# +// * ############ +// * +// * Adyen Dotnet API Library +// * +// * Copyright (c) 2020 Adyen B.V. +// * This file is open source and available under the MIT license. +// * See the LICENSE file for more info. +// */ +#endregion + +namespace Adyen.Webhooks.Models +{ + public class NotificationRequestConstants + { + //Event codes + public const string EventCodeAuthorisation = "AUTHORISATION"; + public const string EventCodeAuthorisationAdjustment = "AUTHORISATION_ADJUSTMENT"; + public const string EventCodeCancellation = "CANCELLATION"; + public const string EventCodeCancelOrRefund = "CANCEL_OR_REFUND"; + public const string EventCodeCapture = "CAPTURE"; + public const string EventCodeCaptureFailed = "CAPTURE_FAILED"; + public const string EventCodeChargeback = "CHARGEBACK"; + public const string EventCodeChargebackReversed = "CHARGEBACK_REVERSED"; + public const string EventCodeHandledExternally = "HANDLED_EXTERNALLY"; + public const string EventCodeManualReviewAccept = "MANUAL_REVIEW_ACCEPT"; + public const string EventCodeManualReviewReject = "MANUAL_REVIEW_REJECT"; + public const string EventCodeNotificationOfChargeback = "NOTIFICATION_OF_CHARGEBACK"; + public const string EventCodeNotificationOfFraud = "NOTIFICATION_OF_FRAUD"; + public const string EventCodeOfferClosed = "OFFER_CLOSED"; + public const string EventCodeOrderClosed = "ORDER_CLOSED"; + public const string EventCodeOrderOpened = "ORDER_OPENED"; + public const string EventCodePaidoutReversed = "PAIDOUT_REVERSED"; + public const string EventCodePayoutDecline = "PAYOUT_DECLINE"; + public const string EventCodePayoutExpire = "PAYOUT_EXPIRE"; + public const string EventCodePayoutThirdparty = "PAYOUT_THIRDPARTY"; + public const string EventCodePending = "PENDING"; + public const string EventCodePostponedRefund = "POSTPONED_REFUND"; + public const string EventCodePrearbitrationLost = "PREARBITRATION_LOST"; + public const string EventCodePrearbitrationWon = "PREARBITRATION_WON"; + public const string EventCodeProcessRetry = "PROCESS_RETRY"; + public const string EventCodeRecurringContract = "RECURRING_CONTRACT"; + public const string EventCodeRefund = "REFUND"; + public const string EventCodeRefundedReversed = "REFUNDED_REVERSED"; + public const string EventCodeRefundFailed = "REFUND_FAILED"; + public const string EventCodeRefundWithData = "REFUND_WITH_DATA"; + public const string EventCodeReportAvailable = "REPORT_AVAILABLE"; + public const string EventCodeRequestForInformation = "REQUEST_FOR_INFORMATION"; + public const string EventCodeSecondChargeback = "SECOND_CHARGEBACK"; + public const string EventCodeVoidPendingRefund = "VOID_PENDING_REFUND"; + + //Additional Data + public const string AdditionalDataTotalFraudScore = "totalFraudScore"; + public const string AdditionalDataFraudCheckPattern = "fraudCheck-(\\d+)-([A-Za-z0-9]+)"; + } } \ No newline at end of file diff --git a/Adyen/Model/Notification/NotificationRequestItem.cs b/Adyen/Webhooks/Models/NotificationRequestItem.cs similarity index 94% rename from Adyen/Model/Notification/NotificationRequestItem.cs rename to Adyen/Webhooks/Models/NotificationRequestItem.cs index 153a43583..9efa46c4d 100644 --- a/Adyen/Model/Notification/NotificationRequestItem.cs +++ b/Adyen/Webhooks/Models/NotificationRequestItem.cs @@ -1,44 +1,43 @@ -#region License -// /* -// * ###### -// * ###### -// * ############ ####( ###### #####. ###### ############ ############ -// * ############# #####( ###### #####. ###### ############# ############# -// * ###### #####( ###### #####. ###### ##### ###### ##### ###### -// * ###### ###### #####( ###### #####. ###### ##### ##### ##### ###### -// * ###### ###### #####( ###### #####. ###### ##### ##### ###### -// * ############# ############# ############# ############# ##### ###### -// * ############ ############ ############# ############ ##### ###### -// * ###### -// * ############# -// * ############ -// * -// * Adyen Dotnet API Library -// * -// * Copyright (c) 2020 Adyen B.V. -// * This file is open source and available under the MIT license. -// * See the LICENSE file for more info. -// */ -#endregion - -using System.Collections.Generic; -using Adyen.Model.Checkout; - -namespace Adyen.Model.Notification -{ - public class NotificationRequestItem - { - public Amount Amount { get; set; } - public string EventCode { get; set; } - public string EventDate { get; set; } - public string MerchantAccountCode { get; set; } - public string MerchantReference { get; set; } - public string OriginalReference { get; set; } - public string PspReference { get; set; } - public string Reason { get; set; } - public bool Success { get; set; } - public string PaymentMethod { get; set; } - public List Operations { get; set; } - public Dictionary AdditionalData { get; set; } - } +#region License +// /* +// * ###### +// * ###### +// * ############ ####( ###### #####. ###### ############ ############ +// * ############# #####( ###### #####. ###### ############# ############# +// * ###### #####( ###### #####. ###### ##### ###### ##### ###### +// * ###### ###### #####( ###### #####. ###### ##### ##### ##### ###### +// * ###### ###### #####( ###### #####. ###### ##### ##### ###### +// * ############# ############# ############# ############# ##### ###### +// * ############ ############ ############# ############ ##### ###### +// * ###### +// * ############# +// * ############ +// * +// * Adyen Dotnet API Library +// * +// * Copyright (c) 2020 Adyen B.V. +// * This file is open source and available under the MIT license. +// * See the LICENSE file for more info. +// */ +#endregion + +using System.Collections.Generic; + +namespace Adyen.Webhooks.Models +{ + public class NotificationRequestItem + { + public Amount Amount { get; set; } + public string EventCode { get; set; } + public string EventDate { get; set; } + public string MerchantAccountCode { get; set; } + public string MerchantReference { get; set; } + public string OriginalReference { get; set; } + public string PspReference { get; set; } + public string Reason { get; set; } + public bool Success { get; set; } + public string PaymentMethod { get; set; } + public List Operations { get; set; } + public Dictionary AdditionalData { get; set; } + } } \ No newline at end of file diff --git a/Adyen/Model/Notification/NotificationRequestItemContainer.cs b/Adyen/Webhooks/Models/NotificationRequestItemContainer.cs similarity index 95% rename from Adyen/Model/Notification/NotificationRequestItemContainer.cs rename to Adyen/Webhooks/Models/NotificationRequestItemContainer.cs index 01ea102cd..f5a78114f 100644 --- a/Adyen/Model/Notification/NotificationRequestItemContainer.cs +++ b/Adyen/Webhooks/Models/NotificationRequestItemContainer.cs @@ -1,46 +1,45 @@ -#region License -// /* -// * ###### -// * ###### -// * ############ ####( ###### #####. ###### ############ ############ -// * ############# #####( ###### #####. ###### ############# ############# -// * ###### #####( ###### #####. ###### ##### ###### ##### ###### -// * ###### ###### #####( ###### #####. ###### ##### ##### ##### ###### -// * ###### ###### #####( ###### #####. ###### ##### ##### ###### -// * ############# ############# ############# ############# ##### ###### -// * ############ ############ ############# ############ ##### ###### -// * ###### -// * ############# -// * ############ -// * -// * Adyen Dotnet API Library -// * -// * Copyright (c) 2020 Adyen B.V. -// * This file is open source and available under the MIT license. -// * See the LICENSE file for more info. -// */ -#endregion - -using Adyen.Util; -using System.Text; - -namespace Adyen.Model.Notification -{ - using Newtonsoft.Json; - - public class NotificationRequestItemContainer - { - [JsonProperty("NotificationRequestItem")] - public NotificationRequestItem NotificationItem { get; set; } - - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - sb.Append("class NotificationRequestItemContainer {\n"); - - sb.Append(" notificationItem: ").Append(NotificationItem).Append("\n"); - sb.Append("}"); - return sb.ToString(); - } - } +#region License +// /* +// * ###### +// * ###### +// * ############ ####( ###### #####. ###### ############ ############ +// * ############# #####( ###### #####. ###### ############# ############# +// * ###### #####( ###### #####. ###### ##### ###### ##### ###### +// * ###### ###### #####( ###### #####. ###### ##### ##### ##### ###### +// * ###### ###### #####( ###### #####. ###### ##### ##### ###### +// * ############# ############# ############# ############# ##### ###### +// * ############ ############ ############# ############ ##### ###### +// * ###### +// * ############# +// * ############ +// * +// * Adyen Dotnet API Library +// * +// * Copyright (c) 2020 Adyen B.V. +// * This file is open source and available under the MIT license. +// * See the LICENSE file for more info. +// */ +#endregion + +using Adyen.Util; +using System.Text; + +namespace Adyen.Webhooks.Models +{ + using Newtonsoft.Json; + + public class NotificationRequestItemContainer + { + [JsonProperty("NotificationRequestItem")] + public NotificationRequestItem NotificationItem { get; set; } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class NotificationRequestItemContainer {\n"); + sb.Append(" notificationItem: ").Append(NotificationItem).Append("\n"); + sb.Append("}"); + return sb.ToString(); + } + } } \ No newline at end of file diff --git a/Adyen/Webhooks/WebhookHandler.cs b/Adyen/Webhooks/WebhookHandler.cs index 690c3cbff..6c89363c8 100644 --- a/Adyen/Webhooks/WebhookHandler.cs +++ b/Adyen/Webhooks/WebhookHandler.cs @@ -1,13 +1,45 @@ -using Adyen.Model.Notification; +using System.Text; using Adyen.Util; +using Adyen.Webhooks.Models; +using Newtonsoft.Json; namespace Adyen.Webhooks { + /// + /// Utility function to deserialize . + /// public class WebhookHandler { - public NotificationRequest HandleNotificationRequest(string jsonRequest) + /// + /// Deserializes a object. + /// + /// The JSON payload. + /// . + /// . + public NotificationRequest DeserializeNotificationRequest(string jsonRequest, JsonSerializerSettings jsonSerializerSettings) { - return JsonOperation.Deserialize(jsonRequest); + return JsonConvert.DeserializeObject(jsonRequest, jsonSerializerSettings); + } + } + + internal class ByteArrayConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType == typeof(byte[]); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + return Encoding.UTF8.GetBytes((string)reader.Value); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + byte[] bytes = (byte[])value; + writer.WriteValue(Encoding.UTF8.GetString(bytes)); } } } diff --git a/templates-v7/csharp/AssemblyInfo.mustache b/templates-v7/csharp/AssemblyInfo.mustache new file mode 100644 index 000000000..d5d937dc1 --- /dev/null +++ b/templates-v7/csharp/AssemblyInfo.mustache @@ -0,0 +1,40 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("{{packageTitle}}")] +[assembly: AssemblyDescription("{{packageDescription}}")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("{{packageCompany}}")] +[assembly: AssemblyProduct("{{packageProductName}}")] +[assembly: AssemblyCopyright("{{packageCopyright}}")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("{{packageVersion}}")] +[assembly: AssemblyFileVersion("{{packageVersion}}")] +{{^supportsAsync}} +// Settings which don't support asynchronous operations rely on non-public constructors +// This is due to how RestSharp requires the type constraint `where T : new()` in places it probably shouldn't. +[assembly: InternalsVisibleTo("RestSharp")] +[assembly: InternalsVisibleTo("NewtonSoft.Json")] +[assembly: InternalsVisibleTo("JsonSubTypes")] +{{/supportsAsync}} diff --git a/templates-v7/csharp/NullConditionalParameter.mustache b/templates-v7/csharp/NullConditionalParameter.mustache new file mode 100644 index 000000000..d8ad92666 --- /dev/null +++ b/templates-v7/csharp/NullConditionalParameter.mustache @@ -0,0 +1 @@ +{{#isNullable}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}}{{/isNullable}} \ No newline at end of file diff --git a/templates-v7/csharp/NullConditionalProperty.mustache b/templates-v7/csharp/NullConditionalProperty.mustache new file mode 100644 index 000000000..7dcafa803 --- /dev/null +++ b/templates-v7/csharp/NullConditionalProperty.mustache @@ -0,0 +1 @@ +{{#lambda.first}}{{#isNullable}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} {{/isNullable}}{{^required}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} {{/required}}{{/lambda.first}} \ No newline at end of file diff --git a/templates-v7/csharp/OpenAPIDateConverter.mustache b/templates-v7/csharp/OpenAPIDateConverter.mustache new file mode 100644 index 000000000..d79051025 --- /dev/null +++ b/templates-v7/csharp/OpenAPIDateConverter.mustache @@ -0,0 +1,21 @@ +{{>partial_header}} +using Newtonsoft.Json.Converters; + +namespace {{packageName}}.Client +{ + /// + /// Formatter for 'date' openapi formats ss defined by full-date - RFC3339 + /// see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#data-types + /// + public class OpenAPIDateConverter : IsoDateTimeConverter + { + /// + /// Initializes a new instance of the class. + /// + public OpenAPIDateConverter() + { + // full-date = date-fullyear "-" date-month "-" date-mday + DateTimeFormat = "yyyy-MM-dd"; + } + } +} diff --git a/templates-v7/csharp/ValidateRegex.mustache b/templates-v7/csharp/ValidateRegex.mustache new file mode 100644 index 000000000..d05be8a97 --- /dev/null +++ b/templates-v7/csharp/ValidateRegex.mustache @@ -0,0 +1,6 @@ +// {{{name}}} ({{{dataType}}}) pattern. +Regex regex{{{name}}} = new Regex(@"{{{vendorExtensions.x-regex}}}"{{#vendorExtensions.x-modifiers}}{{#-first}}, {{/-first}}RegexOptions.{{{.}}}{{^-last}} | {{/-last}}{{/vendorExtensions.x-modifiers}}); +if (!regex{{{name}}}.Match(this.{{{name}}}{{#isUuid}}.ToString(){{/isUuid}}).Success) +{ + yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for {{{name}}}, must match a pattern of " + regex{{{name}}}, new [] { "{{{name}}}" }); +} \ No newline at end of file diff --git a/templates-v7/csharp/auth/OAuthAuthenticator.mustache b/templates-v7/csharp/auth/OAuthAuthenticator.mustache new file mode 100644 index 000000000..4a2a3fa85 --- /dev/null +++ b/templates-v7/csharp/auth/OAuthAuthenticator.mustache @@ -0,0 +1,136 @@ +{{>partial_header}} + +using System; +using System.Threading.Tasks; +using Newtonsoft.Json; +using RestSharp; +using RestSharp.Authenticators; + +namespace {{packageName}}.{{clientPackage}}.Auth +{ + /// + /// An authenticator for OAuth2 authentication flows + /// + public class OAuthAuthenticator : IAuthenticator + { + private TokenResponse{{nrt?}} _token; + + /// + /// Returns the current authentication token. Can return null if there is no authentication token, or it has expired. + /// + public string{{nrt?}} Token + { + get + { + if (_token == null) return null; + if (_token.ExpiresIn == null) return _token.AccessToken; + if (_token.ExpiresAt < DateTime.Now) return null; + + return _token.AccessToken; + } + } + + readonly string _tokenUrl; + readonly string _clientId; + readonly string _clientSecret; + readonly string{{nrt?}} _scope; + readonly string _grantType; + readonly JsonSerializerSettings _serializerSettings; + readonly IReadableConfiguration _configuration; + + /// + /// Initialize the OAuth2 Authenticator + /// + public OAuthAuthenticator( + string tokenUrl, + string clientId, + string clientSecret, + string{{nrt?}} scope, + OAuthFlow? flow, + JsonSerializerSettings serializerSettings, + IReadableConfiguration configuration) + { + _tokenUrl = tokenUrl; + _clientId = clientId; + _clientSecret = clientSecret; + _scope = scope; + _serializerSettings = serializerSettings; + _configuration = configuration; + + switch (flow) + { + /*case OAuthFlow.ACCESS_CODE: + _grantType = "authorization_code"; + break; + case OAuthFlow.IMPLICIT: + _grantType = "implicit"; + break; + case OAuthFlow.PASSWORD: + _grantType = "password"; + break;*/ + case OAuthFlow.APPLICATION: + _grantType = "client_credentials"; + break; + default: + break; + } + } + + /// + /// Creates an authentication parameter from an access token. + /// + /// An authentication parameter. + protected async ValueTask GetAuthenticationParameter() + { + var token = string.IsNullOrEmpty(Token) ? await GetToken().ConfigureAwait(false) : Token; + return new HeaderParameter(KnownHeaders.Authorization, token); + } + + /// + /// Gets the token from the OAuth2 server. + /// + /// An authentication token. + async Task GetToken() + { + var client = new RestClient(_tokenUrl, configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(_serializerSettings, _configuration))); + + var request = new RestRequest(); + if (!string.IsNullOrWhiteSpace(_token?.RefreshToken)) + { + request.AddParameter("grant_type", "refresh_token") + .AddParameter("refresh_token", _token.RefreshToken); + } + else + { + request + .AddParameter("grant_type", _grantType) + .AddParameter("client_id", _clientId) + .AddParameter("client_secret", _clientSecret); + } + if (!string.IsNullOrEmpty(_scope)) + { + request.AddParameter("scope", _scope); + } + _token = await client.PostAsync(request).ConfigureAwait(false); + // RFC6749 - token_type is case insensitive. + // RFC6750 - In Authorization header Bearer should be capitalized. + // Fix the capitalization irrespective of token_type casing. + switch (_token?.TokenType?.ToLower()) + { + case "bearer": + return $"Bearer {_token.AccessToken}"; + default: + return $"{_token?.TokenType} {_token?.AccessToken}"; + } + } + + /// + /// Retrieves the authentication token (creating a new one if necessary) and adds it to the current request + /// + /// + /// + /// + public async ValueTask Authenticate(IRestClient client, RestRequest request) + => request.AddOrUpdateParameter(await GetAuthenticationParameter().ConfigureAwait(false)); + } +} diff --git a/templates-v7/csharp/auth/OAuthFlow.mustache b/templates-v7/csharp/auth/OAuthFlow.mustache new file mode 100644 index 000000000..0578a2d16 --- /dev/null +++ b/templates-v7/csharp/auth/OAuthFlow.mustache @@ -0,0 +1,19 @@ +{{>partial_header}} + +namespace {{packageName}}.{{clientPackage}}.Auth +{ + /// + /// Available flows for OAuth2 authentication + /// + public enum OAuthFlow + { + /// Authorization code flow + ACCESS_CODE, + /// Implicit flow + IMPLICIT, + /// Password flow + PASSWORD, + /// Client credentials flow + APPLICATION + } +} \ No newline at end of file diff --git a/templates-v7/csharp/auth/TokenResponse.mustache b/templates-v7/csharp/auth/TokenResponse.mustache new file mode 100644 index 000000000..fc90d0c51 --- /dev/null +++ b/templates-v7/csharp/auth/TokenResponse.mustache @@ -0,0 +1,24 @@ +{{>partial_header}} + +using System; +using Newtonsoft.Json; + +namespace {{packageName}}.{{clientPackage}}.Auth +{ + class TokenResponse + { + [JsonProperty("token_type")] + public string TokenType { get; set; } + [JsonProperty("access_token")] + public string AccessToken { get; set; } + [JsonProperty("expires_in")] + public int? ExpiresIn { get; set; } + [JsonProperty("created")] + public DateTime? Created { get; set; } + + [JsonProperty("refresh_token")] + public string{{nrt?}} RefreshToken { get; set; } + + public DateTime? ExpiresAt => ExpiresIn == null ? null : Created?.AddSeconds(ExpiresIn.Value); + } +} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/ApiException.mustache b/templates-v7/csharp/libraries/generichost/ApiException.mustache new file mode 100644 index 000000000..df9992b69 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/ApiException.mustache @@ -0,0 +1,47 @@ +// +{{>partial_header}} + +{{#nrt}} +#nullable enable + +{{/nrt}} +using System; + +namespace {{packageName}}.{{corePackageName}}.{{clientPackage}} +{ + /// + /// API Exception + /// + {{>visibility}} class ApiException : Exception + { + /// + /// The reason the api request failed + /// + public string{{nrt?}} ReasonPhrase { get; } + + /// + /// The HttpStatusCode + /// + public System.Net.HttpStatusCode StatusCode { get; } + + /// + /// The raw data returned by the api + /// + public string RawContent { get; } + + /// + /// Construct the ApiException from parts of the response + /// + /// + /// + /// + public ApiException(string{{nrt?}} reasonPhrase, System.Net.HttpStatusCode statusCode, string rawContent) : base(reasonPhrase ?? rawContent) + { + ReasonPhrase = reasonPhrase; + + StatusCode = statusCode; + + RawContent = rawContent; + } + } +} diff --git a/templates-v7/csharp/libraries/generichost/ApiFactory.mustache b/templates-v7/csharp/libraries/generichost/ApiFactory.mustache new file mode 100644 index 000000000..873c8b355 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/ApiFactory.mustache @@ -0,0 +1,48 @@ +using System; +using Microsoft.Extensions.DependencyInjection; + +namespace {{packageName}}.{{corePackageName}}.{{clientPackage}} +{ + /// + /// The factory interface for creating the services that can communicate with the {{packageName}} APIs. + /// + {{>visibility}} interface {{interfacePrefix}}ApiFactory + { + /// + /// A method to create an IApi of type IResult + /// + /// + /// + IResult Create() where IResult : {{interfacePrefix}}{{packageName}}ApiService; + } + + /// + /// The implementation of . + /// + {{>visibility}} class ApiFactory : {{interfacePrefix}}ApiFactory + { + /// + /// The service provider + /// + public IServiceProvider Services { get; } + + /// + /// Initializes a new instance of the class. + /// + /// + public ApiFactory(IServiceProvider services) + { + Services = services; + } + + /// + /// A method to create an IApi of type IResult + /// + /// + /// + public IResult Create() where IResult : {{interfacePrefix}}{{packageName}}ApiService + { + return Services.GetRequiredService(); + } + } +} diff --git a/templates-v7/csharp/libraries/generichost/ApiKeyToken.mustache b/templates-v7/csharp/libraries/generichost/ApiKeyToken.mustache new file mode 100644 index 000000000..dbea2b22d --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/ApiKeyToken.mustache @@ -0,0 +1,50 @@ +// +{{partial_header}} +{{#nrt}} +#nullable enable + +{{/nrt}} +using System; +using {{packageName}}.{{corePackageName}}.Auth; +using {{packageName}}.{{corePackageName}}.{{clientPackage}}; + +namespace {{packageName}}.{{apiName}}.{{clientPackage}} +{ + /// + /// The `ADYEN_API_KEY` is an Adyen API authentication token that must be included in the HTTP request header and allows your application to securely communicate with the Adyen APIs. + /// Guide on how to obtain the `ADYEN_API_KEY` + /// 1. For Digital/ECOM & In-Person Payments, visit: https://docs.adyen.com/development-resources/api-credentials/#generate-api-key to get your API Key. + /// 2. For Platforms & Financial Services, visit: https://docs.adyen.com/adyen-for-platforms-model to get started. + /// + {{>visibility}} class ApiKeyToken : TokenBase + { + private readonly string _apiKeyValue; + + /// + /// The name of the header. + /// + public ClientUtils.ApiKeyHeader Header { get; } + + /// + /// Constructs the ApiKeyToken object with the API key value provided. + /// This can then be accessed using ITokenProvider``. + /// + /// Your Adyen API Key value. + /// The header name, retrieved from + /// Your prefix, default: empty. + public ApiKeyToken(string value, ClientUtils.ApiKeyHeader header, string prefix = "") : base() + { + Header = header; + _apiKeyValue = $"{prefix}{value}"; + } + + /// + /// Places the token in the header. + /// + /// + public virtual void AddTokenToHttpRequestMessageHeader(global::System.Net.Http.HttpRequestMessage request) + { + request.Headers.Add(ClientUtils.ApiKeyHeaderToString(Header), _apiKeyValue); + } + } +} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/ApiResponseEventArgs.mustache b/templates-v7/csharp/libraries/generichost/ApiResponseEventArgs.mustache new file mode 100644 index 000000000..c7f5abf37 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/ApiResponseEventArgs.mustache @@ -0,0 +1,24 @@ +using System; + +namespace {{packageName}}.{{corePackageName}}.{{clientPackage}} +{ + /// + /// Useful for tracking server health + /// + {{>visibility}} class ApiResponseEventArgs : EventArgs + { + /// + /// The ApiResponse + /// + public ApiResponse ApiResponse { get; } + + /// + /// The ApiResponseEventArgs + /// + /// + public ApiResponseEventArgs(ApiResponse apiResponse) + { + ApiResponse = apiResponse; + } + } +} diff --git a/templates-v7/csharp/libraries/generichost/ApiResponse`1.mustache b/templates-v7/csharp/libraries/generichost/ApiResponse`1.mustache new file mode 100644 index 000000000..8d716c0f9 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/ApiResponse`1.mustache @@ -0,0 +1,202 @@ +// +{{#nrt}} +#nullable enable +{{/nrt}} +using System; +{{^netStandard}} +using System.Diagnostics.CodeAnalysis; +{{/netStandard}} +using System.Net; + +namespace {{packageName}}.{{corePackageName}}.{{clientPackage}} +{ + /// + /// Provides a non-generic contract for the ApiResponse wrapper. + /// + {{>visibility}} partial interface IApiResponse + { + /// + /// The IsSuccessStatusCode from the api response + /// + bool IsSuccessStatusCode { get; } + + /// + /// Gets the status code (HTTP status code) + /// + /// The status code. + HttpStatusCode StatusCode { get; } + + /// + /// The raw content of this response. + /// + string RawContent { get; } + + /// + /// The raw binary stream (only set for binary responses) + /// + System.IO.Stream{{nrt?}} ContentStream { get; } + + /// + /// The DateTime when the request was retrieved. + /// + DateTime DownloadedAt { get; } + + /// + /// The headers contained in the api response + /// + System.Net.Http.Headers.HttpResponseHeaders Headers { get; } + + /// + /// The path used when making the request. + /// + string Path { get; } + + /// + /// The reason phrase contained in the api response + /// + string{{nrt?}} ReasonPhrase { get; } + + /// + /// The DateTime when the request was sent. + /// + DateTime RequestedAt { get; } + + /// + /// The Uri used when making the request. + /// + Uri{{nrt?}} RequestUri { get; } + } + + /// + /// API Response + /// + {{>visibility}} partial class ApiResponse : IApiResponse + { + /// + /// Gets the status code (HTTP status code) + /// + /// The status code. + public HttpStatusCode StatusCode { get; } + + /// + /// The raw data + /// + public string RawContent { get; protected set; } + + /// + /// The raw binary stream (only set for binary responses) + /// + public System.IO.Stream{{nrt?}} ContentStream { get; protected set; } + + /// + /// The IsSuccessStatusCode from the api response + /// + public bool IsSuccessStatusCode { get; } + + /// + /// The reason phrase contained in the api response + /// + public string{{nrt?}} ReasonPhrase { get; } + + /// + /// The headers contained in the api response + /// + public System.Net.Http.Headers.HttpResponseHeaders Headers { get; } + + /// + /// The DateTime when the request was retrieved. + /// + public DateTime DownloadedAt { get; } = DateTime.UtcNow; + + /// + /// The DateTime when the request was sent. + /// + public DateTime RequestedAt { get; } + + /// + /// The path used when making the request. + /// + public string Path { get; } + + /// + /// The Uri used when making the request. + /// + public Uri{{nrt?}} RequestUri { get; } + + /// + /// The + /// + protected System.Text.Json.JsonSerializerOptions _jsonSerializerOptions; + + /// + /// Construct the response using an HttpResponseMessage + /// + /// + /// + /// + /// + /// + /// + public ApiResponse(global::System.Net.Http.HttpRequestMessage httpRequestMessage, System.Net.Http.HttpResponseMessage httpResponseMessage, string rawContent, string path, DateTime requestedAt, System.Text.Json.JsonSerializerOptions jsonSerializerOptions) + { + StatusCode = httpResponseMessage.StatusCode; + Headers = httpResponseMessage.Headers; + IsSuccessStatusCode = httpResponseMessage.IsSuccessStatusCode; + ReasonPhrase = httpResponseMessage.ReasonPhrase; + RawContent = rawContent; + Path = path; + RequestUri = httpRequestMessage.RequestUri; + RequestedAt = requestedAt; + _jsonSerializerOptions = jsonSerializerOptions; + OnCreated(httpRequestMessage, httpResponseMessage); + } + + /// + /// Construct the response using an HttpResponseMessage. + /// + /// + /// + /// + /// + /// + /// + public ApiResponse(global::System.Net.Http.HttpRequestMessage httpRequestMessage, System.Net.Http.HttpResponseMessage httpResponseMessage, System.IO.Stream contentStream, string path, DateTime requestedAt, System.Text.Json.JsonSerializerOptions jsonSerializerOptions) + { + StatusCode = httpResponseMessage.StatusCode; + Headers = httpResponseMessage.Headers; + IsSuccessStatusCode = httpResponseMessage.IsSuccessStatusCode; + ReasonPhrase = httpResponseMessage.ReasonPhrase; + ContentStream = contentStream; + RawContent = string.Empty; + Path = path; + RequestUri = httpRequestMessage.RequestUri; + RequestedAt = requestedAt; + _jsonSerializerOptions = jsonSerializerOptions; + OnCreated(httpRequestMessage, httpResponseMessage); + } + + partial void OnCreated(global::System.Net.Http.HttpRequestMessage httpRequestMessage, System.Net.Http.HttpResponseMessage httpResponseMessage); + } + {{#x-http-statuses-with-return}} + + /// + /// An interface for responses of type {{TType}}. + /// + /// + {{>visibility}} interface I{{.}} : IApiResponse + { + /// + /// Deserializes the response if the response is {{.}}. + /// + /// + TType {{.}}(); + + /// + /// Returns true if the response is {{.}} and the deserialized response is not null. + /// + /// + /// + bool TryDeserialize{{.}}Response({{#net60OrLater}}[NotNullWhen(true)]{{/net60OrLater}}out TType{{nrt?}} result); + } + {{/x-http-statuses-with-return}} +} diff --git a/templates-v7/csharp/libraries/generichost/ApiTestsBase.mustache b/templates-v7/csharp/libraries/generichost/ApiTestsBase.mustache new file mode 100644 index 000000000..789de490e --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/ApiTestsBase.mustache @@ -0,0 +1,66 @@ +{{>partial_header}} +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using Microsoft.Extensions.Hosting; +using {{packageName}}.{{clientPackage}};{{#hasImport}} +using {{packageName}}.{{modelPackage}};{{/hasImport}} +using {{packageName}}.Extensions; + + +{{>testInstructions}} + + + +namespace {{packageName}}.Test.{{apiPackage}} +{ + /// + /// Base class for API tests + /// + public class ApiTestsBase + { + protected readonly IHost _host; + + public ApiTestsBase(string[] args) + { + _host = CreateHostBuilder(args).Build(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) + .Configure{{apiName}}((context, services, options) => + { + {{#lambda.trimTrailingWithNewLine}} + {{#apiKeyMethods}} + string apiKeyTokenValue{{-index}} = context.Configuration[""] ?? throw new Exception("Token not found."); + ApiKeyToken apiKeyToken{{-index}} = new{{^net70OrLater}} ApiKeyToken{{/net70OrLater}}(apiKeyTokenValue{{-index}}, ClientUtils.ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}, timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(apiKeyToken{{-index}}); + + {{/apiKeyMethods}} + {{#httpBearerMethods}} + string bearerTokenValue{{-index}} = context.Configuration[""] ?? throw new Exception("Token not found."); + BearerToken bearerToken{{-index}} = new{{^net70OrLater}} BearerToken{{/net70OrLater}}(bearerTokenValue{{-index}}, timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(bearerToken{{-index}}); + + {{/httpBearerMethods}} + {{#httpBasicMethods}} + string basicTokenUsername{{-index}} = context.Configuration[""] ?? throw new Exception("Username not found."); + string basicTokenPassword{{-index}} = context.Configuration[""] ?? throw new Exception("Password not found."); + BasicToken basicToken{{-index}} = new{{^net70OrLater}} BasicToken{{/net70OrLater}}(basicTokenUsername{{-index}}, basicTokenPassword{{-index}}, timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(basicToken{{-index}}); + + {{/httpBasicMethods}} + {{#httpSignatureMethods}} + HttpSigningConfiguration config{{-index}} = new{{^net70OrLater}} HttpSigningConfiguration{{/net70OrLater}}("", "", null, new List(), HashAlgorithmName.SHA256, "", 0); + HttpSignatureToken httpSignatureToken{{-index}} = new{{^net70OrLater}} HttpSignatureToken{{/net70OrLater}}(config{{-index}}, timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(httpSignatureToken{{-index}}); + + {{/httpSignatureMethods}} + {{#oauthMethods}} + string oauthTokenValue{{-index}} = context.Configuration[""] ?? throw new Exception("Token not found."); + OAuthToken oauthToken{{-index}} = new{{^net70OrLater}} OAuthToken{{/net70OrLater}}(oauthTokenValue{{-index}}, timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(oauthToken{{-index}}); + {{/oauthMethods}} + {{/lambda.trimTrailingWithNewLine}} + }); + } +} diff --git a/templates-v7/csharp/libraries/generichost/AsModel.mustache b/templates-v7/csharp/libraries/generichost/AsModel.mustache new file mode 100644 index 000000000..f98d3ad57 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/AsModel.mustache @@ -0,0 +1,3 @@ +return Is{{vendorExtensions.x-http-status}} + ? {{#isBinary}}ContentStream{{/isBinary}}{{^isBinary}}System.Text.Json.JsonSerializer.Deserialize<{{#isModel}}{{^containerType}}{{packageName}}.{{modelPackage}}.{{/containerType}}{{/isModel}}{{{dataType}}}>(RawContent, _jsonSerializerOptions){{/isBinary}} + : {{#net60OrLater}}null{{/net60OrLater}}{{^net60OrLater}}default{{/net60OrLater}}; diff --git a/templates-v7/csharp/libraries/generichost/BearerToken.mustache b/templates-v7/csharp/libraries/generichost/BearerToken.mustache new file mode 100644 index 000000000..1dd463bcd --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/BearerToken.mustache @@ -0,0 +1,41 @@ +\// +{{partial_header}} +{{#nrt}} +#nullable enable + +{{/nrt}} +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace {{packageName}}.{{clientPackage}} +{ + /// + /// A token constructed from a token from a bearer token. + /// + {{>visibility}} class BearerToken : TokenBase + { + private string _raw; + + /// + /// Constructs a BearerToken object. + /// + /// + /// + public BearerToken(string value, TimeSpan? timeout = null) : base(timeout) + { + _raw = value; + } + + /// + /// Places the token in the header. + /// + /// + /// + public virtual void UseInHeader(global::System.Net.Http.HttpRequestMessage request, string headerName) + { + request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _raw); + } + } +} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/ClientUtils.mustache b/templates-v7/csharp/libraries/generichost/ClientUtils.mustache new file mode 100644 index 000000000..c11ae8da7 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/ClientUtils.mustache @@ -0,0 +1,385 @@ +{{>partial_header}} +{{#nrt}} +#nullable enable + +{{/nrt}} +using System; +using System.IO; +using System.Linq; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions;{{#useCompareNetObjects}} +using KellermanSoftware.CompareNetObjects;{{/useCompareNetObjects}} +using {{packageName}}; +using {{packageName}}.{{corePackageName}}.Options; +{{#models}} +{{#-first}} +using {{packageName}}.{{modelPackage}}; +{{/-first}} +{{/models}} +using Models = {{packageName}}.{{modelPackage}}; +using System.Runtime.CompilerServices; + +namespace {{packageName}}.{{apiName}}.{{clientPackage}} +{ + /// + /// Utility functions providing some benefit to API client consumers. + /// + {{>visibility}} static {{#net70OrLater}}partial {{/net70OrLater}}class ClientUtils + { + {{#useCompareNetObjects}} + /// + /// An instance of CompareLogic. + /// + public static CompareLogic compareLogic; + + /// + /// Static constructor to initialise compareLogic. + /// + static ClientUtils() + { + {{#equatable}} + ComparisonConfig comparisonConfig = new{{^net70OrLater}} ComparisonConfig{{/net70OrLater}}(); + comparisonConfig.UseHashCodeIdentifier = true; + {{/equatable}} + compareLogic = new{{^net70OrLater}} CompareLogic{{/net70OrLater}}({{#equatable}}comparisonConfig{{/equatable}}); + } + {{/useCompareNetObjects}} + /// + /// A delegate for events. + /// + /// + /// + /// + /// + public delegate void EventHandler(object sender, T e) where T : EventArgs; + + /// + /// Returns true when deserialization succeeds. + /// + /// + /// + /// + /// + /// + public static bool TryDeserialize(string json, JsonSerializerOptions options, {{#net60OrLater}}[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] {{/net60OrLater}}out T{{#nrt}}{{#net60OrLater}}?{{/net60OrLater}}{{/nrt}} result) + { + try + { + result = JsonSerializer.Deserialize(json, options); + return result != null; + } + catch (Exception) + { + result = default; + return false; + } + } + + /// + /// Returns true when deserialization succeeds. + /// + /// + /// + /// + /// + /// + public static bool TryDeserialize(ref Utf8JsonReader reader, JsonSerializerOptions options, {{#net60OrLater}}[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] {{/net60OrLater}}out T{{#nrt}}{{#net60OrLater}}?{{/net60OrLater}}{{/nrt}} result) + { + try + { + result = JsonSerializer.Deserialize(ref reader, options); + return result != null; + } + catch (Exception) + { + result = default; + return false; + } + } + + /// + /// The format to use for DateTime serialization. + /// + public const string ISO8601_DATETIME_FORMAT = "o"; + + /// + /// If parameter is DateTime, output in a formatted string (default ISO 8601), customizable with Configuration.DateTime. + /// If parameter is a list, join the list with ",". + /// Otherwise just return the string. + /// + /// The parameter (header, path, query, form). + /// The DateTime serialization format. + /// Formatted string. + public static string{{nrt?}} ParameterToString(object{{nrt?}} obj, string{{nrt?}} format = ISO8601_DATETIME_FORMAT) + { + if (obj is DateTime dateTime) + // Return a formatted date string - Can be customized with Configuration.DateTimeFormat + // Defaults to an ISO 8601, using the known as a Round-trip date/time pattern ("o") + // https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Anchor_8 + // For example: 2009-06-15T13:45:30.0000000 + return dateTime.ToString(format); + if (obj is DateTimeOffset dateTimeOffset) + // Return a formatted date string - Can be customized with Configuration.DateTimeFormat + // Defaults to an ISO 8601, using the known as a Round-trip date/time pattern ("o") + // https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx#Anchor_8 + // For example: 2009-06-15T13:45:30.0000000 + return dateTimeOffset.ToString(format); + {{#net60OrLater}} + if (obj is DateOnly dateOnly) + return dateOnly.ToString(format); + {{/net60OrLater}} + if (obj is bool boolean) + return boolean ? "true" : "false"; + {{#models}} + {{#model}} + {{#isEnum}} + if (obj is Models.{{classname}} {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}) + {{! below has #isNumeric as a work around but should probably have ^isString instead https://github.com/OpenAPITools/openapi-generator/issues/15038}} + return {{classname}}ValueConverter{{#isInnerEnum}}{{classname}}.{{/isInnerEnum}}{{{datatypeWithEnum}}}.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}){{#isNumeric}}.ToString(){{/isNumeric}}; + {{/isEnum}} + {{^isEnum}} + {{#vars}} + {{#items.isEnum}} + {{#items}} + {{^complexType}} + if (obj is Models.{{#isInnerEnum}}{{classname}}.{{/isInnerEnum}}{{{datatypeWithEnum}}} {{#lambda.camelcase_sanitize_param}}{{classname}}{{{datatypeWithEnum}}}{{/lambda.camelcase_sanitize_param}}) + {{! below has #isNumeric as a work around but should probably have ^isString instead https://github.com/OpenAPITools/openapi-generator/issues/15038}} + return {{#isInnerEnum}}{{classname}}.{{/isInnerEnum}}{{{datatypeWithEnum}}}.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{{datatypeWithEnum}}}{{/lambda.camelcase_sanitize_param}}){{#isNumeric}}.ToString(){{/isNumeric}}; + {{/complexType}} + {{/items}} + {{/items.isEnum}} + {{#isEnum}} + {{^complexType}} + if (obj is Models.{{#isInnerEnum}}{{classname}}.{{/isInnerEnum}}{{{datatypeWithEnum}}} {{#lambda.camelcase_sanitize_param}}{{classname}}{{{datatypeWithEnum}}}{{/lambda.camelcase_sanitize_param}}) + {{! below has #isNumeric as a work around but should probably have ^isString instead https://github.com/OpenAPITools/openapi-generator/issues/15038}} + return Models.{{#isInnerEnum}}{{classname}}.{{/isInnerEnum}}{{{datatypeWithEnum}}}.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{{datatypeWithEnum}}}{{/lambda.camelcase_sanitize_param}}){{#isNumeric}}.ToString(){{/isNumeric}}; + {{/complexType}} + {{/isEnum}} + {{/vars}} + {{/isEnum}} + {{/model}} + {{/models}} + if (obj is ICollection collection) + { + List entries = new{{^net70OrLater}} List{{/net70OrLater}}(); + foreach (var entry in collection) + entries.Add(ParameterToString(entry)); + return string.Join(",", entries); + } + + return Convert.ToString(obj, System.Globalization.CultureInfo.InvariantCulture); + } + + /// + /// URL encode a string + /// Credit/Ref: https://github.com/restsharp/RestSharp/blob/master/RestSharp/Extensions/StringExtensions.cs#L50 + /// + /// string to be URL encoded + /// Byte array + public static string UrlEncode(string input) + { + const int maxLength = 32766; + + if (input == null) + { + throw new ArgumentNullException("input"); + } + + if (input.Length <= maxLength) + { + return Uri.EscapeDataString(input); + } + + StringBuilder sb = new StringBuilder(input.Length * 2); + int index = 0; + + while (index < input.Length) + { + int length = Math.Min(input.Length - index, maxLength); + string subString = input.Substring(index, length); + + sb.Append(Uri.EscapeDataString(subString)); + index += subString.Length; + } + + return sb.ToString(); + } + + /// + /// Encode string in base64 format. + /// + /// string to be encoded. + /// Encoded string. + public static string Base64Encode(string text) + { + return Convert.ToBase64String(global::System.Text.Encoding.UTF8.GetBytes(text)); + } + + /// + /// Convert stream to byte array + /// + /// Input stream to be converted + /// Byte array + public static byte[] ReadAsBytes(Stream inputStream) + { + using (var ms = new MemoryStream()) + { + inputStream.CopyTo(ms); + return ms.ToArray(); + } + } + + /// + /// Select the Content-Type header's value from the given content-type array: + /// if JSON type exists in the given array, use it; + /// otherwise use the first one defined in 'consumes' + /// + /// The Content-Type array to select from. + /// The Content-Type header to use. + public static string{{nrt?}} SelectHeaderContentType(string[] contentTypes) + { + if (contentTypes.Length == 0) + return null; + + foreach (var contentType in contentTypes) + { + if (IsJsonMime(contentType)) + return contentType; + } + + return contentTypes[0]; // use the first content type specified in 'consumes' + } + + /// + /// Select the Accept header's value from the given accepts array: + /// if JSON exists in the given array, use it; + /// otherwise use all of them (joining into a string) + /// + /// The accepts array to select from. + /// The Accept header to use. + public static string{{nrt?}} SelectHeaderAccept(string[] accepts) + { + if (accepts.Length == 0) + return null; + + if (accepts.Contains("application/json", StringComparer.OrdinalIgnoreCase)) + return "application/json"; + + return string.Join(",", accepts); + } + + /// + /// Provides a case-insensitive check that a provided content type is a known JSON-like content type. + /// + {{#net70OrLater}} + [GeneratedRegex("(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$")] + private static partial Regex JsonRegex(); + {{/net70OrLater}} + {{^net70OrLater}} + private static readonly Regex JsonRegex = new Regex("(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"); + {{/net70OrLater}} + + /// + /// Check if the given MIME is a JSON MIME. + /// JSON MIME examples: + /// application/json + /// application/json; charset=UTF8 + /// APPLICATION/JSON + /// application/vnd.company+json + /// + /// MIME + /// Returns True if MIME type is json. + public static bool IsJsonMime(string mime) + { + if (string.IsNullOrWhiteSpace(mime)) return false; + + return {{#net70OrLater}}JsonRegex(){{/net70OrLater}}{{^net70OrLater}}JsonRegex{{/net70OrLater}}.IsMatch(mime) || mime.Equals("application/json-patch+json"); + } + + /// + /// Get the discriminator + /// + /// + /// + /// + /// + public static string{{nrt?}} GetDiscriminator(Utf8JsonReader utf8JsonReader, string discriminator) + { + int currentDepth = utf8JsonReader.CurrentDepth; + + if (utf8JsonReader.TokenType != JsonTokenType.StartObject && utf8JsonReader.TokenType != JsonTokenType.StartArray) + throw new JsonException(); + + JsonTokenType startingTokenType = utf8JsonReader.TokenType; + + while (utf8JsonReader.Read()) + { + if (startingTokenType == JsonTokenType.StartObject && utf8JsonReader.TokenType == JsonTokenType.EndObject && currentDepth == utf8JsonReader.CurrentDepth) + break; + + if (startingTokenType == JsonTokenType.StartArray && utf8JsonReader.TokenType == JsonTokenType.EndArray && currentDepth == utf8JsonReader.CurrentDepth) + break; + + if (utf8JsonReader.TokenType == JsonTokenType.PropertyName && currentDepth == utf8JsonReader.CurrentDepth - 1) + { + string{{nrt?}} jsonPropertyName = utf8JsonReader.GetString(); + utf8JsonReader.Read(); + + if (jsonPropertyName != null && jsonPropertyName.Equals(discriminator)) + return utf8JsonReader.GetString(); + } + } + + throw new JsonException("The specified discriminator was not found."); + } + + {{#hasApiKeyMethods}} + /// + /// An enum of headers. + /// + public enum ApiKeyHeader + { + {{#apiKeyMethods}} + /// + /// The {{keyParamName}} header. + /// + {{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}{{^-last}},{{/-last}} + {{/apiKeyMethods}} + } + + /// + /// Converts an ApiKeyHeader to a string. + /// + /// + /// as a string value. + /// + {{>visibility}} static string ApiKeyHeaderToString(ApiKeyHeader value) + { + {{#net80OrLater}} + return value switch + { + {{#apiKeyMethods}} + ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}} => "{{keyParamName}}", + {{/apiKeyMethods}} + _ => throw new System.ComponentModel.InvalidEnumArgumentException(nameof(value), (int)value, typeof(ApiKeyHeader)), + }; + {{/net80OrLater}} + {{^net80OrLater}} + switch(value) + { + {{#apiKeyMethods}} + case ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}: + return "{{keyParamName}}"; + {{/apiKeyMethods}} + default: + throw new System.ComponentModel.InvalidEnumArgumentException(nameof(value), (int)value, typeof(ApiKeyHeader)); + } + {{/net80OrLater}} + } + + {{/hasApiKeyMethods}} + } +} diff --git a/templates-v7/csharp/libraries/generichost/CookieContainer.mustache b/templates-v7/csharp/libraries/generichost/CookieContainer.mustache new file mode 100644 index 000000000..f96d4fb41 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/CookieContainer.mustache @@ -0,0 +1,22 @@ +// +{{partial_header}} +{{#nrt}} +#nullable enable + +{{/nrt}} +using System.Linq; +using System.Collections.Generic; + +namespace {{packageName}}.{{clientPackage}} +{ + /// + /// A class containing a CookieContainer + /// + {{>visibility}} sealed class CookieContainer + { + /// + /// The collection of tokens + /// + public System.Net.CookieContainer Value { get; } = new System.Net.CookieContainer(); + } +} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/DateFormats.mustache b/templates-v7/csharp/libraries/generichost/DateFormats.mustache new file mode 100644 index 000000000..920ecda88 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/DateFormats.mustache @@ -0,0 +1,2 @@ + "yyyy'-'MM'-'dd", + "yyyyMMdd" diff --git a/templates-v7/csharp/libraries/generichost/DateOnlyJsonConverter.mustache b/templates-v7/csharp/libraries/generichost/DateOnlyJsonConverter.mustache new file mode 100644 index 000000000..1441421aa --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/DateOnlyJsonConverter.mustache @@ -0,0 +1,52 @@ +{{>partial_header}} +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace {{packageName}}.{{clientPackage}} +{ + /// + /// Formatter for 'date' openapi formats ss defined by full-date - RFC3339 + /// see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#data-types + /// + {{>visibility}} class DateOnlyJsonConverter : JsonConverter + { + /// + /// The formats used to deserialize the date + /// + public static string[] Formats { get; } = { +{{>DateFormats}} + + }; + + /// + /// Returns a DateOnly from the Json object + /// + /// + /// + /// + /// + public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + if (reader.TokenType == JsonTokenType.Null) + throw new NotSupportedException(); + + string value = reader.GetString(){{nrt!}}; + + foreach(string format in Formats) + if (DateOnly.TryParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out DateOnly result)) + return result; + + throw new NotSupportedException(); + } + + /// + /// Writes the DateOnly to the json writer + /// + /// + /// + /// + public override void Write(Utf8JsonWriter writer, DateOnly dateOnlyValue, JsonSerializerOptions options) => + writer.WriteStringValue(dateOnlyValue.ToString("{{{dateFormat}}}", CultureInfo.InvariantCulture)); + } +} diff --git a/templates-v7/csharp/libraries/generichost/DateOnlyNullableJsonConverter.mustache b/templates-v7/csharp/libraries/generichost/DateOnlyNullableJsonConverter.mustache new file mode 100644 index 000000000..53fdfbee7 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/DateOnlyNullableJsonConverter.mustache @@ -0,0 +1,57 @@ +{{>partial_header}} +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace {{packageName}}.{{clientPackage}} +{ + /// + /// Formatter for 'date' openapi formats ss defined by full-date - RFC3339 + /// see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#data-types + /// + {{>visibility}} class DateOnlyNullableJsonConverter : JsonConverter + { + /// + /// The formats used to deserialize the date + /// + public static string[] Formats { get; } = { +{{>DateFormats}} + + }; + + /// + /// Returns a DateOnly from the Json object + /// + /// + /// + /// + /// + public override DateOnly? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + if (reader.TokenType == JsonTokenType.Null) + return null; + + string value = reader.GetString(){{nrt!}}; + + foreach(string format in Formats) + if (DateOnly.TryParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out DateOnly result)) + return result; + + throw new NotSupportedException(); + } + + /// + /// Writes the DateOnly to the json writer + /// + /// + /// + /// + public override void Write(Utf8JsonWriter writer, DateOnly? dateOnlyValue, JsonSerializerOptions options) + { + if (dateOnlyValue == null) + writer.WriteNullValue(); + else + writer.WriteStringValue(dateOnlyValue.Value.ToString("{{{dateFormat}}}", CultureInfo.InvariantCulture)); + } + } +} diff --git a/templates-v7/csharp/libraries/generichost/DateTimeFormats.mustache b/templates-v7/csharp/libraries/generichost/DateTimeFormats.mustache new file mode 100644 index 000000000..85ed99a2c --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/DateTimeFormats.mustache @@ -0,0 +1,22 @@ + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffffffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ffK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fK", + "yyyy'-'MM'-'dd'T'HH':'mm':'ssK", + {{^supportsDateOnly}} + "yyyy'-'MM'-'dd", + {{/supportsDateOnly}} + "yyyyMMddTHHmmss.fffffffK", + "yyyyMMddTHHmmss.ffffffK", + "yyyyMMddTHHmmss.fffffK", + "yyyyMMddTHHmmss.ffffK", + "yyyyMMddTHHmmss.fffK", + "yyyyMMddTHHmmss.ffK", + "yyyyMMddTHHmmss.fK", + "yyyyMMddTHHmmssK", + {{^supportsDateOnly}} + "yyyyMMdd" + {{/supportsDateOnly}} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/DateTimeJsonConverter.mustache b/templates-v7/csharp/libraries/generichost/DateTimeJsonConverter.mustache new file mode 100644 index 000000000..bf5059fca --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/DateTimeJsonConverter.mustache @@ -0,0 +1,52 @@ +{{>partial_header}} +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace {{packageName}}.{{clientPackage}} +{ + /// + /// Formatter for {{#supportsDateOnly}}'date-time'{{/supportsDateOnly}}{{^supportsDateOnly}}'date' and 'date-time'{{/supportsDateOnly}} openapi formats ss defined by full-date - RFC3339 + /// see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#data-types + /// + {{>visibility}} class DateTimeJsonConverter : JsonConverter + { + /// + /// The formats used to deserialize the date + /// + public static string[] Formats { get; } = { +{{>DateTimeFormats}} + + }; + + /// + /// Returns a DateTime from the Json object + /// + /// + /// + /// + /// + public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + if (reader.TokenType == JsonTokenType.Null) + throw new NotSupportedException(); + + string value = reader.GetString(){{nrt!}}; + + foreach(string format in Formats) + if (DateTime.TryParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out DateTime result)) + return result; + + throw new NotSupportedException(); + } + + /// + /// Writes the DateTime to the json writer + /// + /// + /// + /// + public override void Write(Utf8JsonWriter writer, DateTime dateTimeValue, JsonSerializerOptions options) => + writer.WriteStringValue(dateTimeValue.ToString("{{{dateTimeFormat}}}", CultureInfo.InvariantCulture)); + } +} diff --git a/templates-v7/csharp/libraries/generichost/DateTimeNullableJsonConverter.mustache b/templates-v7/csharp/libraries/generichost/DateTimeNullableJsonConverter.mustache new file mode 100644 index 000000000..bbc036490 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/DateTimeNullableJsonConverter.mustache @@ -0,0 +1,57 @@ +{{>partial_header}} +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace {{packageName}}.{{clientPackage}} +{ + /// + /// Formatter for {{#supportsDateOnly}}'date-time'{{/supportsDateOnly}}{{^supportsDateOnly}}'date' and 'date-time'{{/supportsDateOnly}} openapi formats ss defined by full-date - RFC3339 + /// see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#data-types + /// + {{>visibility}} class DateTimeNullableJsonConverter : JsonConverter + { + /// + /// The formats used to deserialize the date + /// + public static string[] Formats { get; } = { +{{>DateTimeFormats}} + + }; + + /// + /// Returns a DateTime from the Json object + /// + /// + /// + /// + /// + public override DateTime? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + if (reader.TokenType == JsonTokenType.Null) + return null; + + string value = reader.GetString(){{nrt!}}; + + foreach(string format in Formats) + if (DateTime.TryParseExact(value, format, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out DateTime result)) + return result; + + return null; + } + + /// + /// Writes the DateTime to the json writer + /// + /// + /// + /// + public override void Write(Utf8JsonWriter writer, DateTime? dateTimeValue, JsonSerializerOptions options) + { + if (dateTimeValue == null) + writer.WriteNullValue(); + else + writer.WriteStringValue(dateTimeValue.Value.ToString("{{{dateTimeFormat}}}", CultureInfo.InvariantCulture)); + } + } +} diff --git a/templates-v7/csharp/libraries/generichost/DependencyInjectionTests.mustache b/templates-v7/csharp/libraries/generichost/DependencyInjectionTests.mustache new file mode 100644 index 000000000..6085b51e5 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/DependencyInjectionTests.mustache @@ -0,0 +1,211 @@ +{{>partial_header}} +using System; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using System.Collections.Generic; +using System.Security.Cryptography; +using {{packageName}}.{{clientPackage}}; +using {{packageName}}.{{apiPackage}}; +using {{packageName}}.Extensions; +using Xunit; + +namespace {{packageName}}.Test.{{apiPackage}} +{ + /// + /// Tests the dependency injection. + /// + public class DependencyInjectionTest + { + private readonly IHost _hostUsingConfigureWithoutAClient = + Host.CreateDefaultBuilder({{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}Array.Empty(){{/net80OrLater}}).Configure{{apiName}}((context, services, options) => + { + {{#lambda.trimTrailingWithNewLine}} + {{#apiKeyMethods}} + ApiKeyToken apiKeyToken{{-index}} = new{{^net70OrLater}} ApiKeyToken{{/net70OrLater}}("", ClientUtils.ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}, timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(apiKeyToken{{-index}}); + + {{/apiKeyMethods}} + {{#httpBearerMethods}} + BearerToken bearerToken{{-index}} = new{{^net70OrLater}} BearerToken{{/net70OrLater}}("", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(bearerToken{{-index}}); + + {{/httpBearerMethods}} + {{#httpBasicMethods}} + BasicToken basicToken{{-index}} = new{{^net70OrLater}} BasicToken{{/net70OrLater}}("", "", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(basicToken{{-index}}); + + {{/httpBasicMethods}} + {{#httpSignatureMethods}} + HttpSigningConfiguration config{{-index}} = new{{^net70OrLater}} HttpSigningConfiguration{{/net70OrLater}}("", "", null, {{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}new List(){{/net80OrLater}}, HashAlgorithmName.SHA256, "", 0); + HttpSignatureToken httpSignatureToken{{-index}} = new{{^net70OrLater}} HttpSignatureToken{{/net70OrLater}}(config{{-index}}, timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(httpSignatureToken{{-index}}); + + {{/httpSignatureMethods}} + {{#oauthMethods}} + OAuthToken oauthToken{{-index}} = new{{^net70OrLater}} OAuthToken{{/net70OrLater}}("token", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(oauthToken{{-index}}); + + {{/oauthMethods}} + {{/lambda.trimTrailingWithNewLine}} + }) + .Build(); + + private readonly IHost _hostUsingConfigureWithAClient = + Host.CreateDefaultBuilder({{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}Array.Empty(){{/net80OrLater}}).Configure{{apiName}}((context, services, options) => + { + {{#lambda.trimTrailingWithNewLine}} + {{#apiKeyMethods}} + ApiKeyToken apiKeyToken{{-index}} = new{{^net70OrLater}} ApiKeyToken{{/net70OrLater}}("", ClientUtils.ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}, timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(apiKeyToken{{-index}}); + + {{/apiKeyMethods}} + {{#httpBearerMethods}} + BearerToken bearerToken{{-index}} = new{{^net70OrLater}} BearerToken{{/net70OrLater}}("", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(bearerToken{{-index}}); + + {{/httpBearerMethods}} + {{#httpBasicMethods}} + BasicToken basicToken{{-index}} = new{{^net70OrLater}} BasicToken{{/net70OrLater}}("", "", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(basicToken{{-index}}); + + {{/httpBasicMethods}} + {{#httpSignatureMethods}} + HttpSigningConfiguration config{{-index}} = new{{^net70OrLater}} HttpSigningConfiguration{{/net70OrLater}}("", "", null, {{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}new List(){{/net80OrLater}}, HashAlgorithmName.SHA256, "", 0); + HttpSignatureToken httpSignatureToken{{-index}} = new{{^net70OrLater}} HttpSignatureToken{{/net70OrLater}}(config{{-index}}, timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(httpSignatureToken{{-index}}); + + {{/httpSignatureMethods}} + {{#oauthMethods}} + OAuthToken oauthToken = new{{^net70OrLater}} OAuthToken{{/net70OrLater}}("token", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(oauthToken); + + {{/oauthMethods}} + {{/lambda.trimTrailingWithNewLine}} + options.Add{{apiName}}HttpClients(client => client.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS)); + }) + .Build(); + + private readonly IHost _hostUsingAddWithoutAClient = + Host.CreateDefaultBuilder({{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}Array.Empty(){{/net80OrLater}}).ConfigureServices((host, services) => + { + services.Add{{apiName}}(options => + { + {{#lambda.trimTrailingWithNewLine}} + {{#apiKeyMethods}} + ApiKeyToken apiKeyToken{{-index}} = new{{^net70OrLater}} ApiKeyToken{{/net70OrLater}}("", ClientUtils.ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}, timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(apiKeyToken{{-index}}); + + {{/apiKeyMethods}} + {{#httpBearerMethods}} + BearerToken bearerToken{{-index}} = new{{^net70OrLater}} BearerToken{{/net70OrLater}}("", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(bearerToken{{-index}}); + + {{/httpBearerMethods}} + {{#httpBasicMethods}} + BasicToken basicToken{{-index}} = new{{^net70OrLater}} BasicToken{{/net70OrLater}}("", "", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(basicToken{{-index}}); + + {{/httpBasicMethods}} + {{#httpSignatureMethods}} + HttpSigningConfiguration config{{-index}} = new{{^net70OrLater}} HttpSigningConfiguration{{/net70OrLater}}("", "", null, {{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}new List(){{/net80OrLater}}, HashAlgorithmName.SHA256, "", 0); + HttpSignatureToken httpSignatureToken{{-index}} = new{{^net70OrLater}} HttpSignatureToken{{/net70OrLater}}(config{{-index}}, timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(httpSignatureToken{{-index}}); + + {{/httpSignatureMethods}} + {{#oauthMethods}} + OAuthToken oauthToken{{-index}} = new{{^net70OrLater}} OAuthToken{{/net70OrLater}}("token", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(oauthToken{{-index}}); + + {{/oauthMethods}} + {{/lambda.trimTrailingWithNewLine}} + }); + }) + .Build(); + + private readonly IHost _hostUsingAddWithAClient = + Host.CreateDefaultBuilder({{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}Array.Empty(){{/net80OrLater}}).ConfigureServices((host, services) => + { + services.Add{{apiName}}(options => + { + {{#lambda.trimTrailingWithNewLine}} + {{#apiKeyMethods}} + ApiKeyToken apiKeyToken{{-index}} = new{{^net70OrLater}} ApiKeyToken{{/net70OrLater}}("", ClientUtils.ApiKeyHeader.{{#lambda.titlecase}}{{#lambda.alphabet_or_underscore}}{{keyParamName}}{{/lambda.alphabet_or_underscore}}{{/lambda.titlecase}}, timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(apiKeyToken{{-index}}); + + {{/apiKeyMethods}} + {{#httpBearerMethods}} + BearerToken bearerToken{{-index}} = new{{^net70OrLater}} BearerToken{{/net70OrLater}}("", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(bearerToken{{-index}}); + + {{/httpBearerMethods}} + {{#httpBasicMethods}} + BasicToken basicToken{{-index}} = new{{^net70OrLater}} BasicToken{{/net70OrLater}}("", "", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(basicToken{{-index}}); + + {{/httpBasicMethods}} + {{#httpSignatureMethods}} + HttpSigningConfiguration config{{-index}} = new{{^net70OrLater}} HttpSigningConfiguration{{/net70OrLater}}("", "", null, {{#net80OrLater}}[]{{/net80OrLater}}{{^net80OrLater}}new List(){{/net80OrLater}}, HashAlgorithmName.SHA256, "", 0); + HttpSignatureToken httpSignatureToken{{-index}} = new{{^net70OrLater}} HttpSignatureToken{{/net70OrLater}}(config{{-index}}, timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(httpSignatureToken{{-index}}); + + {{/httpSignatureMethods}} + {{#oauthMethods}} + OAuthToken oauthToken{{-index}} = new{{^net70OrLater}} OAuthToken{{/net70OrLater}}("token", timeout: TimeSpan.FromSeconds(1)); + options.AddTokens(oauthToken{{-index}}); + + {{/oauthMethods}} + {{/lambda.trimTrailingWithNewLine}} + options.Add{{apiName}}HttpClients(client => client.BaseAddress = new Uri(ClientUtils.BASE_ADDRESS)); + }); + }) + .Build(); + + /// + /// Test dependency injection when using the configure method + /// + [Fact] + public void ConfigureApiWithAClientTest() + { + {{#apiInfo}}{{#apis}}var {{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}} = _hostUsingConfigureWithAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>(); + Assert.True({{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}}.HttpClient.BaseAddress != null);{{^-last}} + + {{/-last}}{{/apis}}{{/apiInfo}} + } + + /// + /// Test dependency injection when using the configure method + /// + [Fact] + public void ConfigureApiWithoutAClientTest() + { + {{#apiInfo}}{{#apis}}var {{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}} = _hostUsingConfigureWithoutAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>(); + Assert.True({{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}}.HttpClient.BaseAddress != null);{{^-last}} + + {{/-last}}{{/apis}}{{/apiInfo}} + } + + /// + /// Test dependency injection when using the add method + /// + [Fact] + public void AddApiWithAClientTest() + { + {{#apiInfo}}{{#apis}}var {{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}} = _hostUsingAddWithAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>(); + Assert.True({{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}}.HttpClient.BaseAddress != null);{{^-last}} + + {{/-last}}{{/apis}}{{/apiInfo}} + } + + /// + /// Test dependency injection when using the add method + /// + [Fact] + public void AddApiWithoutAClientTest() + { + {{#apiInfo}}{{#apis}}var {{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}} = _hostUsingAddWithoutAClient.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>(); + Assert.True({{#lambda.camel_case}}{{classname}}{{/lambda.camel_case}}.HttpClient.BaseAddress != null);{{^-last}} + + {{/-last}}{{/apis}}{{/apiInfo}} + } + } +} diff --git a/templates-v7/csharp/libraries/generichost/EnumValueDataType.mustache b/templates-v7/csharp/libraries/generichost/EnumValueDataType.mustache new file mode 100644 index 000000000..e92e67b36 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/EnumValueDataType.mustache @@ -0,0 +1 @@ +{{#allowableValues}}{{#enumVars}}{{#-first}}{{#isString}}{{^isNumeric}}string{{/isNumeric}}{{/isString}}{{#isNumeric}}{{#isLong}}long{{/isLong}}{{#isFloat}}float{{/isFloat}}{{#isDouble}}double{{/isDouble}}{{#isDecimal}}decimal{{/isDecimal}}{{^isLong}}{{^isFloat}}{{^isDouble}}{{^isDecimal}}int{{/isDecimal}}{{/isDouble}}{{/isFloat}}{{/isLong}}{{/isNumeric}}{{/-first}}{{/enumVars}}{{/allowableValues}} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/ExceptionEventArgs.mustache b/templates-v7/csharp/libraries/generichost/ExceptionEventArgs.mustache new file mode 100644 index 000000000..b74fcfa0a --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/ExceptionEventArgs.mustache @@ -0,0 +1,24 @@ +using System; + +namespace {{packageName}}.{{clientPackage}} +{ + /// + /// Useful for tracking server health + /// + {{>visibility}} class ExceptionEventArgs : EventArgs + { + /// + /// The ApiResponse + /// + public Exception Exception { get; } + + /// + /// The ExceptionEventArgs + /// + /// + public ExceptionEventArgs(Exception exception) + { + Exception = exception; + } + } +} diff --git a/templates-v7/csharp/libraries/generichost/HostConfiguration.mustache b/templates-v7/csharp/libraries/generichost/HostConfiguration.mustache new file mode 100644 index 000000000..b1723804f --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/HostConfiguration.mustache @@ -0,0 +1,163 @@ +{{>partial_header}} +{{#nrt}} +#nullable enable + +{{/nrt}} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Net.Http; +using Microsoft.Extensions.DependencyInjection; +using {{packageName}}.{{apiPackage}}; +using {{packageName}}.{{apiName}}.{{clientPackage}}; +{{#models}} +{{#-first}} +using {{packageName}}.{{modelPackage}}; +{{/-first}} +{{/models}} +using {{packageName}}.{{corePackageName}}; +using {{packageName}}.{{corePackageName}}.Auth; +using {{packageName}}.{{corePackageName}}.Client; +using {{packageName}}.{{corePackageName}}.Options; +using {{packageName}}.{{corePackageName}}.Converters; + +namespace {{packageName}}.{{apiName}}.{{clientPackage}} +{ + /// + /// Provides hosting configuration for {{apiName}} + /// + {{>visibility}} class HostConfiguration + { + private readonly IServiceCollection _services; + private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions(); + private readonly AdyenOptions _adyenOptions = new AdyenOptions(); + + /// + /// The base path of the API, it includes the http(s)-scheme, the host domain name, and the base path. + /// This value can change when `ConfigureAdyenOptions` is called in ). The new value will be based on the .. + /// + public static string BASE_URL = "{{{basePath}}}"; + + /// + /// Instantiates the HostConfiguration (custom JsonConverters, Events, HttpClient) with the necessary dependencies to communicate with the API. + /// + /// + public HostConfiguration(IServiceCollection services) + { + _services = services; + if (_jsonOptions.Converters.FirstOrDefault(x => x.GetType() == typeof(JsonStringEnumConverter)) == null) + _jsonOptions.Converters.Add(new JsonStringEnumConverter()); + if (_jsonOptions.Converters.FirstOrDefault(x => x.GetType() == typeof(DateTimeJsonConverter)) == null) + _jsonOptions.Converters.Add(new DateTimeJsonConverter()); + if (_jsonOptions.Converters.FirstOrDefault(x => x.GetType() == typeof(DateTimeNullableJsonConverter)) == null) + _jsonOptions.Converters.Add(new DateTimeNullableJsonConverter()); + if (_jsonOptions.Converters.FirstOrDefault(x => x.GetType() == typeof(ByteArrayConverter)) == null) + _jsonOptions.Converters.Add(new ByteArrayConverter()); + {{#supportsDateOnly}} + if (_jsonOptions.Converters.FirstOrDefault(x => x.GetType() == typeof(DateOnlyJsonConverter)) == null) + _jsonOptions.Converters.Add(new DateOnlyJsonConverter()); + if (_jsonOptions.Converters.FirstOrDefault(x => x.GetType() == typeof(DateOnlyNullableJsonConverter)) == null) + _jsonOptions.Converters.Add(new DateOnlyNullableJsonConverter()); + {{/supportsDateOnly}} + {{#models}} + {{#model}} + {{#isEnum}} + _jsonOptions.Converters.Add(new {{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}JsonConverter()); + _jsonOptions.Converters.Add(new {{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}NullableJsonConverter()); + {{/isEnum}} + {{^isEnum}} + _jsonOptions.Converters.Add(new {{classname}}JsonConverter()); + {{/isEnum}} + {{/model}} + {{/models}} + JsonSerializerOptionsProvider jsonSerializerOptionsProvider = new{{^net60OrLater}} JsonSerializerOptionsProvider{{/net60OrLater}}(_jsonOptions); + _services.AddSingleton(jsonSerializerOptionsProvider); + {{#useSourceGeneration}} + + {{#models}} + {{#-first}} + _jsonOptions.TypeInfoResolver = System.Text.Json.Serialization.Metadata.JsonTypeInfoResolver.Combine( + {{/-first}} + {{/models}} + {{#lambda.joinLinesWithComma}} + {{#models}} + {{#model}} + new {{datatypeWithEnum}}{{^datatypeWithEnum}}{{classname}}{{/datatypeWithEnum}}SerializationContext(){{#-last}},{{/-last}} + {{/model}} + {{/models}} + {{/lambda.joinLinesWithComma}} + {{#models}} + {{#-last}} + + new System.Text.Json.Serialization.Metadata.DefaultJsonTypeInfoResolver() + ); + {{/-last}} + {{/models}} + + {{/useSourceGeneration}} + _services.AddSingleton<{{interfacePrefix}}ApiFactory, ApiFactory>();{{#apiInfo}}{{#apis}} + _services.AddSingleton<{{classname}}Events>();{{/apis}}{{/apiInfo}} + } + + /// + /// Configures the and . + /// + /// Configures the . + /// Configures the . + /// . + public HostConfiguration Add{{apiName}}HttpClients(Action{{nrt?}} httpClientOptions = null, Action{{nrt?}} httpClientBuilderOptions = null) + { + Action httpClientAction = httpClient => + { + // Configure HttpClient set by the user. + httpClientOptions?.Invoke(httpClient); + + // Set BaseAddress if it's not set. + if (httpClient.BaseAddress == null) + httpClient.BaseAddress = new Uri(UrlBuilderExtensions.ConstructHostUrl(_adyenOptions, BASE_URL)); + }; + + List builders = new List(); + + {{#apiInfo}}{{#apis}}builders.Add(_services.AddHttpClient<{{interfacePrefix}}{{classname}}, {{classname}}>(httpClientAction)); + {{/apis}}{{/apiInfo}} + if (httpClientBuilderOptions != null) + foreach (IHttpClientBuilder builder in builders) + httpClientBuilderOptions(builder); + + return this; + } + + /// + /// Configures the . + /// + /// Configures the . + /// . + public HostConfiguration ConfigureJsonOptions(Action jsonSerializerOptions) + { + jsonSerializerOptions(_jsonOptions); + + return this; + } + + /// + /// Configures the (e.g. Environment, LiveEndpointPrefix). + /// + /// Configures the . + /// . + public HostConfiguration ConfigureAdyenOptions(Action adyenOptions) + { + adyenOptions(_adyenOptions); + {{#hasApiKeyMethods}} + _services.AddSingleton>( + new TokenProvider( + new ApiKeyToken(_adyenOptions.AdyenApiKey, ClientUtils.ApiKeyHeader.X_API_Key, "") + ) + ); + {{/hasApiKeyMethods}} + return this; + } + } +} diff --git a/templates-v7/csharp/libraries/generichost/HttpSigningConfiguration.mustache b/templates-v7/csharp/libraries/generichost/HttpSigningConfiguration.mustache new file mode 100644 index 000000000..8b69a8c0b --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/HttpSigningConfiguration.mustache @@ -0,0 +1,679 @@ +// +{{>partial_header}} + +{{#nrt}} +#nullable enable + +{{/nrt}} +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.Cryptography; +using System.Text; +using System.Web; + +namespace {{packageName}}.{{clientPackage}} +{ + /// + /// Class for HttpSigning auth related parameter and methods + /// + {{>visibility}} class HttpSigningConfiguration + { + /// + /// Create an instance + /// + public HttpSigningConfiguration(string keyId, string keyFilePath, SecureString{{nrt?}} keyPassPhrase, List httpSigningHeader, HashAlgorithmName hashAlgorithm, string signingAlgorithm, int signatureValidityPeriod) + { + KeyId = keyId; + KeyFilePath = keyFilePath; + KeyPassPhrase = keyPassPhrase; + HttpSigningHeader = httpSigningHeader; + HashAlgorithm = hashAlgorithm; + SigningAlgorithm = signingAlgorithm; + SignatureValidityPeriod = signatureValidityPeriod; + } + + /// + ///Gets the Api keyId + /// + public string KeyId { get; set; } + + /// + /// Gets the Key file path + /// + public string KeyFilePath { get; set; } + + /// + /// Gets the key pass phrase for password protected key + /// + public SecureString{{nrt?}} KeyPassPhrase { get; set; } + + /// + /// Gets the HTTP signing header + /// + public List HttpSigningHeader { get; set; } + + /// + /// Gets the hash algorithm sha256 or sha512 + /// + public HashAlgorithmName HashAlgorithm { get; set; } = HashAlgorithmName.SHA256; + + /// + /// Gets the signing algorithm + /// + public string SigningAlgorithm { get; set; } + + /// + /// Gets the Signature validity period in seconds + /// + public int SignatureValidityPeriod { get; set; } + + private enum PrivateKeyType + { + None = 0, + RSA = 1, + ECDSA = 2, + } + + /// + /// Gets the Headers for HttpSigning + /// + /// + /// + /// + internal Dictionary GetHttpSignedHeader(global::System.Net.Http.HttpRequestMessage request, string requestBody, System.Threading.CancellationToken cancellationToken = default{{^netstandard20OrLater}}(global::System.Threading.CancellationToken){{/netstandard20OrLater}}) + { + if (request.RequestUri == null) + throw new NullReferenceException("The request URI was null"); + + const string HEADER_REQUEST_TARGET = "(request-target)"; + + // The time when the HTTP signature expires. The API server should reject HTTP requests that have expired. + const string HEADER_EXPIRES = "(expires)"; + + //The 'Date' header. + const string HEADER_DATE = "Date"; + + //The 'Host' header. + const string HEADER_HOST = "Host"; + + //The time when the HTTP signature was generated. + const string HEADER_CREATED = "(created)"; + + //When the 'Digest' header is included in the HTTP signature, the client automatically + //computes the digest of the HTTP request body, per RFC 3230. + const string HEADER_DIGEST = "Digest"; + + //The 'Authorization' header is automatically generated by the client. It includes + //the list of signed headers and a base64-encoded signature. + const string HEADER_AUTHORIZATION = "Authorization"; + + //Hash table to store singed headers + var HttpSignedRequestHeader = new Dictionary(); + + var httpSignatureHeader = new Dictionary(); + + if (HttpSigningHeader.Count == 0) + HttpSigningHeader.Add("(created)"); + + var dateTime = DateTime.Now; + string digest = String.Empty; + + if (HashAlgorithm == HashAlgorithmName.SHA256) + { + var bodyDigest = GetStringHash(HashAlgorithm, requestBody); + digest = string.Format("SHA-256={0}", Convert.ToBase64String(bodyDigest)); + } + else if (HashAlgorithm == HashAlgorithmName.SHA512) + { + var bodyDigest = GetStringHash(HashAlgorithm, requestBody); + digest = string.Format("SHA-512={0}", Convert.ToBase64String(bodyDigest)); + } + else + throw new Exception(string.Format("{0} not supported", HashAlgorithm)); + + foreach (var header in HttpSigningHeader) + if (header.Equals(HEADER_REQUEST_TARGET)) + httpSignatureHeader.Add(header.ToLower(), request.RequestUri.ToString()); + else if (header.Equals(HEADER_EXPIRES)) + { + var expireDateTime = dateTime.AddSeconds(SignatureValidityPeriod); + httpSignatureHeader.Add(header.ToLower(), GetUnixTime(expireDateTime).ToString()); + } + else if (header.Equals(HEADER_DATE)) + { + var utcDateTime = dateTime.ToString("r").ToString(); + httpSignatureHeader.Add(header.ToLower(), utcDateTime); + HttpSignedRequestHeader.Add(HEADER_DATE, utcDateTime); + } + else if (header.Equals(HEADER_HOST)) + { + httpSignatureHeader.Add(header.ToLower(), request.RequestUri.ToString()); + HttpSignedRequestHeader.Add(HEADER_HOST, request.RequestUri.ToString()); + } + else if (header.Equals(HEADER_CREATED)) + httpSignatureHeader.Add(header.ToLower(), GetUnixTime(dateTime).ToString()); + else if (header.Equals(HEADER_DIGEST)) + { + HttpSignedRequestHeader.Add(HEADER_DIGEST, digest); + httpSignatureHeader.Add(header.ToLower(), digest); + } + else + { + bool isHeaderFound = false; + foreach (var item in request.Headers) + { + if (string.Equals(item.Key, header, StringComparison.OrdinalIgnoreCase)) + { + httpSignatureHeader.Add(header.ToLower(), item.Value.ToString()); + isHeaderFound = true; + break; + } + } + + if (!isHeaderFound) + throw new Exception(string.Format("Cannot sign HTTP request.Request does not contain the {0} header.",header)); + } + + var headersKeysString = String.Join(" ", httpSignatureHeader.Keys); + var headerValuesList = new List(); + + foreach (var keyVal in httpSignatureHeader) + headerValuesList.Add(string.Format("{0}: {1}", keyVal.Key, keyVal.Value)); + + //Concatenate headers value separated by new line + var headerValuesString = string.Join("\n", headerValuesList); + var signatureStringHash = GetStringHash(HashAlgorithm, headerValuesString); + string{{nrt?}} headerSignatureStr = null; + var keyType = GetKeyType(KeyFilePath); + + if (keyType == PrivateKeyType.RSA) + headerSignatureStr = GetRSASignature(signatureStringHash); + + else if (keyType == PrivateKeyType.ECDSA) + headerSignatureStr = GetECDSASignature(signatureStringHash); + + var cryptographicScheme = "hs2019"; + var authorizationHeaderValue = string.Format("Signature keyId=\"{0}\",algorithm=\"{1}\"", + KeyId, cryptographicScheme); + + if (httpSignatureHeader.ContainsKey(HEADER_CREATED)) + authorizationHeaderValue += string.Format(",created={0}", httpSignatureHeader[HEADER_CREATED]); + + if (httpSignatureHeader.ContainsKey(HEADER_EXPIRES)) + authorizationHeaderValue += string.Format(",expires={0}", httpSignatureHeader[HEADER_EXPIRES]); + + authorizationHeaderValue += string.Format(",headers=\"{0}\",signature=\"{1}\"", headersKeysString, headerSignatureStr); + + HttpSignedRequestHeader.Add(HEADER_AUTHORIZATION, authorizationHeaderValue); + + return HttpSignedRequestHeader; + } + + private byte[] GetStringHash(HashAlgorithmName hashAlgorithmName, string stringToBeHashed) + { + HashAlgorithm{{nrt?}} hashAlgorithm = null; + + if (hashAlgorithmName == HashAlgorithmName.SHA1) + hashAlgorithm = SHA1.Create(); + + if (hashAlgorithmName == HashAlgorithmName.SHA256) + hashAlgorithm = SHA256.Create(); + + if (hashAlgorithmName == HashAlgorithmName.SHA512) + hashAlgorithm = SHA512.Create(); + + if (hashAlgorithmName == HashAlgorithmName.MD5) + hashAlgorithm = MD5.Create(); + + if (hashAlgorithm == null) + throw new NullReferenceException($"{ nameof(hashAlgorithm) } was null."); + + byte[] bytes = Encoding.UTF8.GetBytes(stringToBeHashed); + byte[] stringHash = hashAlgorithm.ComputeHash(bytes); + return stringHash; + } + + private int GetUnixTime(DateTime date2) + { + DateTime date1 = new DateTime(1970, 01, 01); + TimeSpan timeSpan = date2 - date1; + return (int)timeSpan.TotalSeconds; + } + + private string GetRSASignature(byte[] stringToSign) + { + if (KeyPassPhrase == null) + throw new NullReferenceException($"{ nameof(KeyPassPhrase) } was null."); + + RSA{{nrt?}} rsa = GetRSAProviderFromPemFile(KeyFilePath, KeyPassPhrase); + + if (rsa == null) + return string.Empty; + else if (SigningAlgorithm == "RSASSA-PSS") + { + var signedbytes = rsa.SignHash(stringToSign, HashAlgorithm, RSASignaturePadding.Pss); + return Convert.ToBase64String(signedbytes); + } + else if (SigningAlgorithm == "PKCS1-v15") + { + var signedbytes = rsa.SignHash(stringToSign, HashAlgorithm, RSASignaturePadding.Pkcs1); + return Convert.ToBase64String(signedbytes); + } + + return string.Empty; + } + + /// + /// Gets the ECDSA signature + /// + /// + /// + private string GetECDSASignature(byte[] dataToSign) + { + {{#net60OrLater}} + if (!File.Exists(KeyFilePath)) + throw new Exception("key file path does not exist."); + + var ecKeyHeader = "-----BEGIN EC PRIVATE KEY-----"; + var ecKeyFooter = "-----END EC PRIVATE KEY-----"; + var keyStr = File.ReadAllText(KeyFilePath); + var ecKeyBase64String = keyStr.Replace(ecKeyHeader, "").Replace(ecKeyFooter, "").Trim(); + var keyBytes = System.Convert.FromBase64String(ecKeyBase64String); + var ecdsa = ECDsa.Create(); + + var byteCount = 0; + if (KeyPassPhrase != null) + { + IntPtr unmanagedString = IntPtr.Zero; + try + { + // convert secure string to byte array + unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(KeyPassPhrase); + + string ptrToStringUni = Marshal.PtrToStringUni(unmanagedString) ?? throw new NullReferenceException(); + + ecdsa.ImportEncryptedPkcs8PrivateKey(Encoding.UTF8.GetBytes(ptrToStringUni), keyBytes, out byteCount); + } + finally + { + if (unmanagedString != IntPtr.Zero) + Marshal.ZeroFreeBSTR(unmanagedString); + } + } + else + ecdsa.ImportPkcs8PrivateKey(keyBytes, out byteCount); + + var signedBytes = ecdsa.SignHash(dataToSign); + var derBytes = ConvertToECDSAANS1Format(signedBytes); + var signedString = System.Convert.ToBase64String(derBytes); + + return signedString; + {{/net60OrLater}} + {{^net60OrLater}} + throw new Exception("ECDSA signing is supported only on NETCOREAPP3_0 and above"); + {{/net60OrLater}} + } + + private byte[] ConvertToECDSAANS1Format(byte[] signedBytes) + { + var derBytes = new List(); + byte derLength = 68; //default length for ECDSA code signing bit 0x44 + byte rbytesLength = 32; //R length 0x20 + byte sbytesLength = 32; //S length 0x20 + var rBytes = new List(); + var sBytes = new List(); + for (int i = 0; i < 32; i++) + rBytes.Add(signedBytes[i]); + + for (int i = 32; i < 64; i++) + sBytes.Add(signedBytes[i]); + + if (rBytes[0] > 0x7F) + { + derLength++; + rbytesLength++; + var tempBytes = new List(); + tempBytes.AddRange(rBytes); + rBytes.Clear(); + rBytes.Add(0x00); + rBytes.AddRange(tempBytes); + } + + if (sBytes[0] > 0x7F) + { + derLength++; + sbytesLength++; + var tempBytes = new List(); + tempBytes.AddRange(sBytes); + sBytes.Clear(); + sBytes.Add(0x00); + sBytes.AddRange(tempBytes); + + } + + derBytes.Add(48); //start of the sequence 0x30 + derBytes.Add(derLength); //total length r length, type and r bytes + + derBytes.Add(2); //tag for integer + derBytes.Add(rbytesLength); //length of r + derBytes.AddRange(rBytes); + + derBytes.Add(2); //tag for integer + derBytes.Add(sbytesLength); //length of s + derBytes.AddRange(sBytes); + return derBytes.ToArray(); + } + + private RSACryptoServiceProvider{{nrt?}} GetRSAProviderFromPemFile(String pemfile, SecureString{{nrt?}} keyPassPhrase = null) + { + const String pempubheader = "-----BEGIN PUBLIC KEY-----"; + const String pempubfooter = "-----END PUBLIC KEY-----"; + bool isPrivateKeyFile = true; + byte[]{{nrt?}} pemkey = null; + + if (!File.Exists(pemfile)) + throw new Exception("private key file does not exist."); + + string pemstr = File.ReadAllText(pemfile).Trim(); + + if (pemstr.StartsWith(pempubheader) && pemstr.EndsWith(pempubfooter)) + isPrivateKeyFile = false; + + if (isPrivateKeyFile) + { + pemkey = ConvertPrivateKeyToBytes(pemstr, keyPassPhrase); + + if (pemkey == null) + return null; + + return DecodeRSAPrivateKey(pemkey); + } + return null; + } + + private byte[]{{nrt?}} ConvertPrivateKeyToBytes(String instr, SecureString{{nrt?}} keyPassPhrase = null) + { + const String pemprivheader = "-----BEGIN RSA PRIVATE KEY-----"; + const String pemprivfooter = "-----END RSA PRIVATE KEY-----"; + String pemstr = instr.Trim(); + byte[] binkey; + + if (!pemstr.StartsWith(pemprivheader) || !pemstr.EndsWith(pemprivfooter)) + return null; + + StringBuilder sb = new StringBuilder(pemstr); + sb.Replace(pemprivheader, ""); + sb.Replace(pemprivfooter, ""); + String pvkstr = sb.ToString().Trim(); + + try + { // if there are no PEM encryption info lines, this is an UNencrypted PEM private key + binkey = Convert.FromBase64String(pvkstr); + return binkey; + } + catch (global::System.FormatException) + { + StringReader str = new StringReader(pvkstr); + + //-------- read PEM encryption info. lines and extract salt ----- + if (!str.ReadLine(){{nrt!}}.StartsWith("Proc-Type: 4,ENCRYPTED")) // TODO: what do we do here if ReadLine is null? + return null; + + String saltline = str.ReadLine(){{nrt!}}; // TODO: what do we do here if ReadLine is null? + if (!saltline.StartsWith("DEK-Info: DES-EDE3-CBC,")) + return null; + + String saltstr = saltline.Substring(saltline.IndexOf(",") + 1).Trim(); + byte[] salt = new byte[saltstr.Length / 2]; + for (int i = 0; i < salt.Length; i++) + salt[i] = Convert.ToByte(saltstr.Substring(i * 2, 2), 16); + + if (!(str.ReadLine() == "")) + return null; + + //------ remaining b64 data is encrypted RSA key ---- + String encryptedstr = str.ReadToEnd(); + + try + { //should have b64 encrypted RSA key now + binkey = Convert.FromBase64String(encryptedstr); + } + catch (global::System.FormatException) + { //data is not in base64 format + return null; + } + + // TODO: what do we do here if keyPassPhrase is null? + byte[] deskey = GetEncryptedKey(salt, keyPassPhrase{{nrt!}}, 1, 2); // count=1 (for OpenSSL implementation); 2 iterations to get at least 24 bytes + if (deskey == null) + return null; + + //------ Decrypt the encrypted 3des-encrypted RSA private key ------ + byte[]{{nrt?}} rsakey = DecryptKey(binkey, deskey, salt); //OpenSSL uses salt value in PEM header also as 3DES IV + + return rsakey; + } + } + + private RSACryptoServiceProvider{{nrt?}} DecodeRSAPrivateKey(byte[] privkey) + { + byte[] MODULUS, E, D, P, Q, DP, DQ, IQ; + + // --------- Set up stream to decode the asn.1 encoded RSA private key ------ + MemoryStream mem = new MemoryStream(privkey); + BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading + byte bt = 0; + ushort twobytes = 0; + int elems = 0; + try + { + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) + binr.ReadByte(); //advance 1 byte + else if (twobytes == 0x8230) + binr.ReadInt16(); //advance 2 bytes + else + return null; + + twobytes = binr.ReadUInt16(); + if (twobytes != 0x0102) //version number + return null; + + bt = binr.ReadByte(); + if (bt != 0x00) + return null; + + //------ all private key components are Integer sequences ---- + elems = GetIntegerSize(binr); + MODULUS = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + E = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + D = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + P = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + Q = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + DP = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + DQ = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + IQ = binr.ReadBytes(elems); + + // ------- create RSACryptoServiceProvider instance and initialize with public key ----- + RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); + RSAParameters RSAparams = new RSAParameters(); + RSAparams.Modulus = MODULUS; + RSAparams.Exponent = E; + RSAparams.D = D; + RSAparams.P = P; + RSAparams.Q = Q; + RSAparams.DP = DP; + RSAparams.DQ = DQ; + RSAparams.InverseQ = IQ; + RSA.ImportParameters(RSAparams); + return RSA; + } + catch (Exception) + { + return null; + } + finally + { + binr.Close(); + } + } + + private int GetIntegerSize(BinaryReader binr) + { + byte bt = 0; + byte lowbyte = 0x00; + byte highbyte = 0x00; + int count = 0; + bt = binr.ReadByte(); + if (bt != 0x02) //expect integer + return 0; + + bt = binr.ReadByte(); + + if (bt == 0x81) + count = binr.ReadByte(); // data size in next byte + else if (bt == 0x82) + { + highbyte = binr.ReadByte(); // data size in next 2 bytes + lowbyte = binr.ReadByte(); + byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; + count = BitConverter.ToInt32(modint, 0); + } + else + count = bt; // we already have the data size + + while (binr.ReadByte() == 0x00) + //remove high order zeros in data + count -= 1; + + binr.BaseStream.Seek(-1, SeekOrigin.Current); + + //last ReadByte wasn't a removed zero, so back up a byte + return count; + } + + private byte[] GetEncryptedKey(byte[] salt, SecureString secpswd, int count, int miter) + { + IntPtr unmanagedPswd = IntPtr.Zero; + int HASHLENGTH = 16; //MD5 bytes + byte[] keymaterial = new byte[HASHLENGTH * miter]; //to store concatenated Mi hashed results + + byte[] psbytes = new byte[secpswd.Length]; + unmanagedPswd = Marshal.SecureStringToGlobalAllocAnsi(secpswd); + Marshal.Copy(unmanagedPswd, psbytes, 0, psbytes.Length); + Marshal.ZeroFreeGlobalAllocAnsi(unmanagedPswd); + + // --- concatenate salt and pswd bytes into fixed data array --- + byte[] data00 = new byte[psbytes.Length + salt.Length]; + Array.Copy(psbytes, data00, psbytes.Length); //copy the pswd bytes + Array.Copy(salt, 0, data00, psbytes.Length, salt.Length); //concatenate the salt bytes + + // ---- do multi-hashing and concatenate results D1, D2 ... into keymaterial bytes ---- + MD5 md5 = MD5.Create(); + byte[]{{nrt?}} result = null; + byte[] hashtarget = new byte[HASHLENGTH + data00.Length]; //fixed length initial hashtarget + + for (int j = 0; j < miter; j++) + { + // ---- Now hash consecutively for count times ------ + if (j == 0) + result = data00; //initialize + else + { + Array.Copy(result{{nrt!}}, hashtarget, result{{nrt!}}.Length); // TODO: what do we do if result is null here? + Array.Copy(data00, 0, hashtarget, result.Length, data00.Length); + result = hashtarget; + } + + for (int i = 0; i < count; i++) + result = md5.ComputeHash(result); + + Array.Copy(result, 0, keymaterial, j * HASHLENGTH, result.Length); //concatenate to keymaterial + } + byte[] deskey = new byte[24]; + Array.Copy(keymaterial, deskey, deskey.Length); + + Array.Clear(psbytes, 0, psbytes.Length); + Array.Clear(data00, 0, data00.Length); + Array.Clear(result{{nrt!}}, 0, result{{nrt!}}.Length); // TODO: what do we do if result is null here? + Array.Clear(hashtarget, 0, hashtarget.Length); + Array.Clear(keymaterial, 0, keymaterial.Length); + return deskey; + } + + private byte[]{{nrt?}} DecryptKey(byte[] cipherData, byte[] desKey, byte[] IV) + { + MemoryStream memst = new MemoryStream(); + TripleDES alg = TripleDES.Create(); + alg.Key = desKey; + alg.IV = IV; + try + { + CryptoStream cs = new CryptoStream(memst, alg.CreateDecryptor(), CryptoStreamMode.Write); + cs.Write(cipherData, 0, cipherData.Length); + cs.Close(); + } + catch (Exception) + { + return null; + } + byte[] decryptedData = memst.ToArray(); + return decryptedData; + } + + /// + /// Detect the key type from the pem file. + /// + /// key file path in pem format + /// + private PrivateKeyType GetKeyType(string keyFilePath) + { + if (!File.Exists(keyFilePath)) + throw new Exception("Key file path does not exist."); + + var ecPrivateKeyHeader = "BEGIN EC PRIVATE KEY"; + var ecPrivateKeyFooter = "END EC PRIVATE KEY"; + var rsaPrivateKeyHeader = "BEGIN RSA PRIVATE KEY"; + var rsaPrivateFooter = "END RSA PRIVATE KEY"; + //var pkcs8Header = "BEGIN PRIVATE KEY"; + //var pkcs8Footer = "END PRIVATE KEY"; + var keyType = PrivateKeyType.None; + var key = File.ReadAllLines(keyFilePath); + + if (key[0].ToString().Contains(rsaPrivateKeyHeader) && key[key.Length - 1].ToString().Contains(rsaPrivateFooter)) + keyType = PrivateKeyType.RSA; + else if (key[0].ToString().Contains(ecPrivateKeyHeader) && key[key.Length - 1].ToString().Contains(ecPrivateKeyFooter)) + keyType = PrivateKeyType.ECDSA; + + else if (key[0].ToString().Contains(ecPrivateKeyHeader) && key[key.Length - 1].ToString().Contains(ecPrivateKeyFooter)) + { + /* this type of key can hold many type different types of private key, but here due lack of pem header + Considering this as EC key + */ + //TODO :- update the key based on oid + keyType = PrivateKeyType.ECDSA; + } + else + throw new Exception("Either the key is invalid or key is not supported"); + + return keyType; + } + } +} diff --git a/templates-v7/csharp/libraries/generichost/HttpSigningToken.mustache b/templates-v7/csharp/libraries/generichost/HttpSigningToken.mustache new file mode 100644 index 000000000..881682e89 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/HttpSigningToken.mustache @@ -0,0 +1,45 @@ +// +{{partial_header}} +{{#nrt}} +#nullable enable + +{{/nrt}} +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace {{packageName}}.{{clientPackage}} +{ + /// + /// A token constructed from an HttpSigningConfiguration + /// + {{>visibility}} class HttpSignatureToken : TokenBase + { + private HttpSigningConfiguration _configuration; + + /// + /// Constructs an HttpSignatureToken object. + /// + /// + /// + public HttpSignatureToken(HttpSigningConfiguration configuration, TimeSpan? timeout = null) : base(timeout) + { + _configuration = configuration; + } + + /// + /// Places the token in the header. + /// + /// + /// + /// + public void UseInHeader(global::System.Net.Http.HttpRequestMessage request, string requestBody, CancellationToken cancellationToken = default{{^netstandard20OrLater}}(global::System.Threading.CancellationToken){{/netstandard20OrLater}}) + { + var signedHeaders = _configuration.GetHttpSignedHeader(request, requestBody, cancellationToken); + + foreach (var signedHeader in signedHeaders) + request.Headers.Add(signedHeader.Key, signedHeader.Value); + } + } +} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/IApi.mustache b/templates-v7/csharp/libraries/generichost/IApi.mustache new file mode 100644 index 000000000..a2fc6d1ea --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/IApi.mustache @@ -0,0 +1,13 @@ +namespace {{packageName}}.{{apiPackage}} +{ + /// + /// Interface for interacting with any {{packageName}} API using . + /// + {{>visibility}} interface {{interfacePrefix}}{{packageName}}ApiService + { + /// + /// The object, best practice: instantiate and manage object using the . + /// + System.Net.Http.HttpClient HttpClient { get; } + } +} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/IHostBuilderExtensions.mustache b/templates-v7/csharp/libraries/generichost/IHostBuilderExtensions.mustache new file mode 100644 index 000000000..b3df192b6 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/IHostBuilderExtensions.mustache @@ -0,0 +1,41 @@ +{{>partial_header}} +{{#nrt}} +#nullable enable + +{{/nrt}} +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using {{packageName}}.{{apiName}}; +using {{packageName}}.{{apiName}}.{{clientPackage}}; + +namespace {{packageName}}.{{apiName}}.Extensions +{ + /// + /// Extension methods for IHostBuilder. + /// + {{>visibility}} static class HostBuilderExtensions + { + /// + /// Add the {{apiName}} API services to the . + /// You can optionally configure the and . + /// + /// . + /// Configures the , , and . + /// Configures the . + /// Configures the . + public static IHostBuilder Configure{{apiName}}(this IHostBuilder hostBuilder, Action hostConfigurationOptions, Action? httpClientOptions = null, Action? httpClientBuilderOptions = null) + { + hostBuilder.ConfigureServices((context, services) => + { + HostConfiguration hostConfiguration = new HostConfiguration(services); + + hostConfigurationOptions(context, services, hostConfiguration); + + hostConfiguration.Add{{apiName}}HttpClients(httpClientOptions, httpClientBuilderOptions); + }); + + return hostBuilder; + } + } +} diff --git a/templates-v7/csharp/libraries/generichost/IHttpClientBuilderExtensions.mustache b/templates-v7/csharp/libraries/generichost/IHttpClientBuilderExtensions.mustache new file mode 100644 index 000000000..053c0226a --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/IHttpClientBuilderExtensions.mustache @@ -0,0 +1,75 @@ +{{>partial_header}} +{{#nrt}} +#nullable enable + +{{/nrt}} +using System; +using System.Net.Http; +using Microsoft.Extensions.DependencyInjection;{{#supportsRetry}} +using Polly.Timeout; +using Polly.Extensions.Http; +using Polly;{{/supportsRetry}} + +namespace {{packageName}}.Extensions +{ + /// + /// Extension methods for IHttpClientBuilder + /// + {{>visibility}} static class IHttpClientBuilderExtensions + { + {{#supportsRetry}} + /// + /// Adds a Polly retry policy to your clients. + /// + /// + /// + /// + public static IHttpClientBuilder AddRetryPolicy(this IHttpClientBuilder client, int retries) + { + client.AddPolicyHandler(RetryPolicy(retries)); + + return client; + } + + /// + /// Adds a Polly timeout policy to your clients. + /// + /// + /// + /// + public static IHttpClientBuilder AddTimeoutPolicy(this IHttpClientBuilder client, TimeSpan timeout) + { + client.AddPolicyHandler(TimeoutPolicy(timeout)); + + return client; + } + + /// + /// Adds a Polly circuit breaker to your clients. + /// + /// + /// + /// + /// + public static IHttpClientBuilder AddCircuitBreakerPolicy(this IHttpClientBuilder client, int handledEventsAllowedBeforeBreaking, TimeSpan durationOfBreak) + { + client.AddTransientHttpErrorPolicy(builder => CircuitBreakerPolicy(builder, handledEventsAllowedBeforeBreaking, durationOfBreak)); + + return client; + } + + private static Polly.Retry.AsyncRetryPolicy RetryPolicy(int retries) + => HttpPolicyExtensions + .HandleTransientHttpError() + .Or() + .RetryAsync(retries); + + private static AsyncTimeoutPolicy TimeoutPolicy(TimeSpan timeout) + => Policy.TimeoutAsync(timeout); + + private static Polly.CircuitBreaker.AsyncCircuitBreakerPolicy CircuitBreakerPolicy( + PolicyBuilder builder, int handledEventsAllowedBeforeBreaking, TimeSpan durationOfBreak) + => builder.CircuitBreakerAsync(handledEventsAllowedBeforeBreaking, durationOfBreak); + {{/supportsRetry}} + } +} diff --git a/templates-v7/csharp/libraries/generichost/IServiceCollectionExtensions.mustache b/templates-v7/csharp/libraries/generichost/IServiceCollectionExtensions.mustache new file mode 100644 index 000000000..3f366532c --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/IServiceCollectionExtensions.mustache @@ -0,0 +1,33 @@ +{{>partial_header}} +{{#nrt}} +#nullable enable + +{{/nrt}} +using System; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using {{packageName}}.{{corePackageName}}.Auth; +using {{packageName}}.{{apiName}}.{{clientPackage}}; + +namespace {{packageName}}.{{apiName}}.Extensions +{ + /// + /// Extension methods for . + /// + {{>visibility}} static class ServiceCollectionExtensions + { + /// + /// Add the {{packageName}} {{apiName}} API services to your . + /// + /// . + /// Configures the . + /// Configures the . + /// Configures the . + public static void Add{{apiName}}Services(this IServiceCollection services, Action hostConfigurationOptions, Action? httpClientOptions = null, Action? httpClientBuilderOptions = null) + { + HostConfiguration hostConfiguration = new{{^net70OrLater}} HostConfiguration{{/net70OrLater}}(services); + hostConfigurationOptions(hostConfiguration); + hostConfiguration.Add{{apiName}}HttpClients(httpClientOptions, httpClientBuilderOptions); + } + } +} diff --git a/templates-v7/csharp/libraries/generichost/ImplementsIEquatable.mustache b/templates-v7/csharp/libraries/generichost/ImplementsIEquatable.mustache new file mode 100644 index 000000000..dd576dd0f --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/ImplementsIEquatable.mustache @@ -0,0 +1 @@ +{{#equatable}}{{#readOnlyVars}}{{#-first}}IEquatable<{{classname}}{{nrt?}}> {{/-first}}{{/readOnlyVars}}{{/equatable}} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/ImplementsValidatable.mustache b/templates-v7/csharp/libraries/generichost/ImplementsValidatable.mustache new file mode 100644 index 000000000..7c3f0e02a --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/ImplementsValidatable.mustache @@ -0,0 +1 @@ +{{#validatable}}IValidatableObject {{/validatable}} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/JsonConverter.mustache b/templates-v7/csharp/libraries/generichost/JsonConverter.mustache new file mode 100644 index 000000000..391e00e66 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/JsonConverter.mustache @@ -0,0 +1,661 @@ + /// + /// A Json converter for type + /// + {{>visibility}} class {{classname}}JsonConverter : JsonConverter<{{classname}}> + { + {{#allVars}} + {{#isDateTime}} + /// + /// The format to use to serialize {{name}}. + /// + public static string {{name}}Format { get; set; } = "{{{dateTimeFormat}}}"; + + {{/isDateTime}} + {{#isDate}} + /// + /// The format to use to serialize {{name}}. + /// + public static string {{name}}Format { get; set; } = "{{{dateFormat}}}"; + + {{/isDate}} + {{/allVars}} + /// + /// Deserializes json to . + /// + /// . + /// . + /// The , initialized from . + /// . + /// + public override {{classname}} Read(ref Utf8JsonReader utf8JsonReader, Type typeToConvert, JsonSerializerOptions jsonSerializerOptions) + { + {{#lambda.trimTrailingWithNewLine}} + {{#lambda.trimLineBreaks}} + int currentDepth = utf8JsonReader.CurrentDepth; + + if (utf8JsonReader.TokenType != JsonTokenType.StartObject && utf8JsonReader.TokenType != JsonTokenType.StartArray) + throw new JsonException(); + + JsonTokenType startingTokenType = utf8JsonReader.TokenType; + + {{#allVars}} + Option<{{#isInnerEnum}}{{^isMap}}{{classname}}.{{/isMap}}{{/isInnerEnum}}{{{datatypeWithEnum}}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}}> {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = default; + {{#-last}} + + {{/-last}} + {{/allVars}} + {{#discriminator}} + {{#children}} + {{#-first}} + string{{nrt?}} discriminator = ClientUtils.GetDiscriminator(utf8JsonReader, "{{discriminator.propertyBaseName}}"); + + {{/-first}} + if (discriminator != null && discriminator.Equals("{{name}}")) + return JsonSerializer.Deserialize<{{{classname}}}>(ref utf8JsonReader, jsonSerializerOptions) ?? throw new JsonException("The result was an unexpected value."); + + {{/children}} + {{/discriminator}} + {{#model.discriminator}} + {{#model.hasDiscriminatorWithNonEmptyMapping}} + {{#mappedModels}} + {{#model}} + {{^vendorExtensions.x-duplicated-data-type}} + {{classname}}{{nrt?}} {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}} = null; + {{#-last}} + + {{/-last}} + {{/vendorExtensions.x-duplicated-data-type}} + {{/model}} + {{/mappedModels}} + Utf8JsonReader utf8JsonReaderDiscriminator = utf8JsonReader; + while (utf8JsonReaderDiscriminator.Read()) + { + if (startingTokenType == JsonTokenType.StartObject && utf8JsonReaderDiscriminator.TokenType == JsonTokenType.EndObject && currentDepth == utf8JsonReaderDiscriminator.CurrentDepth) + break; + + if (startingTokenType == JsonTokenType.StartArray && utf8JsonReaderDiscriminator.TokenType == JsonTokenType.EndArray && currentDepth == utf8JsonReaderDiscriminator.CurrentDepth) + break; + + if (utf8JsonReaderDiscriminator.TokenType == JsonTokenType.PropertyName && currentDepth == utf8JsonReaderDiscriminator.CurrentDepth - 1) + { + string{{nrt?}} jsonPropertyName = utf8JsonReaderDiscriminator.GetString(); + utf8JsonReaderDiscriminator.Read(); + if (jsonPropertyName{{nrt?}}.Equals("{{propertyBaseName}}"){{#nrt}} ?? false{{/nrt}}) + { + string{{nrt?}} discriminator = utf8JsonReaderDiscriminator.GetString(); + {{#mappedModels}} + if (discriminator{{nrt?}}.Equals("{{mappingName}}"){{#nrt}} ?? false{{/nrt}}) + { + Utf8JsonReader utf8JsonReader{{model.classname}} = utf8JsonReader; + {{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}} = JsonSerializer.Deserialize<{{{model.classname}}}>(ref utf8JsonReader{{model.classname}}, jsonSerializerOptions); + } + {{/mappedModels}} + } + } + } + + {{/model.hasDiscriminatorWithNonEmptyMapping}} + {{/model.discriminator}} + {{^model.discriminator}} + {{#composedSchemas}} + {{#oneOf}} + {{^vendorExtensions.x-duplicated-data-type}} + {{{datatypeWithEnum}}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = default; + {{#-last}} + + Utf8JsonReader utf8JsonReaderOneOf = utf8JsonReader; + while (utf8JsonReaderOneOf.Read()) + { + if (startingTokenType == JsonTokenType.StartObject && utf8JsonReaderOneOf.TokenType == JsonTokenType.EndObject && currentDepth == utf8JsonReaderOneOf.CurrentDepth) + break; + + if (startingTokenType == JsonTokenType.StartArray && utf8JsonReaderOneOf.TokenType == JsonTokenType.EndArray && currentDepth == utf8JsonReaderOneOf.CurrentDepth) + break; + + if (utf8JsonReaderOneOf.TokenType == JsonTokenType.PropertyName && currentDepth == utf8JsonReaderOneOf.CurrentDepth - 1) + { + {{#oneOf}} + Utf8JsonReader utf8JsonReader{{name}} = utf8JsonReader; + ClientUtils.TryDeserialize<{{{datatypeWithEnum}}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}}>(ref utf8JsonReader{{name}}, jsonSerializerOptions, out {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}); + {{^-last}} + + {{/-last}} + {{/oneOf}} + } + } + {{/-last}} + {{/vendorExtensions.x-duplicated-data-type}} + {{/oneOf}} + + {{#anyOf}} + {{^vendorExtensions.x-duplicated-data-type}} + {{{datatypeWithEnum}}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = default; + {{#-last}} + + Utf8JsonReader utf8JsonReaderAnyOf = utf8JsonReader; + while (utf8JsonReaderAnyOf.Read()) + { + if (startingTokenType == JsonTokenType.StartObject && utf8JsonReaderAnyOf.TokenType == JsonTokenType.EndObject && currentDepth == utf8JsonReaderAnyOf.CurrentDepth) + break; + + if (startingTokenType == JsonTokenType.StartArray && utf8JsonReaderAnyOf.TokenType == JsonTokenType.EndArray && currentDepth == utf8JsonReaderAnyOf.CurrentDepth) + break; + + if (utf8JsonReaderAnyOf.TokenType == JsonTokenType.PropertyName && currentDepth == utf8JsonReaderAnyOf.CurrentDepth - 1) + { + {{#anyOf}} + Utf8JsonReader utf8JsonReader{{name}} = utf8JsonReader; + ClientUtils.TryDeserialize<{{{datatypeWithEnum}}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}}>(ref utf8JsonReader{{name}}, jsonSerializerOptions, out {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}); + {{^-last}} + + {{/-last}} + {{/anyOf}} + } + } + {{/-last}} + {{/vendorExtensions.x-duplicated-data-type}} + {{/anyOf}} + + {{/composedSchemas}} + {{/model.discriminator}} + while (utf8JsonReader.Read()) + { + if (startingTokenType == JsonTokenType.StartObject && utf8JsonReader.TokenType == JsonTokenType.EndObject && currentDepth == utf8JsonReader.CurrentDepth) + break; + + if (startingTokenType == JsonTokenType.StartArray && utf8JsonReader.TokenType == JsonTokenType.EndArray && currentDepth == utf8JsonReader.CurrentDepth) + break; + + if (utf8JsonReader.TokenType == JsonTokenType.PropertyName && currentDepth == utf8JsonReader.CurrentDepth - 1) + { + string{{nrt?}} jsonPropertyName = utf8JsonReader.GetString(); + utf8JsonReader.Read(); + + switch (jsonPropertyName) + { + {{#allVars}} + case "{{baseName}}": + {{#isString}} + {{^isMap}} + {{^isEnum}} + {{^isUuid}} + {{#isDecimal}} + {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.GetDecimal()); + {{/isDecimal}} + {{^isDecimal}} + {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.GetString(){{^isNullable}}{{nrt!}}{{/isNullable}}); + {{/isDecimal}} + {{/isUuid}} + {{/isEnum}} + {{/isMap}} + {{/isString}} + {{#isBoolean}} + {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.TokenType == JsonTokenType.Null ? (bool?)null : utf8JsonReader.GetBoolean()); + {{/isBoolean}} + {{#isNumeric}} + {{^isEnum}} + {{#isDouble}} + {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.TokenType == JsonTokenType.Null ? (double?)null : utf8JsonReader.GetDouble()); + {{/isDouble}} + {{#isDecimal}} + {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.TokenType == JsonTokenType.Null ? (decimal?)null : utf8JsonReader.GetDecimal()); + {{/isDecimal}} + {{#isFloat}} + {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.TokenType == JsonTokenType.Null ? (float?)null : (float)utf8JsonReader.GetDouble()); + {{/isFloat}} + {{#isLong}} + {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.TokenType == JsonTokenType.Null ? ({{#vendorExtensions.x-unsigned}}u{{/vendorExtensions.x-unsigned}}long?)null : utf8JsonReader.Get{{#vendorExtensions.x-unsigned}}U{{/vendorExtensions.x-unsigned}}Int64()); + {{/isLong}} + {{^isLong}} + {{^isFloat}} + {{^isDecimal}} + {{^isDouble}} + {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.TokenType == JsonTokenType.Null ? ({{#vendorExtensions.x-unsigned}}u{{/vendorExtensions.x-unsigned}}int?)null : utf8JsonReader.Get{{#vendorExtensions.x-unsigned}}U{{/vendorExtensions.x-unsigned}}Int32()); + {{/isDouble}} + {{/isDecimal}} + {{/isFloat}} + {{/isLong}} + {{/isEnum}} + {{/isNumeric}} + {{#isDate}} + {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}JsonSerializer.Deserialize<{{#supportsDateOnly}}DateOnly{{/supportsDateOnly}}{{^supportsDateOnly}}DateTime{{/supportsDateOnly}}{{#isNullable}}?{{/isNullable}}>(ref utf8JsonReader, jsonSerializerOptions)); + {{/isDate}} + {{#isDateTime}} + {{! Fix: Added support for DateTimeOffset, when `useDateTimeOffset=true` }} + {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}JsonSerializer.Deserialize(ref utf8JsonReader, jsonSerializerOptions)); + {{/isDateTime}} + {{#isEnum}} + {{^isMap}} + {{#isNumeric}} + {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.TokenType == JsonTokenType.Null ? ({{#isInnerEnum}}{{classname}}.{{/isInnerEnum}}{{{datatypeWithEnum}}}?)null : ({{#isInnerEnum}}{{classname}}.{{/isInnerEnum}}{{{datatypeWithEnum}}})utf8JsonReader.Get{{#vendorExtensions.x-unsigned}}U{{/vendorExtensions.x-unsigned}}Int32()); + {{/isNumeric}} + {{^isNumeric}} + string{{nrt?}} {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue = utf8JsonReader.GetString(); + {{^isInnerEnum}} + if ({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue != null) + {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}{{{datatypeWithEnum}}}ValueConverter.FromStringOrDefault({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue)); + {{/isInnerEnum}} + {{#isInnerEnum}} + {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}{{classname}}.{{{datatypeWithEnum}}}.FromStringOrDefault({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue)); + {{/isInnerEnum}} + {{/isNumeric}} + {{/isMap}} + {{/isEnum}} + {{#isUuid}} + {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}utf8JsonReader.TokenType == JsonTokenType.Null ? (Guid?)null : utf8JsonReader.GetGuid()); + {{/isUuid}} + {{^isUuid}} + {{^isEnum}} + {{^isString}} + {{^isBoolean}} + {{^isNumeric}} + {{^isDate}} + {{^isDateTime}} + {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} = {{>OptionProperty}}JsonSerializer.Deserialize<{{{datatypeWithEnum}}}>(ref utf8JsonReader, jsonSerializerOptions){{^isNullable}}{{nrt!}}{{/isNullable}}); + {{/isDateTime}} + {{/isDate}} + {{/isNumeric}} + {{/isBoolean}} + {{/isString}} + {{/isEnum}} + {{/isUuid}} + break; + {{/allVars}} + default: + break; + } + } + } + + {{! Required fields }} + {{#allVars}} + {{#required}} + if (!{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}.IsSet) + throw new ArgumentException("Property is required for class {{classname}}.", nameof({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}})); + + {{/required}} + {{/allVars}} + {{^vendorExtensions.x-duplicated-data-type}} + {{#model.discriminator}} + {{#model.hasDiscriminatorWithNonEmptyMapping}} + {{^model.composedSchemas.anyOf}} + {{#mappedModels}} + if ({{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}} != null) + return new {{classname}}({{#lambda.joinWithComma}}{{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}{{#vendorExtensions.x-is-value-type}}{{^isNullable}}.Value{{/isNullable}}{{/vendorExtensions.x-is-value-type}} {{#model.composedSchemas.anyOf}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{#vendorExtensions.x-is-value-type}}{{^isNullable}}.Value{{/isNullable}}{{/vendorExtensions.x-is-value-type}} {{/model.composedSchemas.anyOf}}{{#allVars}}{{^isDiscriminator}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{#required}}.Value{{nrt!}}{{^isNullable}}{{#vendorExtensions.x-is-value-type}}.Value{{/vendorExtensions.x-is-value-type}}{{/isNullable}}{{/required}} {{/isDiscriminator}}{{/allVars}}{{/lambda.joinWithComma}}); + + {{#-last}} + throw new JsonException(); + {{/-last}} + {{/mappedModels}} + {{/model.composedSchemas.anyOf}} + {{/model.hasDiscriminatorWithNonEmptyMapping}} + {{/model.discriminator}} + {{^composedSchemas.oneOf}} + {{^required}} + {{#model.composedSchemas.anyOf}} + Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}> {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}ParsedValue = {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} == null + ? default + : new Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}>({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}); + {{/model.composedSchemas.anyOf}} + {{#-last}} + + {{/-last}} + {{/required}} + return new {{classname}}({{#lambda.joinWithComma}}{{#model.composedSchemas.anyOf}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}ParsedValue{{#required}}.Value{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{nrt!}}{{/vendorExtensions.x-is-value-type}}{{/required}} {{/model.composedSchemas.anyOf}}{{#allVars}}{{^isDiscriminator}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{#required}}.Value{{nrt!}}{{^isNullable}}{{#vendorExtensions.x-is-value-type}}.Value{{nrt!}}{{/vendorExtensions.x-is-value-type}}{{/isNullable}}{{/required}} {{/isDiscriminator}}{{/allVars}}{{/lambda.joinWithComma}}); + {{/composedSchemas.oneOf}} + {{^model.discriminator}} + {{#composedSchemas}} + {{#oneOf}} + {{^vendorExtensions.x-duplicated-data-type}} + if ({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}?.Type != null) + return new {{classname}}({{#lambda.joinWithComma}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{#vendorExtensions.x-is-value-type}}.Value{{/vendorExtensions.x-is-value-type}} {{#model.composedSchemas.anyOf}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{#vendorExtensions.x-is-value-type}}{{^isNullable}}.Value{{/isNullable}}{{/vendorExtensions.x-is-value-type}} {{/model.composedSchemas.anyOf}}{{#allVars}}{{^isDiscriminator}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{#required}}ParsedValue{{/required}} {{/isDiscriminator}}{{/allVars}}{{/lambda.joinWithComma}}); + + {{/vendorExtensions.x-duplicated-data-type}} + {{#-last}} + throw new JsonException(); + {{/-last}} + {{/oneOf}} + {{/composedSchemas}} + {{/model.discriminator}} + {{/vendorExtensions.x-duplicated-data-type}} + {{/lambda.trimLineBreaks}} + {{/lambda.trimTrailingWithNewLine}} + } + + /// + /// Serializes a . + /// + /// + /// + /// + /// + public override void Write(Utf8JsonWriter writer, {{classname}} {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}, JsonSerializerOptions jsonSerializerOptions) + { + {{#lambda.trimLineBreaks}} + {{#lambda.copyText}} + {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}} + {{/lambda.copyText}} + {{#discriminator}} + {{#children}} + if ({{#lambda.paste}}{{/lambda.paste}} is {{classname}} {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}){ + JsonSerializer.Serialize<{{{classname}}}>(writer, {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}, jsonSerializerOptions); + return; + } + + {{/children}} + {{/discriminator}} + {{! start | support oneOf without discriminator.mapping property - WriteProperties }} + {{^model.discriminator}} + {{#composedSchemas.oneOf}} + if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} != null) + JsonSerializer.Serialize(writer, {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}, jsonSerializerOptions); + {{/composedSchemas.oneOf}} + {{/model.discriminator}} + {{! end | oneOf support without discriminator.mapping property - WriteProperties }} + {{! start | support oneOf without discriminator.mapping property- WriteStartObject }} + {{^model.discriminator}}{{#oneOf}}{{#-first}}/* {{/-first}}{{/oneOf}}{{/model.discriminator}} + writer.WriteStartObject(); + {{^model.discriminator}}{{#oneOf}}{{#-first}} */{{/-first}}{{/oneOf}}{{/model.discriminator}} + {{! end | support oneOf without discriminator.mapping property - WriteStartObject }} + {{#model.discriminator}} + {{#model.hasDiscriminatorWithNonEmptyMapping}} + {{#composedSchemas.oneOf}} + {{^vendorExtensions.x-duplicated-data-type}} + if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} != null) + {{#isPrimitiveType}} + {{#isString}} + writer.WriteString("{{vendorExtensions.x-base-name}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}Option.Value); + {{/isString}} + {{#isBoolean}} + writer.WriteBoolean("{{vendorExtensions.x-base-name}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}Option.Value.Value); + {{/isBoolean}} + {{#isNumeric}} + writer.WriteNumber("{{vendorExtensions.x-base-name}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}Option.Value.Value); + {{/isNumeric}} + {{/isPrimitiveType}} + {{^isPrimitiveType}} + { + {{baseType}}JsonConverter {{#lambda.camelcase_sanitize_param}}{{baseType}}JsonConverter{{/lambda.camelcase_sanitize_param}} = ({{baseType}}JsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}.GetType())); + {{#lambda.camelcase_sanitize_param}}{{baseType}}JsonConverter{{/lambda.camelcase_sanitize_param}}.WriteProperties(writer, {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}, jsonSerializerOptions); + } + {{/isPrimitiveType}} + + {{/vendorExtensions.x-duplicated-data-type}} + {{/composedSchemas.oneOf}} + {{/model.hasDiscriminatorWithNonEmptyMapping}} + {{/model.discriminator}} + {{^model.discriminator}} + {{#composedSchemas}} + {{#anyOf}} + if ({{#lambda.joinWithAmpersand}}{{^required}}{{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}Option.IsSet {{/required}}{{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{^required}}Option.Value{{/required}} != null{{/lambda.joinWithAmpersand}}) + {{#isPrimitiveType}} + {{#isString}} + writer.WriteString("{{vendorExtensions.x-base-name}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}Option.Value); + {{/isString}} + {{#isBoolean}} + writer.WriteBoolean("{{vendorExtensions.x-base-name}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}Option.Value.Value); + {{/isBoolean}} + {{#isNumeric}} + writer.WriteNumber("{{vendorExtensions.x-base-name}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}Option.Value.Value); + {{/isNumeric}} + {{#isEnum}} + { + {{datatypeWithEnum}}JsonConverter {{#lambda.camelcase}}{{datatypeWithEnum}}{{/lambda.camelcase}}JsonConverter = ({{datatypeWithEnum}}JsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert({{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{^required}}Option.Value{{/required}}.GetType())); + {{#lambda.camelcase}}{{datatypeWithEnum}}{{/lambda.camelcase}}JsonConverter.Write{{^isEnumRef}}Properties{{/isEnumRef}}(writer, {{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}, jsonSerializerOptions); + } + {{/isEnum}} + {{/isPrimitiveType}} + {{^isPrimitiveType}} + { + {{datatypeWithEnum}}JsonConverter {{#lambda.camelcase}}{{datatypeWithEnum}}{{/lambda.camelcase}}JsonConverter = ({{datatypeWithEnum}}JsonConverter) jsonSerializerOptions.Converters.First(c => c.CanConvert({{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{^required}}Option.Value{{/required}}.GetType())); + {{#lambda.camelcase}}{{datatypeWithEnum}}{{/lambda.camelcase}}JsonConverter.Write{{^isEnumRef}}Properties{{/isEnumRef}}(writer, {{#lambda.camelcase_sanitize_param}}{{model.classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}, jsonSerializerOptions); + } + {{/isPrimitiveType}} + + {{/anyOf}} + {{/composedSchemas}} + {{/model.discriminator}} + WriteProperties(writer, {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}, jsonSerializerOptions); + {{! start | support oneOf without discriminator.mapping property- WriteEndObject }} + {{^model.discriminator}}{{#oneOf}}{{#-first}}/* {{/-first}}{{/oneOf}}{{/model.discriminator}} + writer.WriteEndObject(); + {{^model.discriminator}}{{#oneOf}}{{#-first}} */{{/-first}}{{/oneOf}}{{/model.discriminator}} + {{! end | support oneOf without discriminator.mapping property - WriteEndObject }} + {{/lambda.trimLineBreaks}} + } + + /// + /// Serializes the properties of . + /// + /// + /// + /// + /// + public void WriteProperties(Utf8JsonWriter writer, {{classname}} {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}, JsonSerializerOptions jsonSerializerOptions) + { + {{#lambda.trimTrailingWithNewLine}} + {{#lambda.trimLineBreaks}} + + {{#allVars}} + {{#isDiscriminator}} + {{^model.composedSchemas.anyOf}} + {{^model.composedSchemas.oneOf}} + if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} != null) + writer.WriteString("{{baseName}}", {{^isEnum}}{{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{/isEnum}}{{#isEnum}}{{#isInnerEnum}}{{classname}}.{{{datatypeWithEnum}}}.ToJsonValue{{/isInnerEnum}}{{^isInnerEnum}}{{{datatypeWithEnum}}}ValueConverter.ToJsonValue{{/isInnerEnum}}({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{^required}}{{/required}}){{/isEnum}}); + + {{/model.composedSchemas.oneOf}} + {{/model.composedSchemas.anyOf}} + {{/isDiscriminator}} + {{^isDiscriminator}} + {{#isString}} + {{^isMap}} + {{^isEnum}} + {{^isUuid}} + {{#lambda.copyText}} + {{#isDecimal}} + if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} != null) + writer.WriteString("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}.ToString()); + {{/isDecimal}} + {{^isDecimal}} + if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} != null) + writer.WriteString("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}); + {{/isDecimal}} + {{/lambda.copyText}} + {{#lambda.indent3}} + {{>WriteProperty}}{{! prevent indent}} + {{/lambda.indent3}} + {{/isUuid}} + {{/isEnum}} + {{/isMap}} + {{/isString}} + {{#isBoolean}} + {{#lambda.copyText}} + writer.WriteBoolean("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{^required}}_{{/required}}{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}{{#required}}{{#isNullable}}.Value{{/isNullable}}{{/required}}); + {{/lambda.copyText}} + {{#lambda.indent3}} + {{>WriteProperty}}{{! prevent indent}} + {{/lambda.indent3}} + {{/isBoolean}} + {{^isEnum}} + {{#isNumeric}} + {{#lambda.copyText}} + writer.WriteNumber("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{^required}}_{{/required}}{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}{{#required}}{{#isNullable}}.Value{{/isNullable}}{{/required}}); + {{/lambda.copyText}} + {{#lambda.indent3}} + {{>WriteProperty}}{{! prevent indent}} + {{/lambda.indent3}} + {{/isNumeric}} + {{/isEnum}} + {{#isDate}} + {{#lambda.copyText}} + writer.WriteString("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{^required}}_{{/required}}{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}{{#required}}{{#isNullable}}.Value{{/isNullable}}{{/required}}.ToString({{name}}Format)); + {{/lambda.copyText}} + {{#lambda.indent3}} + {{>WriteProperty}}{{! prevent indent}} + {{/lambda.indent3}} + {{/isDate}} + {{#isDateTime}} + {{#lambda.copyText}} + writer.WriteString("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{^required}}_{{/required}}{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}{{#required}}{{#isNullable}}.Value{{/isNullable}}{{/required}}.ToString({{name}}Format)); + {{/lambda.copyText}} + {{#lambda.indent3}} + {{>WriteProperty}}{{! prevent indent}} + {{/lambda.indent3}} + {{/isDateTime}} + {{#isEnum}} + {{#isNumeric}} + {{#lambda.copyText}} + writer.WriteNumber("{{baseName}}", {{#isInnerEnum}}{{classname}}.{{/isInnerEnum}}{{{datatypeWithEnum}}}.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{^required}}_{{/required}}{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}{{#required}}{{#isNullable}}.Value{{/isNullable}}{{/required}})); + {{/lambda.copyText}} + {{#lambda.indent3}} + {{>WriteProperty}}{{! prevent indent}} + {{/lambda.indent3}} + {{/isNumeric}} + {{^isMap}} + {{^isNumeric}} + {{#isInnerEnum}} + {{#isNullable}} + var {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue = {{classname}}.{{{datatypeWithEnum}}}.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{^required}}_{{/required}}{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}{{#required}}{{#isNullable}}{{nrt!}}.Value{{/isNullable}}{{/required}}); + if ({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue != null) + writer.WriteString("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue); + else + writer.WriteNull("{{baseName}}"); + + {{/isNullable}} + {{^isNullable}} + if ({{^required}}{{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}._{{name}}Option.IsSet && {{/required}}{{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} != null) {{! - }} + { + string? {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue = {{classname}}.{{{datatypeWithEnum}}}.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{^required}}_{{/required}}{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}{{#required}}{{#isNullable}}{{nrt!}}.Value{{/isNullable}}{{/required}}); + writer.WriteString("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue); + } + + {{/isNullable}} + {{/isInnerEnum}} + {{^isInnerEnum}} + {{#lambda.copyText}} + {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}} + {{/lambda.copyText}} + {{#required}} + {{#isNullable}} + if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} == null) + writer.WriteNull("{{baseName}}"); + else + { + var {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue = {{{datatypeWithEnum}}}ValueConverter.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}.Value); + {{#allowableValues}} + {{#enumVars}} + {{#-first}} + {{#isString}} + if ({{#lambda.paste}}{{/lambda.paste}}RawValue != null){{! we cant use name here because enumVar also has a name property, so use the paste lambda instead }} + writer.WriteString("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{nameInPascalCase}}{{/lambda.camelcase_sanitize_param}}RawValue); + else + writer.WriteNull("{{baseName}}"); + {{/isString}} + {{^isString}} + writer.WriteNumber("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{nameInPascalCase}}{{/lambda.camelcase_sanitize_param}}RawValue); + {{/isString}} + {{/-first}} + {{/enumVars}} + {{/allowableValues}} + } + {{/isNullable}} + {{^isNullable}} + var {{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}RawValue = {{{datatypeWithEnum}}}ValueConverter.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}); + {{#allowableValues}} + {{#enumVars}} + {{#-first}} + {{^isNumeric}} + writer.WriteString("{{baseName}}", {{#lambda.paste}}{{/lambda.paste}}RawValue); + {{/isNumeric}} + {{#isNumeric}} + writer.WriteNumber("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{#lambda.paste}}{{/lambda.paste}}{{/lambda.camelcase_sanitize_param}}RawValue); + {{/isNumeric}} + {{/-first}} + {{/enumVars}} + {{/allowableValues}} + {{/isNullable}} + + {{/required}} + {{^required}} + if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}._{{name}}Option.IsSet) + {{#isNullable}} + if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}._{{name}}Option{{nrt!}}.Value != null) + { + var {{#lambda.paste}}{{/lambda.paste}}RawValue = {{{datatypeWithEnum}}}ValueConverter.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}._{{name}}Option.Value{{nrt!}}.Value); + writer.{{#lambda.first}}{{#allowableValues}}{{#enumVars}}{{^isNumeric}}WriteString {{/isNumeric}}{{#isNumeric}}WriteNumber {{/isNumeric}}{{/enumVars}}{{/allowableValues}}{{/lambda.first}}("{{baseName}}", {{#lambda.paste}}{{/lambda.paste}}RawValue); + } + else + writer.WriteNull("{{baseName}}"); + {{/isNullable}} + {{^isNullable}} + { + var {{#lambda.paste}}{{/lambda.paste}}RawValue = {{{datatypeWithEnum}}}ValueConverter.ToJsonValue({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}{{nrt!}}.Value); + writer.{{#lambda.first}}{{#allowableValues}}{{#enumVars}}{{^isNumeric}}WriteString {{/isNumeric}}{{#isNumeric}}WriteNumber {{/isNumeric}}{{/enumVars}}{{/allowableValues}}{{/lambda.first}}("{{baseName}}", {{#lambda.paste}}{{/lambda.paste}}RawValue); + } + {{/isNullable}} + {{/required}} + {{/isInnerEnum}} + {{/isNumeric}} + {{/isMap}} + {{/isEnum}} + {{#isUuid}} + {{#lambda.copyText}} + writer.WriteString("{{baseName}}", {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{^required}}_{{/required}}{{name}}{{^required}}Option.Value{{#vendorExtensions.x-is-value-type}}{{nrt!}}.Value{{/vendorExtensions.x-is-value-type}}{{/required}}{{#required}}{{#isNullable}}.Value{{/isNullable}}{{/required}}); + {{/lambda.copyText}} + {{#lambda.indent3}} + {{>WriteProperty}}{{! prevent indent}} + {{/lambda.indent3}} + {{/isUuid}} + {{^isUuid}} + {{^isEnum}} + {{^isString}} + {{^isBoolean}} + {{^isNumeric}} + {{^isDate}} + {{^isDateTime}} + {{#required}} + {{#isNullable}} + if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}} != null) + { + writer.WritePropertyName("{{baseName}}"); + JsonSerializer.Serialize(writer, {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}, jsonSerializerOptions); + } + else + writer.WriteNull("{{baseName}}"); + {{/isNullable}} + {{^isNullable}} + writer.WritePropertyName("{{baseName}}"); + JsonSerializer.Serialize(writer, {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}, jsonSerializerOptions); + {{/isNullable}} + {{/required}} + {{^required}} + if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}._{{name}}Option.IsSet) + {{#isNullable}} + if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}._{{name}}Option.Value != null) + { + writer.WritePropertyName("{{baseName}}"); + JsonSerializer.Serialize(writer, {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}, jsonSerializerOptions); + } + else + writer.WriteNull("{{baseName}}"); + {{/isNullable}} + {{^isNullable}} + { + writer.WritePropertyName("{{baseName}}"); + JsonSerializer.Serialize(writer, {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{name}}, jsonSerializerOptions); + } + {{/isNullable}} + {{/required}} + {{/isDateTime}} + {{/isDate}} + {{/isNumeric}} + {{/isBoolean}} + {{/isString}} + {{/isEnum}} + {{/isUuid}} + {{/isDiscriminator}} + {{/allVars}} + {{/lambda.trimLineBreaks}} + {{/lambda.trimTrailingWithNewLine}} + } + } \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/JsonSerializerOptionsProvider.mustache b/templates-v7/csharp/libraries/generichost/JsonSerializerOptionsProvider.mustache new file mode 100644 index 000000000..7e5cf9cb8 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/JsonSerializerOptionsProvider.mustache @@ -0,0 +1,29 @@ +// +{{partial_header}} +{{#nrt}} +#nullable enable + +{{/nrt}} +using System.Text.Json; + +namespace {{packageName}}.{{apiName}}.{{clientPackage}} +{ + /// + /// Provides the JsonSerializerOptions. + /// + {{>visibility}} class JsonSerializerOptionsProvider + { + /// + /// The JsonSerializerOptions. + /// + public JsonSerializerOptions Options { get; } + + /// + /// Instantiates a JsonSerializerOptionsProvider to access the JsonSerializerOptions. + /// + public JsonSerializerOptionsProvider(JsonSerializerOptions options) + { + Options = options; + } + } +} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/ModelBaseSignature.mustache b/templates-v7/csharp/libraries/generichost/ModelBaseSignature.mustache new file mode 100644 index 000000000..909a68e35 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/ModelBaseSignature.mustache @@ -0,0 +1 @@ +{{#parentModel.composedSchemas.anyOf}}{{#lambda.camelcase_sanitize_param}}{{parent}}{{/lambda.camelcase_sanitize_param}}.{{#lambda.titlecase}}{{baseType}}{{#isArray}}{{{dataFormat}}}{{/isArray}}{{/lambda.titlecase}} {{/parentModel.composedSchemas.anyOf}}{{#allVars}}{{^isDiscriminator}}{{#isInherited}}{{^isNew}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{/isNew}}{{#isNew}}{{#isEnum}}{{#isInnerEnum}}{{classname}}.{{{datatypeWithEnum}}}ToJsonValue{{/isInnerEnum}}{{^isInnerEnum}}{{{datatypeWithEnum}}}ValueConverter.ToJsonValue{{/isInnerEnum}}({{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}{{^required}}.Value{{/required}}){{/isEnum}}{{^isEnum}}{{#lambda.camelcase_sanitize_param}}{{name}}{{/lambda.camelcase_sanitize_param}}.ToString(){{/isEnum}}{{/isNew}} {{/isInherited}}{{/isDiscriminator}}{{/allVars}} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/ModelSignature.mustache b/templates-v7/csharp/libraries/generichost/ModelSignature.mustache new file mode 100644 index 000000000..09a41b278 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/ModelSignature.mustache @@ -0,0 +1 @@ +{{#model.allVars}}{{^isDiscriminator}}{{^required}}Option<{{/required}}{{{datatypeWithEnum}}}{{>NullConditionalProperty}}{{^required}}>{{/required}} {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}}{{#defaultValue}}{{^isEnum}} = {{^required}}default{{/required}}{{#required}}{{^isDateTime}}{{#isString}}{{^isEnum}}@{{/isEnum}}{{/isString}}{{{.}}}{{/isDateTime}}{{#isDateTime}}default{{/isDateTime}}{{/required}}{{/isEnum}}{{#isEnum}} = default{{/isEnum}}{{/defaultValue}}{{^defaultValue}}{{#lambda.first}}{{#isNullable}} = default {{/isNullable}}{{^required}} = default {{/required}}{{/lambda.first}}{{/defaultValue}} {{/isDiscriminator}}{{/model.allVars}} diff --git a/templates-v7/csharp/libraries/generichost/OAuthToken.mustache b/templates-v7/csharp/libraries/generichost/OAuthToken.mustache new file mode 100644 index 000000000..23b3cab91 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/OAuthToken.mustache @@ -0,0 +1,41 @@ +// +{{partial_header}} +{{#nrt}} +#nullable enable + +{{/nrt}} +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace {{packageName}}.{{clientPackage}} +{ + /// + /// A token constructed with OAuth. + /// + {{>visibility}} class OAuthToken : TokenBase + { + private string _raw; + + /// + /// Consturcts an OAuthToken object. + /// + /// + /// + public OAuthToken(string value, TimeSpan? timeout = null) : base(timeout) + { + _raw = value; + } + + /// + /// Places the token in the header. + /// + /// + /// + public virtual void UseInHeader(global::System.Net.Http.HttpRequestMessage request, string headerName) + { + request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _raw); + } + } +} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/OnDeserializationError.mustache b/templates-v7/csharp/libraries/generichost/OnDeserializationError.mustache new file mode 100644 index 000000000..ff83a5076 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/OnDeserializationError.mustache @@ -0,0 +1,2 @@ +if (!suppressDefaultLog) + Logger.LogError(exception, "An error occurred while deserializing the {code} response.", httpStatusCode); \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/OperationSignature.mustache b/templates-v7/csharp/libraries/generichost/OperationSignature.mustache new file mode 100644 index 000000000..caa9d144c --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/OperationSignature.mustache @@ -0,0 +1 @@ +{{#lambda.joinWithComma}}{{#allParams}}{{#required}}{{{dataType}}}{{>NullConditionalParameter}}{{/required}}{{^required}}Option<{{{dataType}}}{{>NullConditionalParameter}}>{{/required}} {{paramName}}{{#notRequiredOrIsNullable}} = default{{/notRequiredOrIsNullable}} {{/allParams}}System.Threading.CancellationToken cancellationToken = default{{^netstandard20OrLater}}(global::System.Threading.CancellationToken){{/netstandard20OrLater}}{{/lambda.joinWithComma}} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/Option.mustache b/templates-v7/csharp/libraries/generichost/Option.mustache new file mode 100644 index 000000000..8b33bdb51 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/Option.mustache @@ -0,0 +1,48 @@ +// +{{>partial_header}} + +{{#nrt}} +#nullable enable + +{{/nrt}} + +namespace {{packageName}}.{{clientPackage}} +{ + /// + /// A wrapper for operation parameters which are not required + /// + public struct Option + { + /// + /// The value to send to the server + /// + public TType Value { get; } + + /// + /// When true the value will be sent to the server + /// + internal bool IsSet { get; } + + /// + /// A wrapper for operation parameters which are not required + /// + /// + public Option(TType value) + { + IsSet = true; + Value = value; + } + + /// + /// Implicitly converts this option to the contained type + /// + /// + public static implicit operator TType(Option option) => option.Value; + + /// + /// Implicitly converts the provided value to an Option + /// + /// + public static implicit operator Option(TType value) => new Option(value); + } +} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/OptionProperty.mustache b/templates-v7/csharp/libraries/generichost/OptionProperty.mustache new file mode 100644 index 000000000..d75041887 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/OptionProperty.mustache @@ -0,0 +1 @@ +new Option<{{#isInnerEnum}}{{^isMap}}{{classname}}.{{/isMap}}{{/isInnerEnum}}{{{datatypeWithEnum}}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}}>( \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/README.client.mustache b/templates-v7/csharp/libraries/generichost/README.client.mustache new file mode 100644 index 000000000..247cbe399 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/README.client.mustache @@ -0,0 +1,140 @@ +# Created with Openapi Generator + + +## Creating the library +Create a config.yaml file similar to what is below, then run the following powershell command to generate the library `java -jar "/openapi-generator/modules/openapi-generator-cli/target/openapi-generator-cli.jar" generate -c config.yaml` + +```yaml +generatorName: csharp +inputSpec: {{inputSpec}} +outputDir: out + +# https://openapi-generator.tech/docs/generators/csharp +additionalProperties: + packageGuid: '{{packageGuid}}' + +# https://openapi-generator.tech/docs/integrations/#github-integration +# gitHost: +# gitUserId: +# gitRepoId: + +# https://openapi-generator.tech/docs/globals +# globalProperties: + +# https://openapi-generator.tech/docs/customization/#inline-schema-naming +# inlineSchemaOptions: + +# https://openapi-generator.tech/docs/customization/#name-mapping +# modelNameMappings: +# nameMappings: + +# https://openapi-generator.tech/docs/customization/#openapi-normalizer +# openapiNormalizer: + +# templateDir: https://openapi-generator.tech/docs/templating/#modifying-templates + +# releaseNote: +``` + + +## Using the library in your project + +```cs +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using {{packageName}}.Api; +using {{packageName}}.Client; +using {{packageName}}.Model; +using Org.OpenAPITools.Extensions; + +namespace YourProject +{ + public class Program + { + public static async Task Main(string[] args) + { + var host = CreateHostBuilder(args).Build(); + {{#apiInfo}} + {{#apis}} + {{#-first}} + var api = host.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>(); + {{#operations}} + {{#-first}} + {{#operation}} + {{#-first}} + {{interfacePrefix}}{{operationId}}ApiResponse apiResponse = await api.{{operationId}}Async("todo"); + {{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}object{{/returnType}}{{nrt?}} model = apiResponse.Ok(); + {{/-first}} + {{/operation}} + {{/-first}} + {{/operations}} + {{/-first}} + {{/apis}} + {{/apiInfo}} + } + + public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) + .Configure{{apiName}}((context, services, options) => + { + {{#authMethods}} + {{#-first}} + // The type of token here depends on the api security specifications + // Available token types are ApiKeyToken, BearerToken, HttpSigningToken, and OAuthToken. + BearerToken token = new(""); + options.AddTokens(token); + + // optionally choose the method the tokens will be provided with, default is RateLimitProvider + options.UseProvider, BearerToken>(); + + {{/-first}} + {{/authMethods}} + options.ConfigureJsonOptions((jsonOptions) => + { + // your custom converters if any + }); + + options.Add{{apiName}}HttpClients(client => + { + // client configuration + }, builder => + { + builder + .AddRetryPolicy(2) + .AddTimeoutPolicy(TimeSpan.FromSeconds(5)) + .AddCircuitBreakerPolicy(10, TimeSpan.FromSeconds(30)); + // add whatever middleware you prefer + } + ); + }); + } +} +``` + +## Questions + +- What about HttpRequest failures and retries? + Configure Polly in the IHttpClientBuilder +- How are tokens used? + Tokens are provided by a TokenProvider class. The default is RateLimitProvider which will perform client side rate limiting. + Other providers can be used with the UseProvider method. +- Does an HttpRequest throw an error when the server response is not Ok? + It depends how you made the request. If the return type is ApiResponse no error will be thrown, though the Content property will be null. + StatusCode and ReasonPhrase will contain information about the error. + If the return type is T, then it will throw. If the return type is TOrDefault, it will return null. +- How do I validate requests and process responses? + Use the provided On and After partial methods in the api classes. + +## Api Information +- appName: {{appName}} +- appVersion: {{appVersion}} +- appDescription: {{appDescription}} + +## Build +This C# SDK is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project. + +- SDK version: {{packageVersion}} +{{^hideGenerationTimestamp}} +- Build date: {{generatedDate}} +{{/hideGenerationTimestamp}} +- Generator version: {{generatorVersion}} +- Build package: {{generatorClass}} diff --git a/templates-v7/csharp/libraries/generichost/SourceGenerationContext.mustache b/templates-v7/csharp/libraries/generichost/SourceGenerationContext.mustache new file mode 100644 index 000000000..b1798c98e --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/SourceGenerationContext.mustache @@ -0,0 +1,9 @@ +{{#useSourceGeneration}} + + /// + /// The {{classname}}SerializationContext + /// + [JsonSourceGenerationOptions(WriteIndented = true, GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] + [JsonSerializable(typeof({{classname}}))] + {{>visibility}} partial class {{classname}}SerializationContext : JsonSerializerContext { } +{{/useSourceGeneration}} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/TokenBase.mustache b/templates-v7/csharp/libraries/generichost/TokenBase.mustache new file mode 100644 index 000000000..05f06b4a7 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/TokenBase.mustache @@ -0,0 +1,73 @@ +// +{{partial_header}} +{{#nrt}} +#nullable enable + +{{/nrt}} +using System; + +namespace {{packageName}}.{{clientPackage}} +{ + /// + /// The base for all tokens. + /// + {{>visibility}} abstract class TokenBase + { + private DateTime _nextAvailable = DateTime.UtcNow; + private object _nextAvailableLock = new object(); + private readonly System.Timers.Timer _timer = new System.Timers.Timer(); + + + internal TimeSpan? Timeout { get; set; } + internal delegate void TokenBecameAvailableEventHandler(object sender); + internal event TokenBecameAvailableEventHandler{{nrt?}} TokenBecameAvailable; + + + /// + /// Initialize a TokenBase object. + /// + /// + internal TokenBase(TimeSpan? timeout = null) + { + Timeout = timeout; + + if (Timeout != null) + StartTimer(Timeout.Value); + } + + + /// + /// Starts the token's timer + /// + /// + internal void StartTimer(TimeSpan timeout) + { + Timeout = timeout; + _timer.Interval = Timeout.Value.TotalMilliseconds; + _timer.Elapsed += OnTimer; + _timer.AutoReset = true; + _timer.Start(); + } + + /// + /// Returns true while the token is rate limited. + /// + public bool IsRateLimited => _nextAvailable > DateTime.UtcNow; + + /// + /// Triggered when the server returns status code TooManyRequests + /// Once triggered the local timeout will be extended an arbitrary length of time. + /// + public void BeginRateLimit() + { + lock(_nextAvailableLock) + _nextAvailable = DateTime.UtcNow.AddSeconds(5); + } + + private void OnTimer(object{{nrt?}} sender, System.Timers.ElapsedEventArgs e) + { + if (TokenBecameAvailable != null && !IsRateLimited) + TokenBecameAvailable.Invoke(this); + } + } +} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/TokenContainer.mustache b/templates-v7/csharp/libraries/generichost/TokenContainer.mustache new file mode 100644 index 000000000..24c84a551 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/TokenContainer.mustache @@ -0,0 +1,39 @@ +// +{{partial_header}} +{{#nrt}} +#nullable enable + +{{/nrt}} +using System.Linq; +using System.Collections.Generic; + +namespace {{packageName}}.{{clientPackage}} +{ + /// + /// A container for a collection of tokens. + /// + /// + {{>visibility}} sealed class TokenContainer where TTokenBase : TokenBase + { + /// + /// The collection of tokens + /// + public List Tokens { get; } = new List(); + + /// + /// Instantiates a TokenContainer + /// + public TokenContainer() + { + } + + /// + /// Instantiates a TokenContainer + /// + /// + public TokenContainer(global::System.Collections.Generic.IEnumerable tokens) + { + Tokens = tokens.ToList(); + } + } +} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/TokenProvider.mustache b/templates-v7/csharp/libraries/generichost/TokenProvider.mustache new file mode 100644 index 000000000..9fc5f3ea0 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/TokenProvider.mustache @@ -0,0 +1,39 @@ +// +{{>partial_header}} + +{{#nrt}} +#nullable enable + +{{/nrt}} +using System; +using System.Linq; +using System.Collections.Generic; +using {{packageName}}.{{clientPackage}}; + +namespace {{packageName}} +{ + /// + /// A class which will provide tokens. + /// + {{>visibility}} abstract class TokenProvider where TTokenBase : TokenBase + { + /// + /// The array of tokens. + /// + protected TTokenBase[] _tokens; + + internal abstract System.Threading.Tasks.ValueTask GetAsync(string header = "", System.Threading.CancellationToken cancellation = default{{^netstandard20OrLater}}(global::System.Threading.CancellationToken){{/netstandard20OrLater}}); + + /// + /// Instantiates a TokenProvider. + /// + /// + public TokenProvider(IEnumerable tokens) + { + _tokens = tokens.ToArray(); + + if (_tokens.Length == 0) + throw new ArgumentException("You did not provide any tokens."); + } + } +} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/ValidateRegex.mustache b/templates-v7/csharp/libraries/generichost/ValidateRegex.mustache new file mode 100644 index 000000000..8918b8cf4 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/ValidateRegex.mustache @@ -0,0 +1,7 @@ +// {{{name}}} ({{{dataType}}}) pattern +Regex regex{{{name}}} = new Regex(@"{{{vendorExtensions.x-regex}}}"{{#vendorExtensions.x-modifiers}}{{#-first}}, {{/-first}}RegexOptions.{{{.}}}{{^-last}} | {{/-last}}{{/vendorExtensions.x-modifiers}}); +{{#lambda.copy}}this.{{{name}}}{{#useGenericHost}}{{^required}}Option.Value{{/required}}{{/useGenericHost}} != null && {{/lambda.copy}} +if ({{#lambda.first}}{{^required}}{{#lambda.paste}}{{/lambda.paste}} {{/required}}{{#isNullable}}{{#lambda.paste}}{{/lambda.paste}}{{/isNullable}}{{/lambda.first}}!regex{{{name}}}.Match(this.{{{name}}}{{#useGenericHost}}{{^required}}Option.Value{{/required}}{{/useGenericHost}}{{#isUuid}}.ToString(){{#lambda.first}}{{^required}}{{nrt!}} {{/required}}{{#isNullable}}! {{/isNullable}}{{/lambda.first}}{{/isUuid}}).Success) +{ + yield return new System.ComponentModel.DataAnnotations.ValidationResult("Invalid value for {{{name}}}, must match a pattern of " + regex{{{name}}}, new [] { "{{{name}}}" }); +} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/WebhookHandler.mustache b/templates-v7/csharp/libraries/generichost/WebhookHandler.mustache new file mode 100644 index 000000000..19006ee83 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/WebhookHandler.mustache @@ -0,0 +1,64 @@ +{{#lambda.trimLineBreaks}} +// +{{>partial_header}} + +{{#nrt}} +#nullable enable + +{{/nrt}} +using Microsoft.Extensions.Logging; +using System.Text.Json; +using {{packageName}}.{{modelPackage}}; + +namespace {{packageName}}.{{apiName}}.Handlers +{ + /// + /// Interface for deserializing webhooks. + /// + {{>visibility}} interface {{interfacePrefix}}{{apiName}}Handler + { + /// + /// Returns the to deserialize the json payload from the webhook. + /// + {{packageName}}.{{apiName}}.{{clientPackage}}.JsonSerializerOptionsProvider JsonSerializerOptionsProvider { get; } + {{#models}} + {{#model.vendorExtensions.x-webhook-root}} + + /// + /// Uses to attempt to deserialize . + /// + /// + {{model.name}}? Deserialize{{model.name}}(string json); + {{/model.vendorExtensions.x-webhook-root}} + {{/models}} + } + + /// + /// Handler utility function used to deserialize {{apiName}}. + /// + {{>visibility}} partial class {{apiName}}Handler : {{interfacePrefix}}{{apiName}}Handler + { + /// + public {{packageName}}.{{apiName}}.{{clientPackage}}.JsonSerializerOptionsProvider JsonSerializerOptionsProvider { get; } + + /// + /// Initializes the handler utility for deserializing {{apiName}}. + /// + public {{apiName}}Handler({{packageName}}.{{apiName}}.{{clientPackage}}.JsonSerializerOptionsProvider jsonSerializerOptionsProvider) + { + JsonSerializerOptionsProvider = jsonSerializerOptionsProvider; + } + + {{#models}} + {{#model.vendorExtensions.x-webhook-root}} + /// + public {{model.name}}? Deserialize{{model.name}}(string json) + { + return JsonSerializer.Deserialize<{{model.name}}>(json, JsonSerializerOptionsProvider.Options); + } + + {{/model.vendorExtensions.x-webhook-root}} + {{/models}} + } +} +{{/lambda.trimLineBreaks}} diff --git a/templates-v7/csharp/libraries/generichost/WriteProperty.mustache b/templates-v7/csharp/libraries/generichost/WriteProperty.mustache new file mode 100644 index 000000000..8fe669906 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/WriteProperty.mustache @@ -0,0 +1,10 @@ +{{#required}} +{{>WritePropertyHelper}} + +{{/required}} +{{^required}} +if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}._{{name}}Option.IsSet) + {{#lambda.indent1}} + {{>WritePropertyHelper}}{{! prevent indent}} + {{/lambda.indent1}} +{{/required}} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/WritePropertyHelper.mustache b/templates-v7/csharp/libraries/generichost/WritePropertyHelper.mustache new file mode 100644 index 000000000..04270093a --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/WritePropertyHelper.mustache @@ -0,0 +1,10 @@ +{{#isNullable}} +if ({{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{^required}}_{{/required}}{{name}}{{^required}}Option.Value{{/required}} != null) + {{#lambda.paste}}{{/lambda.paste}} +else + writer.WriteNull("{{baseName}}"); +{{/isNullable}} +{{^isNullable}} +{{#lambda.paste}} +{{/lambda.paste}} +{{/isNullable}} \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/api.mustache b/templates-v7/csharp/libraries/generichost/api.mustache new file mode 100644 index 000000000..82eb96f3d --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/api.mustache @@ -0,0 +1,684 @@ +{{#lambda.trimLineBreaks}} +// +{{>partial_header}} + +{{#nrt}} +#nullable enable + +{{/nrt}} +using System; +using System.Collections.Generic; +{{#net80OrLater}} +{{#lambda.uniqueLines}} +{{#operations}} +{{#operation}} +{{#vendorExtensions.x-set-cookie}} +using System.Linq; +{{/vendorExtensions.x-set-cookie}} +{{/operation}} +{{/operations}} +{{/lambda.uniqueLines}} +{{/net80OrLater}} +using System.Net; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.Json; +using {{packageName}}.{{corePackageName}}; +using {{packageName}}.{{corePackageName}}.Auth; +using {{packageName}}.{{corePackageName}}.Client; +using {{packageName}}.{{corePackageName}}.Client.Extensions; +using {{packageName}}.{{apiName}}.{{clientPackage}}; +{{#hasImport}} +using {{packageName}}.{{modelPackage}}; +{{/hasImport}} +{{^netStandard}} +using System.Diagnostics.CodeAnalysis; +{{/netStandard}} + +namespace {{packageName}}.{{apiPackage}} +{ + {{#operations}} + /// + /// Represents a collection of functions to interact with the API endpoints. + /// This class is registered as transient. + /// + {{>visibility}} interface {{interfacePrefix}}{{classname}} : {{interfacePrefix}}{{packageName}}ApiService + { + /// + /// The class containing the events. + /// + {{classname}}Events Events { get; } + + {{#operation}} + /// + /// {{summary}} + /// + /// + /// {{notes}} + /// + /// Thrown when fails to make API call. + {{#allParams}} + /// {{description}}{{#isDeprecated}} (deprecated){{/isDeprecated}} + {{/allParams}} + /// . + /// of . + {{#isDeprecated}} + [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] + {{/isDeprecated}} + Task<{{interfacePrefix}}{{operationId}}ApiResponse> {{operationId}}Async({{>OperationSignature}}); + + {{/operation}} + } + {{#operation}} + {{^vendorExtensions.x-duplicates}} + {{#responses}} + {{#-first}} + + /// + /// The . + /// **Usage:** Use `.TryDeserializeOk(out var result)` to get the result from the API: + /// . + /// + {{>visibility}} interface {{interfacePrefix}}{{operationId}}ApiResponse : {{#lambda.joinWithComma}}{{packageName}}.{{corePackageName}}.{{clientPackage}}.{{interfacePrefix}}ApiResponse {{#responses}}{{#dataType}}{{interfacePrefix}}{{vendorExtensions.x-http-status}}<{{#isModel}}{{^containerType}}{{packageName}}.{{modelPackage}}.{{/containerType}}{{/isModel}}{{{dataType}}}{{#nrt}}?{{/nrt}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}}> {{/dataType}}{{/responses}}{{/lambda.joinWithComma}} + { + {{#responses}} + {{#vendorExtensions.x-http-status-is-default}} + /// + /// Returns true if the response is the default response type. + /// + /// + bool Is{{vendorExtensions.x-http-status}} { get; } + {{/vendorExtensions.x-http-status-is-default}} + {{^vendorExtensions.x-http-status-is-default}} + /// + /// Returns true if the response is {{code}} {{vendorExtensions.x-http-status}}. + /// + /// + bool Is{{vendorExtensions.x-http-status}} { get; } + {{/vendorExtensions.x-http-status-is-default}} + {{^-last}} + + {{/-last}} + {{/responses}} + } + {{/-first}} + {{/responses}} + {{/vendorExtensions.x-duplicates}} + {{/operation}} + + /// + /// Represents a collection of functions to interact with the API endpoints. + /// + {{>visibility}} class {{classname}}Events + { + {{#lambda.trimTrailingWithNewLine}} + {{#operation}} + /// + /// The event raised after the server response. + /// + public event EventHandler{{nrt?}} On{{operationId}}; + + /// + /// The event raised after an error querying the server. + /// + public event EventHandler{{nrt?}} OnError{{operationId}}; + + internal void ExecuteOn{{operationId}}({{#vendorExtensions.x-duplicates}}{{.}}{{/vendorExtensions.x-duplicates}}{{^vendorExtensions.x-duplicates}}{{classname}}{{/vendorExtensions.x-duplicates}}.{{operationId}}ApiResponse apiResponse) + { + On{{operationId}}?.Invoke(this, new ApiResponseEventArgs(apiResponse)); + } + + internal void ExecuteOnError{{operationId}}(Exception exception) + { + OnError{{operationId}}?.Invoke(this, new ExceptionEventArgs(exception)); + } + + {{/operation}} + {{/lambda.trimTrailingWithNewLine}} + } + + /// + /// Represents a collection of functions to interact with the API endpoints. + /// + {{>visibility}} sealed partial class {{classname}} : {{interfacePrefix}}{{classname}} + { + private JsonSerializerOptions _jsonSerializerOptions; + + /// + /// The logger factory. + /// + public ILoggerFactory LoggerFactory { get; } + + /// + /// The logger. + /// + public ILogger<{{classname}}> Logger { get; } + + /// + /// The HttpClient. + /// + public System.Net.Http.HttpClient HttpClient { get; } + + /// + /// The class containing the events. + /// + public {{classname}}Events Events { get; }{{#hasApiKeyMethods}} + + /// + /// A token provider of type . + /// + public ITokenProvider ApiKeyProvider { get; }{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + + /// + /// A token provider of type . + /// + public ITokenProvider BearerTokenProvider { get; }{{/hasHttpBearerMethods}}{{#hasHttpSignatureMethods}} + + /// + /// A token provider of type . + /// + public ITokenProvider HttpSignatureTokenProvider { get; }{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + + /// + /// A token provider of type . + /// + public ITokenProvider OauthTokenProvider { get; }{{/hasOAuthMethods}} + + {{#net80OrLater}} + {{#lambda.unique}} + {{#operation}} + {{#vendorExtensions.x-set-cookie}} + /// + /// The token cookie container. + /// + public {{packageName}}.{{corePackageName}}.Auth.CookieContainer CookieContainer { get; } + + {{/vendorExtensions.x-set-cookie}} + {{/operation}} + {{/lambda.unique}} + {{/net80OrLater}} + /// + /// Initializes a new instance of the class. + /// + public {{classname}}(ILogger<{{classname}}> logger, ILoggerFactory loggerFactory, System.Net.Http.HttpClient httpClient, JsonSerializerOptionsProvider jsonSerializerOptionsProvider, {{classname}}Events {{#lambda.camelcase_sanitize_param}}{{classname}}Events{{/lambda.camelcase_sanitize_param}}{{#hasApiKeyMethods}}, + ITokenProvider apiKeyProvider{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}}, + ITokenProvider bearerTokenProvider{{/hasHttpBearerMethods}}{{#hasHttpSignatureMethods}}, + ITokenProvider httpSignatureTokenProvider{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}}, + ITokenProvider oauthTokenProvider{{/hasOAuthMethods}}{{#net80OrLater}}{{#operation}}{{#lambda.uniqueLines}}{{#vendorExtensions.x-set-cookie}}, + {{packageName}}.{{clientPackage}}.CookieContainer cookieContainer{{/vendorExtensions.x-set-cookie}}{{/lambda.uniqueLines}}{{/operation}}{{/net80OrLater}}) + { + _jsonSerializerOptions = jsonSerializerOptionsProvider.Options; + LoggerFactory = loggerFactory; + Logger = logger == null ? LoggerFactory.CreateLogger<{{classname}}>() : logger; + HttpClient = httpClient; + Events = {{#lambda.camelcase_sanitize_param}}{{classname}}Events{{/lambda.camelcase_sanitize_param}};{{#hasApiKeyMethods}} + ApiKeyProvider = apiKeyProvider;{{/hasApiKeyMethods}}{{#hasHttpBearerMethods}} + BearerTokenProvider = bearerTokenProvider;{{/hasHttpBearerMethods}}{{#hasHttpSignatureMethods}} + HttpSignatureTokenProvider = httpSignatureTokenProvider;{{/hasHttpSignatureMethods}}{{#hasOAuthMethods}} + OauthTokenProvider = oauthTokenProvider;{{/hasOAuthMethods}}{{#net80OrLater}}{{#operation}}{{#lambda.uniqueLines}}{{#vendorExtensions.x-set-cookie}} + CookieContainer = cookieContainer;{{/vendorExtensions.x-set-cookie}}{{/lambda.uniqueLines}}{{/operation}}{{/net80OrLater}} + } + {{#operation}} + /// + /// {{summary}} {{notes}} + /// + /// + /// Use TryDeserializeOk(out result)) to retrieve the API result, when 200 OK response. + /// + /// + /// // Usage: + /// var response = await {{operationId}}Async(...); + /// if (response.TryDeserializeOk(out result)); + /// + /// Thrown when fails to make API call. + {{#allParams}} + /// {{description}}{{^required}} (optional{{#defaultValue}}, default to {{.}}{{/defaultValue}}){{/required}} + {{/allParams}} + /// . + /// of - If 200 OK response wraps the when `TryDeserializeOk(...)` is called. + public async Task<{{interfacePrefix}}{{operationId}}ApiResponse> {{operationId}}Async({{>OperationSignature}}) + { + {{#lambda.trimLineBreaks}} + UriBuilder uriBuilder = new UriBuilder(); + + try + { + using (HttpRequestMessage httpRequestMessage = new HttpRequestMessage()) + { + {{^servers}} + uriBuilder.Host = HttpClient.BaseAddress{{nrt!}}.Host; + uriBuilder.Port = HttpClient.BaseAddress.Port; + uriBuilder.Scheme = HttpClient.BaseAddress.Scheme; + uriBuilder.Path = HttpClient.BaseAddress.AbsolutePath == "/" + ? "{{{path}}}" + : string.Concat(HttpClient.BaseAddress.AbsolutePath, "{{{path}}}"); + {{/servers}} + {{#servers}} + {{#-first}} + Uri url = httpRequestMessage.RequestUri = new Uri("{{url}}"); + uriBuilder.Host = url.Authority; + uriBuilder.Scheme = url.Scheme; + uriBuilder.Path = url.AbsolutePath; + {{/-first}} + {{/servers}} + {{#constantParams}} + {{#isPathParam}} + // Set client side default value of Path Param "{{baseName}}". + uriBuilder.Path = uriBuilder.Path.Replace("%7B{{baseName}}%7D", Uri.EscapeDataString(ClientUtils.ParameterToString({{#_enum}}"{{{.}}}"{{/_enum}}))); // Constant path parameter + {{/isPathParam}} + {{/constantParams}} + {{#pathParams}} + uriBuilder.Path = uriBuilder.Path.Replace("%7B{{baseName}}%7D", Uri.EscapeDataString({{paramName}}.ToString())); + {{#-last}} + + {{/-last}} + {{/pathParams}} + {{#queryParams}} + {{#-first}} + + System.Collections.Specialized.NameValueCollection parseQueryString = System.Web.HttpUtility.ParseQueryString(string.Empty); + {{/-first}} + {{/queryParams}} + {{^queryParams}} + {{#authMethods}} + {{#isApiKey}} + {{#isKeyInQuery}} + + System.Collections.Specialized.NameValueCollection parseQueryString = System.Web.HttpUtility.ParseQueryString(string.Empty); + {{/isKeyInQuery}} + {{/isApiKey}} + {{/authMethods}} + {{/queryParams}} + {{#queryParams}} + {{#required}} + {{#-first}} + + {{/-first}} + parseQueryString["{{baseName}}"] = ClientUtils.ParameterToString({{paramName}}); + {{/required}} + {{/queryParams}} + + {{#constantParams}} + {{#isQueryParam}} + // Set client side default value of Query Param "{{baseName}}". + parseQueryString["{{baseName}}"] = ClientUtils.ParameterToString({{#_enum}}"{{{.}}}"{{/_enum}}); // Constant query parameter + {{/isQueryParam}} + {{/constantParams}} + {{#queryParams}} + {{^required}} + if ({{paramName}}.IsSet) + parseQueryString["{{baseName}}"] = ClientUtils.ParameterToString({{paramName}}.Value); + + {{/required}} + {{#-last}} + uriBuilder.Query = parseQueryString.ToString(); + + {{/-last}} + {{/queryParams}} + {{#constantParams}} + {{#isHeaderParam}} + // Set client side default value of Header Param "{{baseName}}". + httpRequestMessage.Headers.Add("{{baseName}}", ClientUtils.ParameterToString({{#_enum}}"{{{.}}}"{{/_enum}})); // Constant header parameter + {{/isHeaderParam}} + {{/constantParams}} + {{#headerParams}} + {{#required}} + httpRequestMessage.Headers.Add("{{baseName}}", ClientUtils.ParameterToString({{paramName}})); + + {{/required}} + {{^required}} + if ({{paramName}}.IsSet && {{paramName}}.Value != null) + httpRequestMessage.Headers.Add("{{baseName}}", ClientUtils.ParameterToString({{paramName}}.Value)); + + {{/required}} + {{/headerParams}} + {{#formParams}} + {{#-first}} + MultipartContent multipartContent = new MultipartContent(); + + httpRequestMessage.Content = multipartContent; + + List> formParameters = new List>(); + + multipartContent.Add(new FormUrlEncodedContent(formParameters));{{/-first}}{{^isFile}}{{#required}} + + formParameters.Add(new KeyValuePair("{{baseName}}", ClientUtils.ParameterToString({{paramName}}))); + + {{/required}} + {{^required}} + if ({{paramName}}.IsSet) + formParameters.Add(new KeyValuePair("{{baseName}}", ClientUtils.ParameterToString({{paramName}}.Value))); + + {{/required}} + {{/isFile}} + {{#isFile}} + {{#required}} + multipartContent.Add(new StreamContent({{paramName}})); + + {{/required}} + {{^required}} + if ({{paramName}}.IsSet) + multipartContent.Add(new StreamContent({{paramName}}.Value)); + + {{/required}} + {{/isFile}} + {{/formParams}} + {{#bodyParam}} + {{#required}} + httpRequestMessage.Content = ({{paramName}}{{^required}}.Value{{/required}} as object) is System.IO.Stream stream + ? httpRequestMessage.Content = new StreamContent(stream) + : httpRequestMessage.Content = new StringContent(JsonSerializer.Serialize({{paramName}}{{^required}}.Value{{/required}}, _jsonSerializerOptions)); + {{/required}} + {{^required}} + if ({{paramName}}.IsSet) + httpRequestMessage.Content = ({{paramName}}{{^required}}.Value{{/required}} as object) is System.IO.Stream stream + ? httpRequestMessage.Content = new StreamContent(stream) + : httpRequestMessage.Content = new StringContent(JsonSerializer.Serialize({{paramName}}{{^required}}.Value{{/required}}, _jsonSerializerOptions)); + {{/required}} + + {{/bodyParam}} + {{#authMethods}} + + {{#isApiKey}} + {{! Only use API Keys that are appended to the HTTP-headers }} + {{^isKeyInCookie}} + {{#isKeyInHeader}} + // Add authorization token to your HttpRequestMessage header + ApiKeyProvider.Get().AddTokenToHttpRequestMessageHeader(httpRequestMessage); + + {{/isKeyInHeader}} + {{/isKeyInCookie}} + {{/isApiKey}} + {{/authMethods}} + httpRequestMessage.RequestUri = uriBuilder.Uri; + {{#authMethods}} + {{#isBasicBearer}} + // Not supported, you'll have to add the BearerToken class and fetch is similar to the ApiKeyProvider. + BearerToken bearerToken{{-index}} = (BearerToken) await BearerTokenProvider.GetAsync(cancellation: cancellationToken).ConfigureAwait(false); + bearerToken{{-index}}.UseInHeader(httpRequestMessage, "{{keyParamName}}"); + {{/isBasicBearer}} + {{#isOAuth}} + // Not supported (yet), you'll have to add the BearerToken class and fetch is similar to the ApiKeyProvider. + OAuthToken oauthToken{{-index}} = (OAuthToken) await OauthTokenProvider.GetAsync(cancellation: cancellationToken).ConfigureAwait(false); + oauthToken{{-index}}.UseInHeader(httpRequestMessage, "{{keyParamName}}"); + {{/isOAuth}} + {{#isHttpSignature}} + + HttpSignatureToken httpSignatureToken{{-index}} = (HttpSignatureToken) await HttpSignatureTokenProvider.GetAsync(cancellation: cancellationToken).ConfigureAwait(false); + + if (httpRequestMessage.Content != null) { + string requestBody = await httpRequestMessage.Content.ReadAsStringAsync({{#net60OrLater}}cancellationToken{{/net60OrLater}}).ConfigureAwait(false); + + httpSignatureToken{{-index}}.UseInHeader(httpRequestMessage, requestBody, cancellationToken); + } + {{/isHttpSignature}} + {{/authMethods}} + {{#consumes}} + {{#-first}} + + {{=<% %>=}} + string[] contentTypes = new string[] {<%/-first%> + <%={{ }}=%> + "{{{mediaType}}}"{{^-last}},{{/-last}}{{#-last}} + }; + {{/-last}} + {{/consumes}} + {{#consumes}} + {{#-first}} + + httpRequestMessage.AddUserAgentToHeaders(); + httpRequestMessage.AddLibraryNameToHeader(); + httpRequestMessage.AddLibraryVersionToHeader(); + + string{{nrt?}} contentType = ClientUtils.SelectHeaderContentType(contentTypes); + + if (contentType != null && httpRequestMessage.Content != null) + httpRequestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType); + + {{/-first}} + {{/consumes}} + {{#produces}} + {{#-first}} + + {{=<% %>=}} + string[] accepts = new string[] {<%/-first%> + <%={{ }}=%> + "{{{mediaType}}}"{{^-last}},{{/-last}}{{#-last}} + }; + {{/-last}} + {{/produces}} + {{#produces}} + {{#-first}} + + string{{nrt?}} accept = ClientUtils.SelectHeaderAccept(accepts); + + if (accept != null) + httpRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(accept)); + {{/-first}} + {{/produces}} + {{#net60OrLater}} + + httpRequestMessage.Method = HttpMethod.{{#lambda.titlecase}}{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}{{/lambda.titlecase}}; + {{/net60OrLater}} + {{^net60OrLater}} + httpRequestMessage.Method = new HttpMethod("{{#lambda.uppercase}}{{httpMethod}}{{/lambda.uppercase}}"); + {{/net60OrLater}} + + DateTime requestedAt = DateTime.UtcNow; + + using (HttpResponseMessage httpResponseMessage = await HttpClient.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false)) + { + ILogger<{{#vendorExtensions.x-duplicates}}{{.}}.{{/vendorExtensions.x-duplicates}}{{operationId}}ApiResponse> apiResponseLogger = LoggerFactory.CreateLogger<{{#vendorExtensions.x-duplicates}}{{.}}.{{/vendorExtensions.x-duplicates}}{{operationId}}ApiResponse>(); + {{#vendorExtensions.x-duplicates}}{{.}}.{{/vendorExtensions.x-duplicates}}{{operationId}}ApiResponse apiResponse; + + switch ((int)httpResponseMessage.StatusCode) { + {{#responses}} + {{#isBinary}} + case ({{code}}): + {{/isBinary}} + {{/responses}} + {{#responses}} + {{#isBinary}} + {{#-first}} + { + byte[] responseBytesArray = await httpResponseMessage.Content.ReadAsByteArrayAsync({{#net60OrLater}}cancellationToken{{/net60OrLater}}).ConfigureAwait(false); + System.IO.Stream responseContentStream = new System.IO.MemoryStream(responseBytesArray); + apiResponse = new{{^net60OrLater}} {{operationId}}ApiResponse{{/net60OrLater}}(apiResponseLogger, httpRequestMessage, httpResponseMessage, responseContentStream, "{{{path}}}", requestedAt, _jsonSerializerOptions); + + break; + } + {{/-first}} + {{/isBinary}} + {{/responses}} + default: { + string responseContent = await httpResponseMessage.Content.ReadAsStringAsync({{#net60OrLater}}cancellationToken{{/net60OrLater}}).ConfigureAwait(false); + apiResponse = new{{^net60OrLater}} {{operationId}}ApiResponse{{/net60OrLater}}(apiResponseLogger, httpRequestMessage, httpResponseMessage, responseContent, "{{{path}}}", requestedAt, _jsonSerializerOptions); + + break; + } + } + + Events.ExecuteOn{{operationId}}(apiResponse); + {{#net80OrLater}} + {{#responses}} + {{#vendorExtensions.x-set-cookie}} + if (httpResponseMessage.StatusCode == (HttpStatusCode) {{code}} && httpResponseMessage.Headers.TryGetValues("Set-Cookie", out var cookieHeaders)) + { + foreach(string cookieHeader in cookieHeaders) + { + IList setCookieHeaderValues = Microsoft.Net.Http.Headers.SetCookieHeaderValue.ParseList(cookieHeaders.ToArray()); + + foreach(Microsoft.Net.Http.Headers.SetCookieHeaderValue setCookieHeaderValue in setCookieHeaderValues) + { + Cookie cookie = new Cookie(setCookieHeaderValue.Name.ToString(), setCookieHeaderValue.Value.ToString()) + { + HttpOnly = setCookieHeaderValue.HttpOnly + }; + + if (setCookieHeaderValue.Expires.HasValue) + cookie.Expires = setCookieHeaderValue.Expires.Value.UtcDateTime; + + if (setCookieHeaderValue.Path.HasValue) + cookie.Path = setCookieHeaderValue.Path.Value; + + if (setCookieHeaderValue.Domain.HasValue) + cookie.Domain = setCookieHeaderValue.Domain.Value; + + CookieContainer.Value.Add(new Uri($"{uriBuilder.Scheme}://{uriBuilder.Host}"), cookie); + } + } + } + + {{/vendorExtensions.x-set-cookie}} + {{/responses}} + {{/net80OrLater}} + return apiResponse; + } + } + } + catch(Exception exception) + { + Events.ExecuteOnError{{operationId}}(exception); + throw; + } + {{/lambda.trimLineBreaks}} + } + {{^vendorExtensions.x-duplicates}} + {{#responses}} + {{#-first}} + + /// + /// The . + /// + {{>visibility}} partial class {{operationId}}ApiResponse : {{packageName}}.{{corePackageName}}.{{clientPackage}}.ApiResponse, {{interfacePrefix}}{{operationId}}ApiResponse + { + /// + /// The logger for . + /// + public ILogger<{{operationId}}ApiResponse> Logger { get; } + + /// + /// The . + /// + /// + /// + /// + /// + /// + /// + /// + public {{operationId}}ApiResponse(ILogger<{{operationId}}ApiResponse> logger, System.Net.Http.HttpRequestMessage httpRequestMessage, System.Net.Http.HttpResponseMessage httpResponseMessage, string rawContent, string path, DateTime requestedAt, System.Text.Json.JsonSerializerOptions jsonSerializerOptions) : base(httpRequestMessage, httpResponseMessage, rawContent, path, requestedAt, jsonSerializerOptions) + { + Logger = logger; + OnCreated(httpRequestMessage, httpResponseMessage); + } + + /// + /// The . + /// + /// + /// + /// + /// + /// + /// + /// + public {{operationId}}ApiResponse(ILogger<{{operationId}}ApiResponse> logger, System.Net.Http.HttpRequestMessage httpRequestMessage, System.Net.Http.HttpResponseMessage httpResponseMessage, System.IO.Stream contentStream, string path, DateTime requestedAt, System.Text.Json.JsonSerializerOptions jsonSerializerOptions) : base(httpRequestMessage, httpResponseMessage, contentStream, path, requestedAt, jsonSerializerOptions) + { + Logger = logger; + OnCreated(httpRequestMessage, httpResponseMessage); + } + + partial void OnCreated(global::System.Net.Http.HttpRequestMessage httpRequestMessage, System.Net.Http.HttpResponseMessage httpResponseMessage); + {{#responses}} + + {{#vendorExtensions.x-http-status-is-default}} + /// + /// Returns true if the response is the default response type. + /// + /// + public bool Is{{vendorExtensions.x-http-status}} => {{#vendorExtensions.x-only-default}}true{{/vendorExtensions.x-only-default}}{{^vendorExtensions.x-only-default}}{{#lambda.joinConditions}}{{#responses}}{{^vendorExtensions.x-http-status-is-default}}!Is{{vendorExtensions.x-http-status}} {{/vendorExtensions.x-http-status-is-default}}{{/responses}}{{/lambda.joinConditions}}{{/vendorExtensions.x-only-default}}; + {{/vendorExtensions.x-http-status-is-default}} + {{^vendorExtensions.x-http-status-is-default}} + /// + /// Returns true if the response is {{code}} {{vendorExtensions.x-http-status}}. + /// + /// + {{#vendorExtensions.x-http-status-range}} + public bool Is{{vendorExtensions.x-http-status}} + { + get + { + int statusCode = (int)StatusCode; + return {{vendorExtensions.x-http-status-range}}00 >= statusCode && {{vendorExtensions.x-http-status-range}}99 <= statusCode; + } + } + {{/vendorExtensions.x-http-status-range}} + {{^vendorExtensions.x-http-status-range}} + public bool Is{{vendorExtensions.x-http-status}} => {{code}} == (int)StatusCode; + {{/vendorExtensions.x-http-status-range}} + {{/vendorExtensions.x-http-status-is-default}} + {{#dataType}} + + /// + /// Deserializes the response if the response is {{code}} {{vendorExtensions.x-http-status}}. + /// + /// + public {{#isModel}}{{^containerType}}{{packageName}}.{{modelPackage}}.{{/containerType}}{{/isModel}}{{{dataType}}}{{#nrt}}?{{/nrt}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} {{vendorExtensions.x-http-status}}() + { + {{#lambda.trimTrailingWithNewLine}} + {{#lambda.indent4}} + {{>AsModel}}{{! prevent indent}} + {{/lambda.indent4}} + {{/lambda.trimTrailingWithNewLine}} + } + + /// + /// Returns true if the response is {{code}} {{vendorExtensions.x-http-status}} and the deserialized response is not null. + /// + /// + /// + public bool TryDeserialize{{vendorExtensions.x-http-status}}Response({{#net60OrLater}}[NotNullWhen(true)]{{/net60OrLater}}out {{#isModel}}{{^containerType}}{{packageName}}.{{modelPackage}}.{{/containerType}}{{/isModel}}{{{dataType}}}{{#nrt}}?{{/nrt}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} result) + { + result = null; + + try + { + result = {{vendorExtensions.x-http-status}}(); + } + catch (Exception exception) + { + OnDeserializationError(exception, (HttpStatusCode){{#vendorExtensions.x-http-status-range}}{{.}}{{/vendorExtensions.x-http-status-range}}{{^vendorExtensions.x-http-status-range}}{{code}}{{/vendorExtensions.x-http-status-range}}); + } + + return result != null; + } + {{/dataType}} + {{#-last}} + + private void OnDeserializationError(Exception exception, HttpStatusCode httpStatusCode) + { + bool suppressDefaultLog = false; + OnDeserializationError(ref suppressDefaultLog, exception, httpStatusCode); + {{#lambda.trimTrailingWithNewLine}} + {{#lambda.indent4}} + {{>OnDeserializationError}}{{! prevent indent}} + {{/lambda.indent4}} + {{/lambda.trimTrailingWithNewLine}} + } + + partial void OnDeserializationError(ref bool suppressDefaultLog, Exception exception, HttpStatusCode httpStatusCode); + {{/-last}} + {{/responses}} + } + + {{/-first}} + {{/responses}} + {{/vendorExtensions.x-duplicates}} + {{/operation}} + } + {{/operations}} +} +{{/lambda.trimLineBreaks}} diff --git a/templates-v7/csharp/libraries/generichost/api_doc.mustache b/templates-v7/csharp/libraries/generichost/api_doc.mustache new file mode 100644 index 000000000..218feb13b --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/api_doc.mustache @@ -0,0 +1,91 @@ +## {{packageName}}.{{apiPackage}}.{{classname}}{{#description}} +{{.}}{{/description}} + +#### API Base-Path: **{{{basePath}}}** + + +#### Authorization: {{^authMethods}}No authorization required{{/authMethods}}{{#authMethods}}{{#isApiKey}}{{^isKeyInCookie}}{{#isKeyInHeader}}{{{name}}}{{/isKeyInHeader}}{{/isKeyInCookie}}{{/isApiKey}}{{/authMethods}} + + +#### Initialization + + +```csharp +using {{packageName}}.{{corePackageName}}.Options; +using {{packageName}}.{{apiName}}.Extensions; +using {{packageName}}.{{apiName}}.Services; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +IHost host = Host.CreateDefaultBuilder() + .Configure{{apiName}}((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + // Set your ADYEN_API_KEY or use get it from your environment using context.Configuration["ADYEN_API_KEY"]. + options.AdyenApiKey = context.Configuration["ADYEN_API_KEY"]; + + // Set your environment, e.g. `Test` or `Live`. + options.Environment = AdyenEnvironment.Test; + + // For the Live environment, additionally include your LiveEndpointUrlPrefix. + options.LiveEndpointUrlPrefix = "your-live-endpoint-url-prefix"; + }); + }) + .Build(); + + // You can inject this service in your constructor. + {{interfacePrefix}}{{classname}} {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}} = host.Services.GetRequiredService<{{interfacePrefix}}{{classname}}>(); +``` + +| Method | HTTP request | Description | +|--------|--------------|-------------| +{{#operations}} +{{#operation}} +| [**{{operationId}}**]({{classname}}.md#{{operationIdLowerCase}}) | **{{httpMethod}}** {{path}} | {{summary}} | +{{/operation}} +{{/operations}} + +{{#operations}} +{{#operation}} + +### **{{httpMethod}}** **{{{path}}}** + +{{{summary}}} + +#### Parameters +{{^allParams}}This endpoint does not need any parameter.{{/allParams}}{{#allParams}}{{#-last}} +| Name | Type | Description | +|------|------|-------------| +{{/-last}} +{{/allParams}} +{{#allParams}} +| **{{paramName}}** | {{#isFile}}**{{dataType}}**{{/isFile}}{{#isPrimitiveType}}[{{dataType}}]{{/isPrimitiveType}}{{^isPrimitiveType}}{{^isFile}}**{{dataType}}**{{/isFile}}{{/isPrimitiveType}} | {{description}} | +{{/allParams}} + +#### Example usage + +```csharp +using {{packageName}}.{{modelPackage}}; +using {{packageName}}.{{apiName}}.Services; + +// Example `{{classname}}.{{operationId}}` usage: +// Provide the following values: {{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}. +{{interfacePrefix}}{{returnType}} response = await {{#lambda.camelcase_sanitize_param}}{{classname}}{{/lambda.camelcase_sanitize_param}}.{{operationId}}Async( + {{#allParams}}{{{dataType}}} {{paramName}}{{^-last}}, + {{/-last}}{{/allParams}}, + CancellationToken cancellationToken = default); + +if (response.TryDeserializeOkResponse(out {{returnType}} result)) +{ + // Handle result, if 200 OK response +} + +``` + +#### Return type +{{#returnType}}{{#returnTypeIsPrimitive}}[{{{returnType}}}]{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}[{{returnType}}](){{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}void (empty response body){{/returnType}} + + +{{/operation}} +{{/operations}} diff --git a/templates-v7/csharp/libraries/generichost/api_test.mustache b/templates-v7/csharp/libraries/generichost/api_test.mustache new file mode 100644 index 000000000..d54244957 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/api_test.mustache @@ -0,0 +1,71 @@ +{{>partial_header}} +using {{packageName}}.Core.Options;{{#hasImport}} +using {{packageName}}.{{modelPackage}};{{/hasImport}} +using {{packageName}}.{{apiName}}.{{clientPackageName}}; +using {{packageName}}.{{apiName}}.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Text.Json; + +{{>testInstructions}} + + +namespace {{packageName}}.Test.{{apiPackage}} +{ + /// + /// Class for testing {{classname}} + /// + [TestClass] + public sealed class {{classname}}Test + { + private readonly JsonSerializerOptionsProvider _jsonSerializerOptionsProvider; + + public {{classname}}Test() + { + IHost host = Host.CreateDefaultBuilder() + .Configure{{classname}}((context, services, config) => + { + config.ConfigureAdyenOptions(options => + { + options.Environment = AdyenEnvironment.Test; + }); + }) + .Build(); + + _jsonSerializerOptionsProvider = host.Services.GetRequiredService(); + } + {{#operations}} + {{#operation}} + + /// + /// Test {{operationId}}. + /// + public async Task Given_Deserialize_When_{{operationId}}_Successfully_Deserializes() + { + // Arrange + // TODO: Insert your example code from open-api here + var client = TestUtilities.GetTestFileContent("mocks/posmobile/create-session.json"); + + {{#allParams}} + {{^required}}Client.Option<{{/required}}{{{dataType}}}{{>NullConditionalParameter}}{{^required}}>{{/required}} {{paramName}} = default{{nrt!}}; + {{/allParams}} + {{#responses}} + {{#-first}} + {{#dataType}} + + // Act + // TODO: Prefill the ResponseType + // IDEALLY: We insert the SERVICE and then we call it using the right parameters (this gets tricky with query params* but very doable!) + var response = JsonSerializer.Deserialize<{{name}}Response}>(json, _jsonSerializerOptionsProvider); + + // Assert + Assert.IsNotNull(response); + {{/dataType}} + {{/-first}} + {{/responses}} + } + {{/operation}} + {{/operations}} + } +} diff --git a/templates-v7/csharp/libraries/generichost/model.mustache b/templates-v7/csharp/libraries/generichost/model.mustache new file mode 100644 index 000000000..fa07a7053 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/model.mustache @@ -0,0 +1,56 @@ +// +{{>partial_header}} + +{{#nrt}} +#nullable enable + +{{/nrt}} +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.IO; +{{^useGenericHost}} +using System.Runtime.Serialization; +{{/useGenericHost}} +using System.Text; +using System.Text.RegularExpressions; +using System.Text.Json; +using System.Text.Json.Serialization; +{{#validatable}} +using System.ComponentModel.DataAnnotations; +{{/validatable}} +{{#useCompareNetObjects}} +using OpenAPIClientUtils = {{packageName}}.Client.ClientUtils; +{{/useCompareNetObjects}} +{{#useGenericHost}} +{{#useSourceGeneration}} +using System.Text.Json.Serialization.Metadata; +{{/useSourceGeneration}} +using {{packageName}}.{{corePackageName}}; +using {{packageName}}.{{apiName}}.{{clientPackage}}; +{{/useGenericHost}} +{{#models}} +{{#lambda.trimTrailingWithNewLine}} +{{#model}} + +namespace {{packageName}}.{{modelPackage}} +{ +{{#isEnum}} +{{>modelEnum}} + +{{/isEnum}} +{{^isEnum}} +{{>modelGeneric}} + + +{{>JsonConverter}} + +{{/isEnum}} +{{>SourceGenerationContext}} + +{{/model}} +{{/lambda.trimTrailingWithNewLine}} +{{/models}} +} diff --git a/templates-v7/csharp/libraries/generichost/modelGeneric.mustache b/templates-v7/csharp/libraries/generichost/modelGeneric.mustache new file mode 100644 index 000000000..00f340413 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/modelGeneric.mustache @@ -0,0 +1,413 @@ + /// + /// {{{description}}}{{^description}}{{classname}}{{/description}}. + /// + {{>visibility}} partial class {{classname}}{{#lambda.firstDot}}{{#parent}} : .{{/parent}}{{#validatable}} : .{{/validatable}}{{#equatable}}{{#readOnlyVars}}{{#-first}} : .{{/-first}}{{/readOnlyVars}}{{/equatable}}{{/lambda.firstDot}}{{#lambda.joinWithComma}}{{#parent}}{{{.}}} {{/parent}}{{>ImplementsIEquatable}}{{#validatable}}IValidatableObject {{/validatable}}{{/lambda.joinWithComma}} + { + {{#composedSchemas.oneOf}} + {{^vendorExtensions.x-duplicated-data-type}} + /// + /// Initializes a new instance of the class. + /// + /// + {{#composedSchemas.anyOf}} + /// + {{/composedSchemas.anyOf}} + {{#allVars}} + {{^isDiscriminator}} + /// {{description}}{{^description}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/description}}{{#defaultValue}} (default to {{.}}){{/defaultValue}} + {{/isDiscriminator}} + {{/allVars}} + {{#model.vendorExtensions.x-model-is-mutable}}{{>visibility}}{{/model.vendorExtensions.x-model-is-mutable}}{{^model.vendorExtensions.x-model-is-mutable}}internal{{/model.vendorExtensions.x-model-is-mutable}} {{classname}}({{#lambda.joinWithComma}}{{{dataType}}} {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}} {{#model.composedSchemas.anyOf}}{{^required}}Option<{{/required}}{{{dataType}}}{{>NullConditionalProperty}}{{^required}}>{{/required}} {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{baseType}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}} {{/model.composedSchemas.anyOf}}{{>ModelSignature}}{{/lambda.joinWithComma}}){{#parent}} : base({{#lambda.joinWithComma}}{{#parentModel.composedSchemas.oneOf}}{{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{parent}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}}.{{#lambda.titlecase}}{{baseType}}{{/lambda.titlecase}} {{/parentModel.composedSchemas.oneOf}}{{>ModelBaseSignature}}{{/lambda.joinWithComma}}){{/parent}} + { + {{#composedSchemas.anyOf}} + {{#lambda.titlecase}}{{name}}{{/lambda.titlecase}}{{^required}}Option{{/required}} = {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}}; + {{/composedSchemas.anyOf}} + {{name}} = {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}}; + {{#allVars}} + {{^isDiscriminator}} + {{^isInherited}} + {{name}}{{^required}}Option{{/required}} = {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}}; + {{/isInherited}} + {{#isInherited}} + {{#isNew}} + {{name}}{{^required}}Option{{/required}} = {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}}; + {{/isNew}} + {{/isInherited}} + {{/isDiscriminator}} + {{#vendorExtensions.x-is-base-or-new-discriminator}} + {{^model.composedSchemas.anyOf}} + {{^model.composedSchemas.oneOf}} + {{name}} = {{^isEnum}}this.GetType().Name{{/isEnum}}{{#isEnum}}({{datatypeWithEnum}})Enum.Parse(typeof({{datatypeWithEnum}}), this.GetType().Name){{/isEnum}}; + {{/model.composedSchemas.oneOf}} + {{/model.composedSchemas.anyOf}} + {{/vendorExtensions.x-is-base-or-new-discriminator}} + {{/allVars}} + OnCreated(); + } + + {{/vendorExtensions.x-duplicated-data-type}} + {{/composedSchemas.oneOf}} + {{^composedSchemas.oneOf}} + /// + /// Initializes a new instance of the class. + /// + {{#composedSchemas.anyOf}} + /// + {{/composedSchemas.anyOf}} + {{#allVars}} + {{^isDiscriminator}} + /// {{description}}{{^description}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/description}}{{#defaultValue}} (default to {{.}}){{/defaultValue}} + {{/isDiscriminator}} + {{/allVars}} + {{^composedSchemas.anyOf}} + [JsonConstructor] + {{/composedSchemas.anyOf}} + {{#model.vendorExtensions.x-model-is-mutable}}{{>visibility}}{{/model.vendorExtensions.x-model-is-mutable}}{{^model.vendorExtensions.x-model-is-mutable}}internal{{/model.vendorExtensions.x-model-is-mutable}} {{classname}}({{#lambda.joinWithComma}}{{#composedSchemas.anyOf}}{{^required}}Option<{{/required}}{{{datatypeWithEnum}}}{{>NullConditionalProperty}}{{^required}}>{{/required}} {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}} {{/composedSchemas.anyOf}}{{>ModelSignature}}{{/lambda.joinWithComma}}){{#parent}} : base({{#lambda.joinWithComma}}{{>ModelBaseSignature}}{{/lambda.joinWithComma}}){{/parent}} + { + {{#composedSchemas.anyOf}} + {{#lambda.titlecase}}{{name}}{{/lambda.titlecase}}{{^required}}Option{{/required}} = {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}}; + {{/composedSchemas.anyOf}} + {{#allVars}} + {{^isDiscriminator}} + {{^isInherited}} + {{^required}}_{{/required}}{{name}}{{^required}}Option{{/required}} = {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}}; + {{/isInherited}} + {{#isInherited}} + {{#isNew}} + {{^required}}_{{/required}}{{name}}{{^required}}Option{{/required}} = {{#lambda.escape_reserved_word}}{{#lambda.camel_case}}{{name}}{{/lambda.camel_case}}{{/lambda.escape_reserved_word}}; + {{/isNew}} + {{/isInherited}} + {{/isDiscriminator}} + {{#vendorExtensions.x-is-base-or-new-discriminator}} + {{^model.composedSchemas.anyOf}} + {{^model.composedSchemas.oneOf}} + {{name}} = {{^isEnum}}this.GetType().Name{{/isEnum}}{{#isEnum}}({{datatypeWithEnum}})Enum.Parse(typeof({{datatypeWithEnum}}), this.GetType().Name){{/isEnum}}; + {{/model.composedSchemas.oneOf}} + {{/model.composedSchemas.anyOf}} + {{/vendorExtensions.x-is-base-or-new-discriminator}} + {{/allVars}} + OnCreated(); + } + + {{#allVars}} + {{^isDiscriminator}} + {{#-first}} + /// + /// Best practice: Use the constructor to initialize your objects to understand which parameters are required/optional. + /// + {{#model.vendorExtensions.x-model-is-mutable}}{{>visibility}}{{/model.vendorExtensions.x-model-is-mutable}}{{^model.vendorExtensions.x-model-is-mutable}}internal{{/model.vendorExtensions.x-model-is-mutable}} {{classname}}() + { + } + {{/-first}} + {{/isDiscriminator}} + {{/allVars}} + + {{/composedSchemas.oneOf}} + partial void OnCreated(); + + {{#vars}} + {{#items.isEnum}} + {{#items}} + {{^complexType}} +{{>modelInnerEnum}} + + {{/complexType}} + {{/items}} + {{/items.isEnum}} + {{#isEnum}} + {{^complexType}} +{{>modelInnerEnum}} + + {{/complexType}} + {{/isEnum}} + {{^isDiscriminator}} + {{#isEnum}} + {{^required}} + /// + /// This is used to track if an optional field is set. If set, will be populated. + /// + [JsonIgnore] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public {{#isNew}}new {{/isNew}}Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}> _{{name}}Option { get; {{^isReadOnly}}private set; {{/isReadOnly}}} + + {{/required}} + /// + /// {{{description}}}{{^description}}.{{/description}} + /// + {{#description}} + /// {{.}} + {{/description}} + {{#example}} + /* {{.}} */ + {{/example}} + [JsonPropertyName("{{baseName}}")] + {{#deprecated}} + [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] + {{/deprecated}} + public {{#isNew}}new {{/isNew}}{{{datatypeWithEnum}}}{{#lambda.first}}{{#isNullable}}{{>NullConditionalProperty}} {{/isNullable}}{{^required}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} {{/required}}{{/lambda.first}} {{name}} {{#required}}{ get; {{^isReadOnly}}set; {{/isReadOnly}}}{{/required}}{{^required}}{ get { return this._{{name}}Option; } {{^isReadOnly}}set { this._{{name}}Option = new{{^net70OrLater}} Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}>{{/net70OrLater}}(value); } {{/isReadOnly}}}{{/required}} + + {{/isEnum}} + {{/isDiscriminator}} + {{/vars}} + {{#composedSchemas.anyOf}} + {{^vendorExtensions.x-duplicated-data-type}} + {{^required}} + /// + /// This is used to track if an optional field is set. If set, will be populated. + /// + [JsonIgnore] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public {{#isNew}}new {{/isNew}}Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}> _{{#lambda.titlecase}}{{name}}{{/lambda.titlecase}}Option { get; {{^isReadOnly}}private set; {{/isReadOnly}}} + + {{/required}} + /// + /// {{{description}}}{{^description}}.{{/description}} + /// {{#description}} + /// {{.}}{{/description}} + {{#example}} + /* {{.}} */ + {{/example}} + {{#deprecated}} + [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] + {{/deprecated}} + public {{{datatypeWithEnum}}}{{#lambda.first}}{{#isNullable}}{{>NullConditionalProperty}} {{/isNullable}}{{^required}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} {{/required}}{{/lambda.first}} {{#lambda.titlecase}}{{baseType}}{{/lambda.titlecase}} {{#required}}{ get; {{^isReadOnly}}set; {{/isReadOnly}}}{{/required}}{{^required}}{ get { return this._{{#lambda.titlecase}}{{name}}{{/lambda.titlecase}}Option; } {{^isReadOnly}}set { this._{{#lambda.titlecase}}{{name}}{{/lambda.titlecase}}Option = new{{^net70OrLater}} Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}>{{/net70OrLater}}(value); } {{/isReadOnly}}}{{/required}} + + {{/vendorExtensions.x-duplicated-data-type}} + {{/composedSchemas.anyOf}} + {{#composedSchemas.oneOf}} + {{^vendorExtensions.x-duplicated-data-type}} + /// + /// {{{description}}}{{^description}}.{{/description}}. + /// {{#description}} + /// {{.}}{{/description}} + {{#example}} + /* {{.}} */ + {{/example}} + {{#deprecated}} + [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] + {{/deprecated}} + public {{{dataType}}}{{>NullConditionalProperty}} {{#lambda.titlecase}}{{name}}{{/lambda.titlecase}} { get; {{^isReadOnly}}set; {{/isReadOnly}}} + + {{/vendorExtensions.x-duplicated-data-type}} + {{/composedSchemas.oneOf}} + {{#allVars}} + {{#vendorExtensions.x-is-base-or-new-discriminator}} + {{^model.composedSchemas.anyOf}} + {{^model.composedSchemas.oneOf}} + /// + /// The discriminator. + /// + [JsonIgnore] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public {{#isNew}}new {{/isNew}}{{datatypeWithEnum}} {{name}} { get; } + + {{/model.composedSchemas.oneOf}} + {{/model.composedSchemas.anyOf}} + {{/vendorExtensions.x-is-base-or-new-discriminator}} + {{^vendorExtensions.x-is-base-or-new-discriminator}} + {{^isEnum}} + {{#isInherited}} + {{#isNew}} + {{^required}} + /// + /// This is used to track if an optional field is set. If set, will be populated. + /// + [JsonIgnore] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public new Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}> _{{name}}Option { get; {{^isReadOnly}}private set; {{/isReadOnly}}} + + {{/required}} + /// + /// {{{description}}}{{^description}}.{{/description}} + /// {{#description}} + /// {{.}}{{/description}} + {{#example}} + /* {{.}} */ + {{/example}} + [JsonPropertyName("{{baseName}}")] + {{#deprecated}} + [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] + {{/deprecated}} + public new {{{datatypeWithEnum}}}{{#lambda.first}}{{#isNullable}}{{>NullConditionalProperty}} {{/isNullable}}{{^required}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} {{/required}}{{/lambda.first}} {{name}} {{#required}}{ get; {{^isReadOnly}}set; {{/isReadOnly}}}{{/required}}{{^required}}{ get { return this._{{name}}Option } {{^isReadOnly}}set { this._{{name}}Option = new{{^net70OrLater}} Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}>{{/net70OrLater}}(value); } {{/isReadOnly}}}{{/required}} + + {{/isNew}} + {{/isInherited}} + {{^isInherited}} + {{^required}} + /// + /// This is used to track if an optional field is set. If set, will be populated. + /// + [JsonIgnore] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}> _{{name}}Option { get; {{^isReadOnly}}private set; {{/isReadOnly}}} + + {{/required}} + /// + /// {{description}}{{^description}}.{{/description}} + /// {{#description}} + /// {{{description}}}{{/description}} + {{#example}} + /* {{.}} */ + {{/example}} + [JsonPropertyName("{{baseName}}")] + {{#deprecated}} + [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] + {{/deprecated}} + public {{{datatypeWithEnum}}}{{#lambda.first}}{{#isNullable}}{{>NullConditionalProperty}} {{/isNullable}}{{^required}}{{nrt?}}{{^nrt}}{{#vendorExtensions.x-is-value-type}}?{{/vendorExtensions.x-is-value-type}}{{/nrt}} {{/required}}{{/lambda.first}} {{name}} {{#required}}{ get; {{^isReadOnly}}set; {{/isReadOnly}}}{{/required}}{{^required}}{ get { return this._{{name}}Option; } {{^isReadOnly}}set { this._{{name}}Option = new{{^net70OrLater}} Option<{{{datatypeWithEnum}}}{{>NullConditionalProperty}}>{{/net70OrLater}}(value); } {{/isReadOnly}}}{{/required}} + + {{/isInherited}} + {{/isEnum}} + {{/vendorExtensions.x-is-base-or-new-discriminator}} + {{/allVars}} + {{#isAdditionalPropertiesTrue}} + {{^parentModel}} + /// + /// Gets or sets additional properties + /// + [JsonExtensionData] + public Dictionary AdditionalProperties { get; } = new Dictionary(); + + {{/parentModel}} + {{/isAdditionalPropertiesTrue}} + /// + /// Returns the string presentation of the object + /// + /// String presentation of the object + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("class {{classname}} {\n"); + {{#composedSchemas.oneOf}} + if (this.{{name}} != null) + sb.Append({{name}}.ToString().Replace("\n", "\n ")); + {{/composedSchemas.oneOf}} + {{#parent}} + sb.Append(" ").Append(base.ToString()?.Replace("\n", "\n ")).Append("\n"); + {{/parent}} + {{#vars}} + {{^isDiscriminator}} + sb.Append(" {{name}}: ").Append({{name}}).Append("\n"); + {{/isDiscriminator}} + {{/vars}} + {{#isAdditionalPropertiesTrue}} + {{^parentModel}} + sb.Append(" AdditionalProperties: ").Append(AdditionalProperties).Append("\n"); + {{/parentModel}} + {{/isAdditionalPropertiesTrue}} + sb.Append("}\n"); + return sb.ToString(); + } + {{#equatable}} + {{#readOnlyVars}} + {{#-first}} + + /// + /// Returns true if objects are equal. + /// + /// Object to be compared + /// Boolean + public override bool Equals(object{{nrt?}} input) + { + {{#useCompareNetObjects}} + return OpenAPIClientUtils.compareLogic.Compare(this, input as {{classname}}).AreEqual; + {{/useCompareNetObjects}} + {{^useCompareNetObjects}} + return this.Equals(input as {{classname}}); + {{/useCompareNetObjects}} + } + + /// + /// Returns true if {{classname}} instances are equal. + /// + /// Instance of {{classname}} to be compared + /// Boolean + public bool Equals({{classname}}{{nrt?}} input) + { + {{#useCompareNetObjects}} + return OpenAPIClientUtils.compareLogic.Compare(this, input).AreEqual; + {{/useCompareNetObjects}} + {{^useCompareNetObjects}} + if (input == null) + return false; + + return {{#parent}}base.Equals(input) && {{/parent}}{{#readOnlyVars}}{{^isInherited}}{{^isContainer}} + ( + {{name}} == input.{{name}} || + {{^vendorExtensions.x-is-value-type}} + ({{name}} != null && + {{name}}.Equals(input.{{name}})) + {{/vendorExtensions.x-is-value-type}} + {{#vendorExtensions.x-is-value-type}} + {{name}}.Equals(input.{{name}}) + {{/vendorExtensions.x-is-value-type}} + ){{^-last}} && {{/-last}}{{/isContainer}}{{#isContainer}} + ( + {{name}} == input.{{name}} || + {{^vendorExtensions.x-is-value-type}}{{name}} != null && + input.{{name}} != null && + {{/vendorExtensions.x-is-value-type}}{{name}}.SequenceEqual(input.{{name}}) + ){{^-last}} && {{/-last}}{{/isContainer}}{{/isInherited}}{{/readOnlyVars}}{{^readOnlyVars}}{{#parent}}base.Equals(input){{/parent}}{{^parent}}false{{/parent}}{{/readOnlyVars}}{{^isAdditionalPropertiesTrue}};{{/isAdditionalPropertiesTrue}} + {{#isAdditionalPropertiesTrue}} + {{^parentModel}} + && (AdditionalProperties.Count == input.AdditionalProperties.Count && !AdditionalProperties.Except(input.AdditionalProperties).Any()); + {{/parentModel}} + {{/isAdditionalPropertiesTrue}} + {{/useCompareNetObjects}} + } + + /// + /// Gets the hash code. + /// + /// Hash code + public override int GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + {{#parent}} + int hashCode = base.GetHashCode(); + {{/parent}} + {{^parent}} + int hashCode = 41; + {{/parent}} + {{#readOnlyVars}} + {{#required}} + {{^isNullable}} + hashCode = (hashCode * 59) + {{name}}.GetHashCode(); + {{/isNullable}} + {{/required}} + {{/readOnlyVars}} + {{#readOnlyVars}} + {{#lambda.copy}} + if ({{name}} != null) + hashCode = (hashCode * 59) + {{name}}.GetHashCode(); + + {{/lambda.copy}} + {{#isNullable}} + {{#lambda.pasteOnce}} + {{/lambda.pasteOnce}} + {{/isNullable}} + {{^required}} + {{#lambda.pasteOnce}} + {{/lambda.pasteOnce}} + {{/required}} + {{/readOnlyVars}} + {{#isAdditionalPropertiesTrue}} + {{^parentModel}} + hashCode = (hashCode * 59) + AdditionalProperties.GetHashCode(); + {{/parentModel}} + {{/isAdditionalPropertiesTrue}} + + return hashCode; + } + } + {{/-first}} + {{/readOnlyVars}} + {{/equatable}} +{{#validatable}} +{{^parentModel}} + +{{>validatable}} + +{{/parentModel}} +{{/validatable}} + } \ No newline at end of file diff --git a/templates-v7/csharp/libraries/generichost/modelInnerEnum.mustache b/templates-v7/csharp/libraries/generichost/modelInnerEnum.mustache new file mode 100644 index 000000000..572d0f136 --- /dev/null +++ b/templates-v7/csharp/libraries/generichost/modelInnerEnum.mustache @@ -0,0 +1,116 @@ + {{^isContainer}} + /// + /// {{{description}}}{{^description}}Defines {{{name}}}.{{/description}} + /// + {{#description}} + /// {{.}} + {{/description}} + {{#isString}} + {{#useGenericHost}} + [JsonConverter(typeof({{datatypeWithEnum}}JsonConverter))] + {{/useGenericHost}} + {{/isString}} + {{>visibility}} class {{datatypeWithEnum}} : IEnum + { + /// + /// Returns the value of the {{datatypeWithEnum}}. + /// + public string? Value { get; set; } + + {{#allowableValues}} + {{#enumVars}} + /// + /// {{datatypeWithEnum}}.{{name}} - {{value}} + /// + public static readonly {{datatypeWithEnum}} {{name}} = new("{{value}}"); + {{^-last}} + + {{/-last}} + {{/enumVars}} + {{/allowableValues}} + + private {{datatypeWithEnum}}(string? value) + { + Value = value; + } + + /// + /// Converts a string to a implicitly. + /// + /// The string value to convert. Defaults to null. + /// A new instance initialized with the string value. + public static implicit operator {{datatypeWithEnum}}?(string? value) => value == null ? null : new {{datatypeWithEnum}}(value); + + /// + /// Converts a instance to a string implicitly. + /// + /// The instance. Default to null. + /// String value of the instance./// + public static implicit operator string?({{datatypeWithEnum}}? option) => option?.Value; + + public static bool operator ==({{datatypeWithEnum}}? left, {{datatypeWithEnum}}? right) => string.Equals(left?.Value, right?.Value, StringComparison.OrdinalIgnoreCase); + + public static bool operator !=({{datatypeWithEnum}}? left, {{datatypeWithEnum}}? right) => !string.Equals(left?.Value, right?.Value, StringComparison.OrdinalIgnoreCase); + + public override bool Equals(object? obj) => obj is {{datatypeWithEnum}} other && string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + public override int GetHashCode() => Value?.GetHashCode() ?? 0; + + public override string ToString() => Value ?? string.Empty; + + {{#useGenericHost}} + /// + /// Returns a . + /// + /// + /// or null. + public static {{datatypeWithEnum}}? FromStringOrDefault(string value) + { + return value switch { + {{#allowableValues}}{{#enumVars}}{{#isString}}"{{{value}}}"{{/isString}} => {{datatypeWithEnum}}.{{name}}, + {{/enumVars}}{{/allowableValues}}_ => null, + }; + } + + /// + /// Converts the to the json value. + /// + /// + /// String value of the enum. + {{#isString}} + /// + {{/isString}} + public static {{>EnumValueDataType}}?{{#lambda.first}}{{#nrt}}{{/nrt}}{{/lambda.first}} ToJsonValue({{datatypeWithEnum}}? value) + { + if (value == null) + return null; + + {{#allowableValues}} + {{#enumVars}} + if (value == {{datatypeWithEnum}}.{{name}}) + return {{#isString}}"{{{value}}}"{{/isString}}; + + {{/enumVars}} + {{/allowableValues}} + return null; + } + + /// + /// JsonConverter for writing {{datatypeWithEnum}}. + /// + public class {{datatypeWithEnum}}JsonConverter : JsonConverter<{{datatypeWithEnum}}> + { + public override {{datatypeWithEnum}}? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions jsonOptions) + { + string value = reader.GetString(); + return value == null ? null : {{datatypeWithEnum}}.FromStringOrDefault(value) ?? new {{datatypeWithEnum}}(value); + } + + public override void Write(Utf8JsonWriter writer, {{datatypeWithEnum}} value, JsonSerializerOptions jsonOptions) + { + writer.WriteStringValue({{datatypeWithEnum}}.ToJsonValue(value)); + } + } + {{/useGenericHost}} + } + {{/isContainer}} \ No newline at end of file diff --git a/templates-v7/csharp/partial_header.mustache b/templates-v7/csharp/partial_header.mustache new file mode 100644 index 000000000..377ccfabb --- /dev/null +++ b/templates-v7/csharp/partial_header.mustache @@ -0,0 +1,18 @@ +/* + {{#appName}} + * {{{.}}} + * + {{/appName}} + {{#appDescription}} + * {{{.}}} + * + {{/appDescription}} + {{#version}} + * The version of the OpenAPI document: {{{.}}} + {{/version}} + {{#infoEmail}} + * Contact: {{{.}}} + {{/infoEmail}} + * NOTE: This class is auto-generated using a modified version (https://github.com/Adyen/adyen-sdk-automation) of the OpenAPI Generator: https://github.com/openapitools/openapi-generator.git. + * Do not edit this class manually. + */ diff --git a/templates-v7/csharp/validatable.mustache b/templates-v7/csharp/validatable.mustache new file mode 100644 index 000000000..d8b0f2512 --- /dev/null +++ b/templates-v7/csharp/validatable.mustache @@ -0,0 +1,141 @@ +{{#discriminator}} + /// + /// To validate all properties of the instance. + /// + /// Validation context + /// Validation Result + IEnumerable IValidatableObject.Validate(ValidationContext validationContext) + { + return this.BaseValidate(validationContext); + } + + /// + /// To validate all properties of the instance. + /// + /// Validation context + /// Validation Result + protected IEnumerable BaseValidate(ValidationContext validationContext) + { +{{/discriminator}} +{{^discriminator}} + {{#parent}} + /// + /// To validate all properties of the instance. + /// + /// Validation context + /// Validation Result + IEnumerable IValidatableObject.Validate(ValidationContext validationContext) + { + return this.BaseValidate(validationContext); + } + + /// + /// To validate all properties of the instance. + /// + /// Validation context + /// Validation Result + protected IEnumerable BaseValidate(ValidationContext validationContext) + { + {{/parent}} + {{^parent}} + /// + /// To validate all properties of the instance. + /// + /// Validation context + /// Validation Result + IEnumerable IValidatableObject.Validate(ValidationContext validationContext) + { + {{/parent}} +{{/discriminator}} + {{#parent}} + {{^isArray}} + {{^isMap}} + foreach (var x in {{#discriminator}}base.{{/discriminator}}BaseValidate(validationContext)) + { + yield return x; + } + {{/isMap}} + {{/isArray}} + {{/parent}} + {{#vars}} + {{#hasValidation}} + {{^isEnum}} + {{^isDateTime}} + {{^isDate}} + {{^isTime}} + {{#maxLength}} + // {{{name}}} ({{{dataType}}}) maxLength + if (this.{{{name}}} != null && this.{{{name}}}.Length > {{maxLength}}) + { + yield return new ValidationResult("Invalid value for {{{name}}}, length must be less than {{maxLength}}.", new [] { "{{{name}}}" }); + } + + {{/maxLength}} + {{#minLength}} + // {{{name}}} ({{{dataType}}}) minLength + if (this.{{{name}}} != null && this.{{{name}}}.Length < {{minLength}}) + { + yield return new ValidationResult("Invalid value for {{{name}}}, length must be greater than {{minLength}}.", new [] { "{{{name}}}" }); + } + + {{/minLength}} + {{/isTime}} + {{/isDate}} + {{/isDateTime}} + {{#maximum}} + // {{{name}}} ({{{dataType}}}) maximum + if ({{#useGenericHost}}{{^required}}this.{{{name}}}Option.IsSet && {{/required}}{{/useGenericHost}}this.{{{name}}}{{#useGenericHost}}{{^required}}Option.Value{{/required}}{{/useGenericHost}} {{#exclusiveMaximum}}<={{/exclusiveMaximum}}{{^exclusiveMaximum}}>{{/exclusiveMaximum}} ({{{dataType}}}){{maximum}}) + { + yield return new ValidationResult("Invalid value for {{{name}}}, must be a value less than {{^exclusiveMaximum}}or equal to {{/exclusiveMaximum}}{{maximum}}.", new [] { "{{{name}}}" }); + } + + {{/maximum}} + {{#minimum}} + // {{{name}}} ({{{dataType}}}) minimum + if ({{#useGenericHost}}{{^required}}this.{{{name}}}Option.IsSet && {{/required}}{{/useGenericHost}}this.{{{name}}}{{#useGenericHost}}{{^required}}Option.Value{{/required}}{{/useGenericHost}} {{#exclusiveMaximum}}>={{/exclusiveMaximum}}{{^exclusiveMaximum}}<{{/exclusiveMaximum}} ({{{dataType}}}){{minimum}}) + { + yield return new ValidationResult("Invalid value for {{{name}}}, must be a value greater than {{^exclusiveMinimum}}or equal to {{/exclusiveMinimum}}{{minimum}}.", new [] { "{{{name}}}" }); + } + + {{/minimum}} + {{#pattern}} + {{^isByteArray}} + {{#vendorExtensions.x-is-value-type}} + {{#isNullable}} + if (this.{{{name}}}{{#useGenericHost}}{{^required}}Option.Value{{/required}}{{/useGenericHost}} != null){ + {{#lambda.trimTrailingWithNewLine}} + {{#lambda.indent4}} + {{>ValidateRegex}}{{! prevent indent}} + {{/lambda.indent4}} + + {{/lambda.trimTrailingWithNewLine}} + } + + {{/isNullable}} + {{^isNullable}} + {{#lambda.trimTrailingWithNewLine}} + {{#lambda.indent3}} + {{>ValidateRegex}}{{! prevent indent}} + {{/lambda.indent3}} + + {{/lambda.trimTrailingWithNewLine}} + {{/isNullable}} + {{/vendorExtensions.x-is-value-type}} + {{^vendorExtensions.x-is-value-type}} + if (this.{{{name}}}{{#useGenericHost}}{{^required}}Option.Value{{/required}}{{/useGenericHost}} != null) { + {{#lambda.trimTrailingWithNewLine}} + {{#lambda.indent4}} + {{>ValidateRegex}}{{! prevent indent}} + {{/lambda.indent4}} + + {{/lambda.trimTrailingWithNewLine}} + } + + {{/vendorExtensions.x-is-value-type}} + {{/isByteArray}} + {{/pattern}} + {{/isEnum}} + {{/hasValidation}} + {{/vars}} + yield break; + } \ No newline at end of file diff --git a/templates-v7/csharp/visibility.mustache b/templates-v7/csharp/visibility.mustache new file mode 100644 index 000000000..a1d1f4163 --- /dev/null +++ b/templates-v7/csharp/visibility.mustache @@ -0,0 +1 @@ +{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} \ No newline at end of file diff --git a/templates/csharp/AbstractOpenAPISchema.mustache b/templates/csharp/AbstractOpenAPISchema.mustache deleted file mode 100644 index ab90f174c..000000000 --- a/templates/csharp/AbstractOpenAPISchema.mustache +++ /dev/null @@ -1,71 +0,0 @@ -{{>partial_header}} -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Runtime.Serialization; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; - -namespace {{packageName}}.{{modelPackage}} -{ - /// - /// Abstract base class for oneOf, anyOf schemas in the OpenAPI specification - /// - {{>visibility}} abstract partial class AbstractOpenAPISchema - { - /// - /// Custom JSON serializer - /// - static public readonly JsonSerializerSettings SerializerSettings = new JsonSerializerSettings - { - // OpenAPI generated types generally hide default constructors. - ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor, - MissingMemberHandling = MissingMemberHandling.Ignore, - ContractResolver = new DefaultContractResolver - { - NamingStrategy = new CamelCaseNamingStrategy - { - OverrideSpecifiedNames = false - } - } - }; - - /// - /// Gets or Sets the actual instance - /// - public abstract Object ActualInstance { get; set; } - - /// - /// Gets or Sets IsNullable to indicate whether the instance is nullable - /// - public bool IsNullable { get; protected set; } - - /// - /// Gets or Sets the schema type, which can be either `oneOf` or `anyOf` - /// - public string SchemaType { get; protected set; } - - /// - /// Converts the instance into JSON string. - /// - public abstract string ToJson(); - - // Check if the contains TypeEnum value - protected static bool ContainsValue(string type) where T : struct, IConvertible - { - // Search for type in .TypeEnum - List list = new List(); - var members = typeof(T).GetTypeInfo().DeclaredMembers; - foreach (var member in members) - { - var val = member?.GetCustomAttribute(false)?.Value; - if (!string.IsNullOrEmpty(val)) - { - list.Add(val); - } - } - - return list.Contains(type); - } - } -} diff --git a/templates/csharp/api-single.mustache b/templates/csharp/api-single.mustache deleted file mode 100644 index da60553d0..000000000 --- a/templates/csharp/api-single.mustache +++ /dev/null @@ -1,87 +0,0 @@ -{{>partial_header}} -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Adyen.Constants; -using Adyen.Model; -{{#hasImport}}using Adyen.{{modelPackage}}; -{{/hasImport}} - -namespace {{packageName}}.Service -{ -{{#operations}} - /// - /// {{classname}} Interface - /// - public interface I{{customApi}}Service - { - {{#operation}} -{{>method_documentation}} - {{#returnType}} - /// . - {{/returnType}} - {{#isDeprecated}} - [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] - {{/isDeprecated}} - {{#returnType}}{{modelPackage}}.{{{.}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}({{>api_parameters}}); - - {{#supportsAsync}} -{{>method_documentation}} - /// A CancellationToken enables cooperative cancellation between threads, thread pool work items, or Task objects.{{#returnType}} - /// Task of .{{/returnType}} - {{#isDeprecated}} - [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] - {{/isDeprecated}} - {{#returnType}}Task<{{modelPackage}}.{{{.}}}>{{/returnType}}{{^returnType}}Task{{/returnType}} {{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}Async({{>api_parameters_async}}); - - {{/supportsAsync}} - {{/operation}} - } - {{/operations}} - - {{#operations}} - /// - /// Represents a collection of functions to interact with the {{customApi}}Service API endpoints - /// - {{>visibility}} class {{customApi}}Service : AbstractService, I{{customApi}}Service - { - private readonly string _baseUrl; - - public {{customApi}}Service(Client client) : base(client) - { - _baseUrl = CreateBaseUrl("{{{basePath}}}"); - } - {{#operation}} - - {{#isDeprecated}} - [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] - {{/isDeprecated}} - public {{#returnType}}{{modelPackage}}.{{{.}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}({{>api_parameters}}) - { - {{#returnType}}return {{/returnType}}{{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}Async({{>api_invoke}}).ConfigureAwait(false).GetAwaiter().GetResult(); - } - - {{#supportsAsync}} - {{#isDeprecated}} - [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] - {{/isDeprecated}} - {{#returnType}}public async Task<{{modelPackage}}.{{{.}}}>{{/returnType}}{{^returnType}}public async Task{{/returnType}} {{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}Async({{>api_parameters_async}}) - { - {{#hasQueryParams}} - // Build the query string - var queryParams = new Dictionary(); - {{#queryParams}} - {{^required}}if ({{paramName}} != null) {{/required}}queryParams.Add("{{baseName}}", {{paramName}}{{^isString}}{{^isDateTime}}.Value.ToString(){{/isDateTime}}{{#isDateTime}}.ToString("yyyy-MM-ddTHH:mm:ssZ"){{/isDateTime}}{{/isString}}); - {{/queryParams}} - {{/hasQueryParams}} - var endpoint = _baseUrl + {{#hasPathParams}}${{/hasPathParams}}"{{{path}}}"{{#hasQueryParams}} + ToQueryString(queryParams){{/hasQueryParams}}; - var resource = new ServiceResource(this, endpoint); - {{#returnType}}return {{/returnType}}await resource.RequestAsync{{#returnType}}<{{modelPackage}}.{{returnType}}>{{/returnType}}({{#bodyParam}}{{paramName}}.ToJson(){{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}, requestOptions, new HttpMethod("{{httpMethod}}"), cancellationToken).ConfigureAwait(false); - } - {{/supportsAsync}} - {{/operation}} - } - {{/operations}} -} \ No newline at end of file diff --git a/templates/csharp/api.mustache b/templates/csharp/api.mustache deleted file mode 100644 index 27ff0eb4d..000000000 --- a/templates/csharp/api.mustache +++ /dev/null @@ -1,86 +0,0 @@ -{{>partial_header}} -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Adyen.Model; -{{#hasImport}}using Adyen.{{modelPackage}}; -{{/hasImport}} - -namespace {{packageName}}.{{apiPackage}} -{ -{{#operations}} - /// - /// {{classname}} Interface - /// - public interface I{{classname}} - { - {{#operation}} -{{>method_documentation}} - {{#returnType}} - /// . - {{/returnType}} - {{#isDeprecated}} - [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] - {{/isDeprecated}} - {{#returnType}}{{modelPackage}}.{{{.}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}({{>api_parameters}}); - - {{#supportsAsync}} -{{>method_documentation}} - /// A CancellationToken enables cooperative cancellation between threads, thread pool work items, or Task objects.{{#returnType}} - /// Task of .{{/returnType}} - {{#isDeprecated}} - [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] - {{/isDeprecated}} - {{#returnType}}Task<{{modelPackage}}.{{{.}}}>{{/returnType}}{{^returnType}}Task{{/returnType}} {{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}Async({{>api_parameters_async}}); - - {{/supportsAsync}} - {{/operation}} - } - {{/operations}} - - {{#operations}} - /// - /// Represents a collection of functions to interact with the {{classname}} API endpoints - /// - {{>visibility}} class {{classname}} : AbstractService, I{{classname}} - { - private readonly string _baseUrl; - - public {{classname}}(Client client) : base(client) - { - _baseUrl = CreateBaseUrl("{{{basePath}}}"); - } - {{#operation}} - - {{#isDeprecated}} - [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] - {{/isDeprecated}} - public {{#returnType}}{{modelPackage}}.{{{.}}}{{/returnType}}{{^returnType}}void{{/returnType}} {{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}({{>api_parameters}}) - { - {{#returnType}}return {{/returnType}}{{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}Async({{>api_invoke}}).ConfigureAwait(false).GetAwaiter().GetResult(); - } - - {{#supportsAsync}} - {{#isDeprecated}} - [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] - {{/isDeprecated}} - {{#returnType}}public async Task<{{modelPackage}}.{{{.}}}>{{/returnType}}{{^returnType}}public async Task{{/returnType}} {{#lambda.pascalcase}}{{vendorExtensions.x-methodName}}{{/lambda.pascalcase}}Async({{>api_parameters_async}}) - { - {{#hasQueryParams}} - // Build the query string - var queryParams = new Dictionary(); - {{#queryParams}} - {{^required}}if ({{paramName}} != null) {{/required}}queryParams.Add("{{baseName}}", {{paramName}}{{^isString}}{{^isDateTime}}.ToString(){{/isDateTime}}{{#isDateTime}}{{^required}}.Value{{/required}}.ToString("yyyy-MM-ddTHH:mm:ssZ"){{/isDateTime}}{{/isString}}); - {{/queryParams}} - {{/hasQueryParams}} - var endpoint = _baseUrl + {{#hasPathParams}}${{/hasPathParams}}"{{{path}}}"{{#hasQueryParams}} + ToQueryString(queryParams){{/hasQueryParams}}; - var resource = new ServiceResource(this, endpoint); - {{#returnType}}return {{/returnType}}await resource.RequestAsync{{#returnType}}<{{modelPackage}}.{{returnType}}>{{/returnType}}({{#bodyParam}}{{paramName}}.ToJson(){{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}, requestOptions, new HttpMethod("{{httpMethod}}"), cancellationToken).ConfigureAwait(false); - } - {{/supportsAsync}} - {{/operation}} - } - {{/operations}} -} \ No newline at end of file diff --git a/templates/csharp/api_invoke.mustache b/templates/csharp/api_invoke.mustache deleted file mode 100644 index 45815fc53..000000000 --- a/templates/csharp/api_invoke.mustache +++ /dev/null @@ -1 +0,0 @@ -{{#requiredParams}}{{#isPathParam}}{{paramName}}, {{/isPathParam}}{{#isBodyParam}}{{paramName}}, {{/isBodyParam}}{{#isQueryParam}}{{paramName}}, {{/isQueryParam}}{{/requiredParams}}{{#optionalParams}}{{#isPathParam}}{{paramName}}, {{/isPathParam}}{{#isBodyParam}}{{paramName}}, {{/isBodyParam}}{{#isQueryParam}}{{paramName}}, {{/isQueryParam}}{{/optionalParams}}requestOptions \ No newline at end of file diff --git a/templates/csharp/api_parameter_ordering.mustache b/templates/csharp/api_parameter_ordering.mustache deleted file mode 100644 index 593846f25..000000000 --- a/templates/csharp/api_parameter_ordering.mustache +++ /dev/null @@ -1 +0,0 @@ -{{#isPathParam}}{{{dataType}}} {{paramName}} = default, {{/isPathParam}}{{#isBodyParam}}{{{dataType}}} {{paramName}} = default, {{/isBodyParam}}{{#isQueryParam}}{{{dataType}}} {{paramName}} = default, {{/isQueryParam}} \ No newline at end of file diff --git a/templates/csharp/api_parameter_ordering_required.mustache b/templates/csharp/api_parameter_ordering_required.mustache deleted file mode 100644 index 0c1ecfea1..000000000 --- a/templates/csharp/api_parameter_ordering_required.mustache +++ /dev/null @@ -1 +0,0 @@ -{{#isPathParam}}{{{dataType}}} {{paramName}}, {{/isPathParam}}{{#isBodyParam}}{{{dataType}}} {{paramName}}, {{/isBodyParam}}{{#isQueryParam}}{{{dataType}}} {{paramName}}, {{/isQueryParam}} \ No newline at end of file diff --git a/templates/csharp/api_parameters.mustache b/templates/csharp/api_parameters.mustache deleted file mode 100644 index 3ca742884..000000000 --- a/templates/csharp/api_parameters.mustache +++ /dev/null @@ -1,2 +0,0 @@ -{{! Path and body are required, followed by optional query string and request options }} -{{#requiredParams}}{{>api_parameter_ordering_required}}{{/requiredParams}}{{#optionalParams}}{{>api_parameter_ordering}}{{/optionalParams}}RequestOptions requestOptions = default \ No newline at end of file diff --git a/templates/csharp/api_parameters_async.mustache b/templates/csharp/api_parameters_async.mustache deleted file mode 100644 index 4287dc8b4..000000000 --- a/templates/csharp/api_parameters_async.mustache +++ /dev/null @@ -1,2 +0,0 @@ -{{! Path and body are required, followed by optional query string and request options }} -{{#requiredParams}}{{>api_parameter_ordering_required}}{{/requiredParams}}{{#optionalParams}}{{>api_parameter_ordering}}{{/optionalParams}}RequestOptions requestOptions = default, CancellationToken cancellationToken = default \ No newline at end of file diff --git a/templates/csharp/config.yaml b/templates/csharp/config.yaml deleted file mode 100644 index 829b2714b..000000000 --- a/templates/csharp/config.yaml +++ /dev/null @@ -1,6 +0,0 @@ -templateDir: ./templates/csharp -files: - api-single.mustache: - folder: api - templateType: API - destinationFilename: Single.cs \ No newline at end of file diff --git a/templates/csharp/method_documentation.mustache b/templates/csharp/method_documentation.mustache deleted file mode 100644 index 537b3924e..000000000 --- a/templates/csharp/method_documentation.mustache +++ /dev/null @@ -1,13 +0,0 @@ - /// - /// {{{summary}}} - /// - {{#pathParams}} - /// - {{description}}{{#isDeprecated}} (deprecated){{/isDeprecated}} - {{/pathParams}} - {{#bodyParams}} - /// - {{description}}{{#isDeprecated}} (deprecated){{/isDeprecated}} - {{/bodyParams}} - {{#queryParams}} - /// - {{description}}{{#isDeprecated}} (deprecated){{/isDeprecated}} - {{/queryParams}} - /// - Additional request options. \ No newline at end of file diff --git a/templates/csharp/model.mustache b/templates/csharp/model.mustache deleted file mode 100644 index 51a2ad036..000000000 --- a/templates/csharp/model.mustache +++ /dev/null @@ -1,49 +0,0 @@ -{{>partial_header}} -{{#models}} -{{#model}} -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.IO; -{{#vendorExtensions.x-com-visible}} -using System.Runtime.InteropServices; -{{/vendorExtensions.x-com-visible}} -using System.Runtime.Serialization; -using System.Text; -using System.Text.RegularExpressions; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Linq; -{{#discriminator}} -using JsonSubTypes; -{{/discriminator}} -{{/model}} -{{/models}} -{{#validatable}} -using System.ComponentModel.DataAnnotations; -{{/validatable}} -using OpenAPIDateConverter = Adyen.ApiSerialization.OpenAPIDateConverter; -{{#useCompareNetObjects}} -using OpenAPIClientUtils = {{packageName}}.Client.ClientUtils; -{{/useCompareNetObjects}} -{{#models}} -{{#model}} -{{#oneOf}} -{{#-first}} -using System.Reflection; -{{/-first}} -{{/oneOf}} -{{#anyOf}} -{{#-first}} -using System.Reflection; -{{/-first}} -{{/anyOf}} - -namespace {{packageName}}.{{modelPackage}} -{ -{{#isEnum}}{{>modelEnum}}{{/isEnum}}{{^isEnum}}{{#oneOf}}{{#-first}}{{>modelOneOf}}{{/-first}}{{/oneOf}}{{#anyOf}}{{#-first}}{{>modelAnyOf}}{{/-first}}{{/anyOf}}{{^oneOf}}{{^anyOf}}{{>modelGeneric}}{{/anyOf}}{{/oneOf}}{{/isEnum}} -{{/model}} -{{/models}} -} diff --git a/templates/csharp/modelGeneric.mustache b/templates/csharp/modelGeneric.mustache deleted file mode 100644 index ffeedcb6e..000000000 --- a/templates/csharp/modelGeneric.mustache +++ /dev/null @@ -1,384 +0,0 @@ - /// - /// {{description}}{{^description}}{{classname}}{{/description}} - /// - {{#vendorExtensions.x-cls-compliant}} - [CLSCompliant({{{vendorExtensions.x-cls-compliant}}})] - {{/vendorExtensions.x-cls-compliant}} - {{#vendorExtensions.x-com-visible}} - [ComVisible({{{vendorExtensions.x-com-visible}}})] - {{/vendorExtensions.x-com-visible}} - [DataContract(Name = "{{{name}}}")] - {{#discriminator}} - [JsonConverter(typeof(JsonSubtypes), "{{{discriminatorName}}}")] - {{#mappedModels}} - [JsonSubtypes.KnownSubType(typeof({{{modelName}}}), "{{^vendorExtensions.x-discriminator-value}}{{{mappingName}}}{{/vendorExtensions.x-discriminator-value}}{{#vendorExtensions.x-discriminator-value}}{{{.}}}{{/vendorExtensions.x-discriminator-value}}")] - {{/mappedModels}} - {{/discriminator}} - {{>visibility}} partial class {{classname}} : {{#parent}}{{{.}}}, {{/parent}}IEquatable<{{classname}}>{{#validatable}}, IValidatableObject{{/validatable}} - { - {{#vars}} - {{#items.isEnum}} - {{#items}} - {{^complexType}} -{{>modelInnerEnum}} - {{/complexType}} - {{/items}} - {{/items.isEnum}} - {{#isEnum}} - {{^complexType}} -{{>modelInnerEnum}} - {{/complexType}} - {{/isEnum}} - {{#isEnum}} - - /// - /// {{description}}{{^description}}Gets or Sets {{{name}}}{{/description}} - /// - {{#description}} - /// {{.}} - {{/description}} - {{^conditionalSerialization}} - [DataMember(Name = "{{baseName}}"{{#required}}, IsRequired = false{{/required}}, EmitDefaultValue = {{#vendorExtensions.x-emit-default-value}}true{{/vendorExtensions.x-emit-default-value}}{{^vendorExtensions.x-emit-default-value}}{{#required}}false{{/required}}{{^required}}{{#isBoolean}}false{{/isBoolean}}{{^isBoolean}}{{#isNullable}}false{{/isNullable}}{{^isNullable}}false{{/isNullable}}{{/isBoolean}}{{/required}}{{/vendorExtensions.x-emit-default-value}})] - {{#deprecated}} - [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] - {{/deprecated}} - public {{{complexType}}}{{^complexType}}{{{datatypeWithEnum}}}{{/complexType}}{{^isContainer}}{{^required}}?{{/required}}{{/isContainer}} {{name}} { get; set; } - {{#isReadOnly}} - - /// - /// Returns false as {{name}} should not be serialized given that it's read-only. - /// - /// false (boolean) - public bool ShouldSerialize{{name}}() - { - return false; - } - {{/isReadOnly}} - {{/conditionalSerialization}} - {{#conditionalSerialization}} - {{#isReadOnly}} - [DataMember(Name = "{{baseName}}"{{#required}}, IsRequired = false{{/required}}, EmitDefaultValue = {{#vendorExtensions.x-emit-default-value}}true{{/vendorExtensions.x-emit-default-value}}{{^vendorExtensions.x-emit-default-value}}{{#required}}false{{/required}}{{^required}}{{#isBoolean}}false{{/isBoolean}}{{^isBoolean}}{{#isNullable}}false{{/isNullable}}{{^isNullable}}false{{/isNullable}}{{/isBoolean}}{{/required}}{{/vendorExtensions.x-emit-default-value}})] - {{#deprecated}} - [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] - {{/deprecated}} - public {{{complexType}}}{{^complexType}}{{{datatypeWithEnum}}}{{/complexType}}{{^isContainer}}{{^required}}?{{/required}}{{/isContainer}} {{name}} { get; set; } - - /// - /// Returns false as {{name}} should not be serialized given that it's read-only. - /// - /// false (boolean) - public bool ShouldSerialize{{name}}() - { - return false; - } - {{/isReadOnly}} - - {{^isReadOnly}} - [DataMember(Name = "{{baseName}}"{{#required}}, IsRequired = false{{/required}}, EmitDefaultValue = {{#vendorExtensions.x-emit-default-value}}true{{/vendorExtensions.x-emit-default-value}}{{^vendorExtensions.x-emit-default-value}}{{#required}}true{{/required}}{{^required}}{{#isBoolean}}false{{/isBoolean}}{{^isBoolean}}{{#isNullable}}true{{/isNullable}}{{^isNullable}}false{{/isNullable}}{{/isBoolean}}{{/required}}{{/vendorExtensions.x-emit-default-value}})] - {{#deprecated}} - [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] - {{/deprecated}} - public {{{complexType}}}{{^complexType}}{{{datatypeWithEnum}}}{{/complexType}}{{^isContainer}}{{^required}}?{{/required}}{{/isContainer}} {{name}} - { - get{ return _{{name}};} - set - { - _{{name}} = value; - _flag{{name}} = true; - } - } - private {{{complexType}}}{{^complexType}}{{{datatypeWithEnum}}}{{/complexType}}{{^isContainer}}{{^required}}?{{/required}}{{/isContainer}} _{{name}}; - private bool _flag{{name}}; - - /// - /// Returns false as {{name}} should not be serialized given that it's read-only. - /// - /// false (boolean) - public bool ShouldSerialize{{name}}() - { - return _flag{{name}}; - } - {{/isReadOnly}} - {{/conditionalSerialization}} - {{/isEnum}} - {{/vars}} - {{#hasRequired}} - {{^hasOnlyReadOnly}} - /// - /// Initializes a new instance of the class. - /// - [JsonConstructorAttribute] - {{^isAdditionalPropertiesTrue}} - protected {{classname}}() { } - {{/isAdditionalPropertiesTrue}} - {{#isAdditionalPropertiesTrue}} - protected {{classname}}() - { - this.AdditionalProperties = new Dictionary(); - } - {{/isAdditionalPropertiesTrue}} - {{/hasOnlyReadOnly}} - {{/hasRequired}} - /// - /// Initializes a new instance of the class. - /// - {{#readWriteVars}} - /// {{description}}{{^description}}{{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}{{/description}}{{#required}} (required){{/required}}{{#defaultValue}} (default to {{.}}){{/defaultValue}}. - {{/readWriteVars}} - {{#hasOnlyReadOnly}} - [JsonConstructorAttribute] - {{/hasOnlyReadOnly}} - public {{classname}}({{#readWriteVars}}{{{datatypeWithEnum}}}{{#isNumeric}}?{{/isNumeric}}{{#isBoolean}}{{^isNullable}}?{{/isNullable}}{{/isBoolean}}{{#isEnum}}{{^isContainer}}{{^required}}?{{/required}}{{/isContainer}}{{/isEnum}} {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}} = {{#defaultValue}}{{^isDateTime}}{{{defaultValue}}}{{/isDateTime}}{{#isDateTime}}default({{{datatypeWithEnum}}}){{/isDateTime}}{{/defaultValue}}{{^defaultValue}}default({{{datatypeWithEnum}}}{{#isNumeric}}?{{/isNumeric}}{{#isBoolean}}{{^isNullable}}?{{/isNullable}}{{/isBoolean}}{{#isEnum}}{{^isContainer}}{{^required}}?{{/required}}{{/isContainer}}{{/isEnum}}){{/defaultValue}}{{^-last}}, {{/-last}}{{/readWriteVars}}){{#parent}} : base({{#parentVars}}{{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}{{^-last}}, {{/-last}}{{/parentVars}}){{/parent}} - { - {{#vars}} - {{^isInherited}} - {{^isReadOnly}} - {{#required}} - {{^conditionalSerialization}} - {{^vendorExtensions.x-csharp-value-type}} - this.{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}; - {{/vendorExtensions.x-csharp-value-type}} - {{#vendorExtensions.x-csharp-value-type}} - this.{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}; - {{/vendorExtensions.x-csharp-value-type}} - {{/conditionalSerialization}} - {{#conditionalSerialization}} - {{^vendorExtensions.x-csharp-value-type}} - this._{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}; - {{/vendorExtensions.x-csharp-value-type}} - {{#vendorExtensions.x-csharp-value-type}} - this._{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}; - {{/vendorExtensions.x-csharp-value-type}} - {{/conditionalSerialization}} - {{/required}} - {{/isReadOnly}} - {{/isInherited}} - {{/vars}} - {{#vars}} - {{^isInherited}} - {{^isReadOnly}} - {{^required}} - {{#defaultValue}} - {{^conditionalSerialization}} - {{^vendorExtensions.x-csharp-value-type}} - // use default value if no "{{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}" provided - this.{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}} ?? {{{defaultValue}}}; - {{/vendorExtensions.x-csharp-value-type}} - {{#vendorExtensions.x-csharp-value-type}} - this.{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}; - {{/vendorExtensions.x-csharp-value-type}} - {{/conditionalSerialization}} - {{/defaultValue}} - {{^defaultValue}} - {{^conditionalSerialization}} - this.{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}; - {{/conditionalSerialization}} - {{#conditionalSerialization}} - this._{{name}} = {{#lambda.camelcase_param}}{{name}}{{/lambda.camelcase_param}}; - if (this.{{name}} != null) - { - this._flag{{name}} = true; - } - {{/conditionalSerialization}} - {{/defaultValue}} - {{/required}} - {{/isReadOnly}} - {{/isInherited}} - {{/vars}} - {{#isAdditionalPropertiesTrue}} - this.AdditionalProperties = new Dictionary(); - {{/isAdditionalPropertiesTrue}} - } - - {{#vars}} - {{^isInherited}} - {{^isEnum}} - /// - /// {{description}}{{^description}}Gets or Sets {{{name}}}{{/description}} - /// {{#description}} - /// {{.}}{{/description}} - {{^conditionalSerialization}} - [DataMember(Name = "{{baseName}}"{{#required}}, IsRequired = false{{/required}}, EmitDefaultValue = {{#vendorExtensions.x-emit-default-value}}true{{/vendorExtensions.x-emit-default-value}}{{^vendorExtensions.x-emit-default-value}}{{#required}}false{{/required}}{{^required}}{{#isBoolean}}false{{/isBoolean}}{{^isBoolean}}{{#isNullable}}false{{/isNullable}}{{^isNullable}}false{{/isNullable}}{{/isBoolean}}{{/required}}{{/vendorExtensions.x-emit-default-value}})] - {{#isDate}} - [JsonConverter(typeof(OpenAPIDateConverter))] - {{/isDate}} - {{#deprecated}} - [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] - {{/deprecated}} - public {{{dataType}}}{{#isNumeric}}?{{/isNumeric}}{{#isBoolean}}{{^isNullable}}?{{/isNullable}}{{/isBoolean}} {{name}} { get; {{#isReadOnly}}private {{/isReadOnly}}set; } - - {{/conditionalSerialization}} - {{#conditionalSerialization}} - {{#isReadOnly}} - [DataMember(Name = "{{baseName}}"{{#required}}, IsRequired = false{{/required}}, EmitDefaultValue = {{#vendorExtensions.x-emit-default-value}}true{{/vendorExtensions.x-emit-default-value}}{{^vendorExtensions.x-emit-default-value}}{{#required}}true{{/required}}{{^required}}{{#isBoolean}}false{{/isBoolean}}{{^isBoolean}}{{#isNullable}}true{{/isNullable}}{{^isNullable}}false{{/isNullable}}{{/isBoolean}}{{/required}}{{/vendorExtensions.x-emit-default-value}})] - {{#isDate}} - [JsonConverter(typeof(OpenAPIDateConverter))] - {{/isDate}} - {{#deprecated}} - [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] - {{/deprecated}} - public {{{dataType}}} {{name}} { get; private set; } - - {{/isReadOnly}} - {{^isReadOnly}} - {{#isDate}} - [JsonConverter(typeof(OpenAPIDateConverter))] - {{/isDate}} - [DataMember(Name = "{{baseName}}"{{#required}}, IsRequired = false{{/required}}, EmitDefaultValue = {{#vendorExtensions.x-emit-default-value}}true{{/vendorExtensions.x-emit-default-value}}{{^vendorExtensions.x-emit-default-value}}{{#required}}false{{/required}}{{^required}}{{#isBoolean}}false{{/isBoolean}}{{^isBoolean}}{{#isNullable}}true{{/isNullable}}{{^isNullable}}false{{/isNullable}}{{/isBoolean}}{{/required}}{{/vendorExtensions.x-emit-default-value}})] - {{#deprecated}} - [Obsolete("{{#vendorExtensions.x-deprecatedInVersion}}Deprecated since {{#appName}}{{{.}}}{{/appName}} v{{#vendorExtensions.x-deprecatedInVersion}}{{.}}{{/vendorExtensions.x-deprecatedInVersion}}.{{/vendorExtensions.x-deprecatedInVersion}}{{#vendorExtensions.x-deprecatedMessage}} {{{.}}}{{/vendorExtensions.x-deprecatedMessage}}")] - {{/deprecated}} - public {{{dataType}}} {{name}} - { - get{ return _{{name}};} - set - { - _{{name}} = value; - _flag{{name}} = true; - } - } - private {{{dataType}}} _{{name}}; - private bool _flag{{name}}; - - /// - /// Returns false as {{name}} should not be serialized given that it's read-only. - /// - /// false (boolean) - public bool ShouldSerialize{{name}}() - { - return _flag{{name}}; - } - {{/isReadOnly}} - {{/conditionalSerialization}} - {{/isEnum}} - {{/isInherited}} - {{/vars}} - {{#isAdditionalPropertiesTrue}} - /// - /// Gets or Sets additional properties - /// - [JsonExtensionData] - public IDictionary AdditionalProperties { get; set; } - - {{/isAdditionalPropertiesTrue}} - /// - /// Returns the string presentation of the object - /// - /// String presentation of the object - public override string ToString() - { - StringBuilder sb = new StringBuilder(); - sb.Append("class {{classname}} {\n"); - {{#parent}} - sb.Append(" ").Append(base.ToString().Replace("\n", "\n ")).Append("\n"); - {{/parent}} - {{#vars}} - sb.Append(" {{name}}: ").Append({{name}}).Append("\n"); - {{/vars}} - {{#isAdditionalPropertiesTrue}} - sb.Append(" AdditionalProperties: ").Append(AdditionalProperties).Append("\n"); - {{/isAdditionalPropertiesTrue}} - sb.Append("}\n"); - return sb.ToString(); - } - - /// - /// Returns the JSON string presentation of the object - /// - /// JSON string presentation of the object - public {{#parent}}{{^isArray}}{{^isMap}}override {{/isMap}}{{/isArray}}{{/parent}}{{^parent}}virtual {{/parent}}string ToJson() - { - return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); - } - - /// - /// Returns true if objects are equal - /// - /// Object to be compared - /// Boolean - public override bool Equals(object input) - { - {{#useCompareNetObjects}} - return OpenAPIClientUtils.compareLogic.Compare(this, input as {{classname}}).AreEqual; - {{/useCompareNetObjects}} - {{^useCompareNetObjects}} - return this.Equals(input as {{classname}}); - {{/useCompareNetObjects}} - } - - /// - /// Returns true if {{classname}} instances are equal - /// - /// Instance of {{classname}} to be compared - /// Boolean - public bool Equals({{classname}} input) - { - {{#useCompareNetObjects}} - return OpenAPIClientUtils.compareLogic.Compare(this, input).AreEqual; - {{/useCompareNetObjects}} - {{^useCompareNetObjects}} - if (input == null) - { - return false; - } - return {{#vars}}{{#parent}}base.Equals(input) && {{/parent}}{{^isContainer}} - ( - this.{{name}} == input.{{name}} || - {{^vendorExtensions.x-is-value-type}} - (this.{{name}} != null && - this.{{name}}.Equals(input.{{name}})) - {{/vendorExtensions.x-is-value-type}} - {{#vendorExtensions.x-is-value-type}} - this.{{name}}.Equals(input.{{name}}) - {{/vendorExtensions.x-is-value-type}} - ){{^-last}} && {{/-last}}{{/isContainer}}{{#isContainer}} - ( - this.{{name}} == input.{{name}} || - {{^vendorExtensions.x-is-value-type}}this.{{name}} != null && - input.{{name}} != null && - {{/vendorExtensions.x-is-value-type}}this.{{name}}.SequenceEqual(input.{{name}}) - ){{^-last}} && {{/-last}}{{/isContainer}}{{/vars}}{{^vars}}{{#parent}}base.Equals(input){{/parent}}{{^parent}}false{{/parent}}{{/vars}}{{^isAdditionalPropertiesTrue}};{{/isAdditionalPropertiesTrue}} - {{#isAdditionalPropertiesTrue}} - && (this.AdditionalProperties.Count == input.AdditionalProperties.Count && !this.AdditionalProperties.Except(input.AdditionalProperties).Any()); - {{/isAdditionalPropertiesTrue}} - {{/useCompareNetObjects}} - } - - /// - /// Gets the hash code - /// - /// Hash code - public override int GetHashCode() - { - unchecked // Overflow is fine, just wrap - { - {{#parent}} - int hashCode = base.GetHashCode(); - {{/parent}} - {{^parent}} - int hashCode = 41; - {{/parent}} - {{#vars}} - {{^vendorExtensions.x-is-value-type}} - if (this.{{name}} != null) - { - hashCode = (hashCode * 59) + this.{{name}}.GetHashCode(); - } - {{/vendorExtensions.x-is-value-type}} - {{#vendorExtensions.x-is-value-type}} - hashCode = (hashCode * 59) + this.{{name}}.GetHashCode(); - {{/vendorExtensions.x-is-value-type}} - {{/vars}} - {{#isAdditionalPropertiesTrue}} - if (this.AdditionalProperties != null) - { - hashCode = (hashCode * 59) + this.AdditionalProperties.GetHashCode(); - } - {{/isAdditionalPropertiesTrue}} - return hashCode; - } - } -{{#validatable}} -{{>validatable}} -{{/validatable}} - } diff --git a/templates/csharp/modelOneOf.mustache b/templates/csharp/modelOneOf.mustache deleted file mode 100644 index 66721caf6..000000000 --- a/templates/csharp/modelOneOf.mustache +++ /dev/null @@ -1,286 +0,0 @@ -{{#model}} - /// - /// {{description}}{{^description}}{{classname}}{{/description}} - /// - {{#vendorExtensions.x-cls-compliant}} - [CLSCompliant({{{.}}})] - {{/vendorExtensions.x-cls-compliant}} - {{#vendorExtensions.x-com-visible}} - [ComVisible({{{.}}})] - {{/vendorExtensions.x-com-visible}} - [JsonConverter(typeof({{classname}}JsonConverter))] - [DataContract(Name = "{{{name}}}")] - {{>visibility}} partial class {{classname}} : AbstractOpenAPISchema, {{#parent}}{{{.}}}, {{/parent}}IEquatable<{{classname}}>{{#validatable}}, IValidatableObject{{/validatable}} - { - {{#isNullable}} - /// - /// Initializes a new instance of the class. - /// - public {{classname}}() - { - this.IsNullable = true; - this.SchemaType= "oneOf"; - } - - {{/isNullable}} - {{#composedSchemas.oneOf}} - {{^isNull}} - /// - /// Initializes a new instance of the class - /// with the class - /// - /// An instance of {{dataType}}. - public {{classname}}({{{dataType}}} actualInstance) - { - this.IsNullable = {{#model.isNullable}}true{{/model.isNullable}}{{^model.isNullable}}false{{/model.isNullable}}; - this.SchemaType= "oneOf"; - this.ActualInstance = actualInstance{{^model.isNullable}}{{^isPrimitiveType}} ?? throw new ArgumentException("Invalid instance found. Must not be null."){{/isPrimitiveType}}{{#isPrimitiveType}}{{#isArray}} ?? throw new ArgumentException("Invalid instance found. Must not be null."){{/isArray}}{{/isPrimitiveType}}{{#isPrimitiveType}}{{#isFreeFormObject}} ?? throw new ArgumentException("Invalid instance found. Must not be null."){{/isFreeFormObject}}{{/isPrimitiveType}}{{#isPrimitiveType}}{{#isString}} ?? throw new ArgumentException("Invalid instance found. Must not be null."){{/isString}}{{/isPrimitiveType}}{{/model.isNullable}}; - } - - {{/isNull}} - {{/composedSchemas.oneOf}} - - private Object _actualInstance; - - /// - /// Gets or Sets ActualInstance - /// - public override Object ActualInstance - { - get - { - return _actualInstance; - } - set - { - {{#oneOf}} - {{^-first}}else {{/-first}}if (value.GetType() == typeof({{{.}}})) - { - this._actualInstance = value; - } - {{/oneOf}} - else - { - throw new ArgumentException("Invalid instance found. Must be the following types:{{#oneOf}} {{{.}}}{{^-last}},{{/-last}}{{/oneOf}}"); - } - } - } - {{#composedSchemas.oneOf}} - {{^isNull}} - - /// - /// Get the actual instance of `{{dataType}}`. If the actual instance is not `{{dataType}}`, - /// the InvalidClassException will be thrown - /// - /// An instance of {{dataType}} - public {{{dataType}}} Get{{#lambda.titlecase}}{{baseType}}{{/lambda.titlecase}}{{#isArray}}{{#lambda.titlecase}}{{{dataFormat}}}{{/lambda.titlecase}}{{/isArray}}() - { - return ({{{dataType}}})this.ActualInstance; - } - {{/isNull}} - {{/composedSchemas.oneOf}} - - /// - /// Returns the string presentation of the object - /// - /// String presentation of the object - public override string ToString() - { - var sb = new StringBuilder(); - sb.Append("class {{classname}} {\n"); - sb.Append(" ActualInstance: ").Append(this.ActualInstance).Append("\n"); - sb.Append("}\n"); - return sb.ToString(); - } - - /// - /// Returns the JSON string presentation of the object - /// - /// JSON string presentation of the object - public override string ToJson() - { - return JsonConvert.SerializeObject(this.ActualInstance, {{classname}}.SerializerSettings); - } - - /// - /// Converts the JSON string into an instance of {{classname}} - /// - /// JSON string - /// An instance of {{classname}} - public static {{classname}} FromJson(string jsonString) - { - {{classname}} new{{classname}} = null; - - if (string.IsNullOrEmpty(jsonString)) - { - return new{{classname}}; - } - {{#useOneOfDiscriminatorLookup}} - {{#discriminator}} - - try - { - var discriminatorObj = JObject.Parse(jsonString)["{{{propertyBaseName}}}"]; - string discriminatorValue = discriminatorObj == null ?string.Empty :discriminatorObj.ToString(); - switch (discriminatorValue) - { - {{#mappedModels}} - case "{{{mappingName}}}": - new{{classname}} = new {{classname}}(JsonConvert.DeserializeObject<{{{modelName}}}>(jsonString, {{classname}}.AdditionalPropertiesSerializerSettings)); - return new{{classname}}; - {{/mappedModels}} - default: - System.Diagnostics.Debug.WriteLine(string.Format("Failed to lookup discriminator value `{0}` for {{classname}}. Possible values:{{#mappedModels}} {{{mappingName}}}{{/mappedModels}}", discriminatorValue)); - break; - } - } - catch (Exception ex) - { - System.Diagnostics.Debug.WriteLine(string.Format("Failed to parse the json data : `{0}` {1}", jsonString, ex.ToString())); - } - - {{/discriminator}} - {{/useOneOfDiscriminatorLookup}} - int match = 0; - List matchedTypes = new List(); - JToken typeToken = JObject.Parse(jsonString).GetValue("type"); - string type = typeToken?.Value(); - // Throw exception if jsonString does not contain type param - if (type == null) - { - throw new InvalidDataException("JsonString does not contain required enum type for deserialization."); - } - try - { - {{#oneOf}} - // Check if the jsonString type enum matches the {{{.}}} type enums - if (ContainsValue<{{{.}}}.TypeEnum>(type)) - { - new{{classname}} = new {{classname}}(JsonConvert.DeserializeObject<{{{.}}}>(jsonString, {{classname}}.SerializerSettings)); - matchedTypes.Add("{{{.}}}"); - match++; - } - {{/oneOf}} - } - catch (Exception ex) - { - if (!(ex is JsonSerializationException)) - { - throw new InvalidDataException(string.Format("Failed to deserialize `{0}` into target: {1}", jsonString, ex.ToString())); - } - } - - if (match != 1) - { - throw new InvalidDataException("The JSON string `" + jsonString + "` cannot be deserialized into any schema defined. MatchedTypes are: " + matchedTypes); - } - - // deserialization is considered successful at this point if no exception has been thrown. - return new{{classname}}; - } - - /// - /// Returns true if objects are equal - /// - /// Object to be compared - /// Boolean - public override bool Equals(object input) - { - {{#useCompareNetObjects}} - return OpenAPIClientUtils.compareLogic.Compare(this, input as {{classname}}).AreEqual; - {{/useCompareNetObjects}} - {{^useCompareNetObjects}} - return this.Equals(input as {{classname}}); - {{/useCompareNetObjects}} - } - - /// - /// Returns true if {{classname}} instances are equal - /// - /// Instance of {{classname}} to be compared - /// Boolean - public bool Equals({{classname}} input) - { - {{#useCompareNetObjects}} - return OpenAPIClientUtils.compareLogic.Compare(this, input).AreEqual; - {{/useCompareNetObjects}} - {{^useCompareNetObjects}} - if (input == null) - return false; - - return this.ActualInstance.Equals(input.ActualInstance); - {{/useCompareNetObjects}} - } - - /// - /// Gets the hash code - /// - /// Hash code - public override int GetHashCode() - { - unchecked // Overflow is fine, just wrap - { - int hashCode = 41; - if (this.ActualInstance != null) - hashCode = hashCode * 59 + this.ActualInstance.GetHashCode(); - return hashCode; - } - } - - {{#validatable}} - /// - /// To validate all properties of the instance - /// - /// Validation context - /// Validation Result - IEnumerable IValidatableObject.Validate(ValidationContext validationContext) - { - yield break; - } - {{/validatable}} - } - - /// - /// Custom JSON converter for {{classname}} - /// - public class {{classname}}JsonConverter : JsonConverter - { - /// - /// To write the JSON string - /// - /// JSON writer - /// Object to be converted into a JSON string - /// JSON Serializer - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - writer.WriteRawValue((string)(typeof({{classname}}).GetMethod("ToJson").Invoke(value, null))); - } - - /// - /// To convert a JSON string into an object - /// - /// JSON reader - /// Object type - /// Existing value - /// JSON Serializer - /// The object converted from the JSON string - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - if(reader.TokenType != JsonToken.Null) - { - return {{classname}}.FromJson(JObject.Load(reader).ToString(Formatting.None)); - } - return null; - } - - /// - /// Check if the object can be converted - /// - /// Object type - /// True if the object can be converted - public override bool CanConvert(Type objectType) - { - return false; - } - } -{{/model}} diff --git a/templates/csharp/partial_header.mustache b/templates/csharp/partial_header.mustache deleted file mode 100644 index a4c7dd929..000000000 --- a/templates/csharp/partial_header.mustache +++ /dev/null @@ -1,12 +0,0 @@ -/* -{{#appName}} -* {{{.}}} -* -{{/appName}} -* -* {{#version}}The version of the OpenAPI document: {{{.}}}{{/version}} -* -* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). -* https://openapi-generator.tech -* Do not edit the class manually. -*/