From 06003fa405fb86ab0631708188055d9f6cd83a03 Mon Sep 17 00:00:00 2001 From: david ruiz Date: Mon, 17 Nov 2025 18:59:17 +0100 Subject: [PATCH 01/18] Created agentic/enroll endpoint --- src/CheckoutSdk/Agentic/AgenticClient.cs | 39 ++++ src/CheckoutSdk/Agentic/IAgenticClient.cs | 22 +++ .../Agentic/Requests/AgenticEnrollRequest.cs | 25 +++ .../Agentic/Requests/DeviceInfo.cs | 28 +++ .../Agentic/Requests/PaymentSource.cs | 35 ++++ .../Responses/AgenticEnrollResponse.cs | 26 +++ src/CheckoutSdk/CheckoutApi.cs | 7 + src/CheckoutSdk/ICheckoutApi.cs | 3 + .../Agentic/AgenticClientTest.cs | 149 ++++++++++++++ .../Agentic/AgenticIntegrationTest.cs | 181 ++++++++++++++++++ 10 files changed, 515 insertions(+) create mode 100644 src/CheckoutSdk/Agentic/AgenticClient.cs create mode 100644 src/CheckoutSdk/Agentic/IAgenticClient.cs create mode 100644 src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs create mode 100644 src/CheckoutSdk/Agentic/Requests/DeviceInfo.cs create mode 100644 src/CheckoutSdk/Agentic/Requests/PaymentSource.cs create mode 100644 src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs create mode 100644 test/CheckoutSdkTest/Agentic/AgenticClientTest.cs create mode 100644 test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs diff --git a/src/CheckoutSdk/Agentic/AgenticClient.cs b/src/CheckoutSdk/Agentic/AgenticClient.cs new file mode 100644 index 00000000..6781b42c --- /dev/null +++ b/src/CheckoutSdk/Agentic/AgenticClient.cs @@ -0,0 +1,39 @@ +using Checkout.Agentic.Requests; +using Checkout.Agentic.Responses; +using Checkout.Common; +using System.Threading; +using System.Threading.Tasks; + +namespace Checkout.Agentic +{ + /// + /// Agentic API Client + /// + public class AgenticClient : AbstractClient, IAgenticClient + { + private const string AgenticPath = "agentic"; + private const string EnrollPath = "enroll"; + + public AgenticClient(IApiClient apiClient, CheckoutConfiguration configuration) : + base(apiClient, configuration, SdkAuthorizationType.SecretKeyOrOAuth) + { + } + + /// + /// Enroll in agentic services + /// Enrolls a card for use with agentic commerce. + /// + public Task Enroll( + AgenticEnrollRequest agenticEnrollRequest, + CancellationToken cancellationToken = default) + { + CheckoutUtils.ValidateParams("agenticEnrollRequest", agenticEnrollRequest); + return ApiClient.Post( + BuildPath(AgenticPath, EnrollPath), + SdkAuthorization(), + agenticEnrollRequest, + cancellationToken + ); + } + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/IAgenticClient.cs b/src/CheckoutSdk/Agentic/IAgenticClient.cs new file mode 100644 index 00000000..e3937c07 --- /dev/null +++ b/src/CheckoutSdk/Agentic/IAgenticClient.cs @@ -0,0 +1,22 @@ +using Checkout.Agentic.Requests; +using Checkout.Agentic.Responses; +using Checkout.Common; +using System.Threading; +using System.Threading.Tasks; + +namespace Checkout.Agentic +{ + /// + /// Agentic API Client + /// + public interface IAgenticClient + { + /// + /// Enroll in agentic services + /// Enrolls a merchant or entity in agentic services + /// + Task Enroll( + AgenticEnrollRequest agenticEnrollRequest, + CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs b/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs new file mode 100644 index 00000000..86babaa2 --- /dev/null +++ b/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs @@ -0,0 +1,25 @@ +using Checkout.Common; + +namespace Checkout.Agentic.Requests +{ + /// + /// Agentic Enroll Request + /// + public class AgenticEnrollRequest + { + /// + /// Payment source information + /// + public PaymentSource Source { get; set; } + + /// + /// Device information for fraud detection and analysis + /// + public DeviceInfo Device { get; set; } + + /// + /// Customer information + /// + public CustomerRequest Customer { get; set; } + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Requests/DeviceInfo.cs b/src/CheckoutSdk/Agentic/Requests/DeviceInfo.cs new file mode 100644 index 00000000..227a60f5 --- /dev/null +++ b/src/CheckoutSdk/Agentic/Requests/DeviceInfo.cs @@ -0,0 +1,28 @@ +namespace Checkout.Agentic.Requests +{ + /// + /// Device Information + /// + public class DeviceInfo + { + /// + /// IP address of the device + /// + public string IpAddress { get; set; } + + /// + /// User agent string from the browser + /// + public string UserAgent { get; set; } + + /// + /// Device brand (e.g., "apple", "samsung") + /// + public string DeviceBrand { get; set; } + + /// + /// Device type (e.g., "tablet", "mobile", "desktop") + /// + public string DeviceType { get; set; } + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Requests/PaymentSource.cs b/src/CheckoutSdk/Agentic/Requests/PaymentSource.cs new file mode 100644 index 00000000..e1232ce0 --- /dev/null +++ b/src/CheckoutSdk/Agentic/Requests/PaymentSource.cs @@ -0,0 +1,35 @@ +using Checkout.Common; + +namespace Checkout.Agentic.Requests +{ + /// + /// Payment Source for Agentic Enrollment + /// + public class PaymentSource + { + /// + /// Card number + /// + public string Number { get; set; } + + /// + /// Card expiry month (1-12) + /// + public int? ExpiryMonth { get; set; } + + /// + /// Card expiry year (e.g., 2025) + /// + public int? ExpiryYear { get; set; } + + /// + /// Card verification value (CVV) + /// + public string Cvv { get; set; } + + /// + /// Payment source type + /// + public PaymentSourceType? Type { get; set; } + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs b/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs new file mode 100644 index 00000000..784fa804 --- /dev/null +++ b/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs @@ -0,0 +1,26 @@ +using System; +using Checkout.Common; + +namespace Checkout.Agentic.Responses +{ + /// + /// Agentic Enroll Response + /// + public class AgenticEnrollResponse : HttpMetadata + { + /// + /// The unique token identifier for the enrolled agentic service + /// + public string TokenId { get; set; } + + /// + /// Current status of the enrollment (e.g., "enrolled") + /// + public string Status { get; set; } + + /// + /// The timestamp when the enrollment was created + /// + public DateTime CreatedAt { get; set; } + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/CheckoutApi.cs b/src/CheckoutSdk/CheckoutApi.cs index db137cd4..d9d0b119 100644 --- a/src/CheckoutSdk/CheckoutApi.cs +++ b/src/CheckoutSdk/CheckoutApi.cs @@ -1,4 +1,5 @@ using Checkout.Accounts; +using Checkout.Agentic; using Checkout.Authentication; using Checkout.Balances; using Checkout.Issuing; @@ -47,6 +48,7 @@ public class CheckoutApi : ICheckoutApi private readonly IPaymentSessionsClient _paymentSessionsClient; private readonly IForwardClient _forwardClient; private readonly INetworkTokensClient _networkTokensClient; + private readonly IAgenticClient _agenticClient; public CheckoutApi(CheckoutConfiguration configuration) { @@ -78,6 +80,7 @@ public CheckoutApi(CheckoutConfiguration configuration) _paymentSessionsClient = new PaymentSessionsClient(baseApiClient, configuration); _forwardClient = new ForwardClient(baseApiClient, configuration); _networkTokensClient = new NetworkTokensClient(baseApiClient, configuration); + _agenticClient = new AgenticClient(baseApiClient, configuration); } private static ApiClient BaseApiClient(CheckoutConfiguration configuration) @@ -221,5 +224,9 @@ public INetworkTokensClient NetworkTokensClient() return _networkTokensClient; } + public IAgenticClient AgenticClient() + { + return _agenticClient; + } } } diff --git a/src/CheckoutSdk/ICheckoutApi.cs b/src/CheckoutSdk/ICheckoutApi.cs index 37262804..77e51028 100644 --- a/src/CheckoutSdk/ICheckoutApi.cs +++ b/src/CheckoutSdk/ICheckoutApi.cs @@ -1,4 +1,5 @@ using Checkout.Accounts; +using Checkout.Agentic; using Checkout.Authentication; using Checkout.Balances; using Checkout.Issuing; @@ -68,5 +69,7 @@ public interface ICheckoutApi : ICheckoutApiClient IForwardClient ForwardClient(); INetworkTokensClient NetworkTokensClient(); + + IAgenticClient AgenticClient(); } } diff --git a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs new file mode 100644 index 00000000..10eb7390 --- /dev/null +++ b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs @@ -0,0 +1,149 @@ +using System.Threading; +using System.Threading.Tasks; +using Moq; +using Shouldly; +using Xunit; +using Checkout.Agentic.Requests; +using Checkout.Agentic.Responses; +using Checkout.Common; + +namespace Checkout.Agentic +{ + public class AgenticClientTest : UnitTestFixture + { + private readonly SdkAuthorization _authorization = new SdkAuthorization(PlatformType.Default, ValidDefaultSk); + private readonly Mock _apiClient = new Mock(); + private readonly Mock _sdkCredentials = new Mock(PlatformType.Default); + private readonly Mock _httpClientFactory = new Mock(); + private readonly Mock _configuration; + + public AgenticClientTest() + { + _sdkCredentials.Setup(credentials => credentials.GetSdkAuthorization(SdkAuthorizationType.SecretKeyOrOAuth)) + .Returns(_authorization); + + _configuration = new Mock(_sdkCredentials.Object, + Environment.Sandbox, _httpClientFactory.Object); + } + + [Fact] + private async Task ShouldEnrollInAgenticServices() + { + var agenticEnrollRequest = new AgenticEnrollRequest + { + Source = new PaymentSource + { + Number = "4242424242424242", + ExpiryMonth = 12, + ExpiryYear = 2025, + Cvv = "123", + Type = PaymentSourceType.Card + }, + Device = new DeviceInfo + { + IpAddress = "192.168.1.1", + UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + }, + Customer = new CustomerRequest + { + Email = "test@example.com", + Name = "Test Customer" + } + }; + + var expectedResponse = new AgenticEnrollResponse + { + TokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + Status = "enrolled", + CreatedAt = System.DateTime.UtcNow + }; + + _apiClient.Setup(apiClient => + apiClient.Post("agentic/enroll", _authorization, agenticEnrollRequest, + CancellationToken.None, null)) + .ReturnsAsync(() => expectedResponse); + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + var response = await client.Enroll(agenticEnrollRequest, CancellationToken.None); + + response.ShouldNotBeNull(); + response.TokenId.ShouldBe(expectedResponse.TokenId); + response.Status.ShouldBe(expectedResponse.Status); + response.CreatedAt.ShouldBe(expectedResponse.CreatedAt); + } + + [Fact] + private async Task ShouldThrowExceptionWhenRequestIsNull() + { + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + var exception = await Should.ThrowAsync( + async () => await client.Enroll(null, CancellationToken.None)); + + exception.Message.ShouldContain("agenticEnrollRequest"); + } + + [Fact] + private async Task ShouldCallCorrectEndpoint() + { + var agenticEnrollRequest = new AgenticEnrollRequest + { + Source = new PaymentSource + { + Number = "4242424242424242", + ExpiryMonth = 12, + ExpiryYear = 2025, + Cvv = "123", + Type = PaymentSourceType.Card + }, + Device = new DeviceInfo + { + IpAddress = "192.168.1.1", + UserAgent = "Mozilla/5.0 Test" + }, + Customer = new CustomerRequest + { + Email = "test@example.com" + } + }; + + _apiClient.Setup(apiClient => + apiClient.Post("agentic/enroll", _authorization, agenticEnrollRequest, + CancellationToken.None, null)) + .ReturnsAsync(() => new AgenticEnrollResponse()); + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + await client.Enroll(agenticEnrollRequest, CancellationToken.None); + + _apiClient.Verify(apiClient => + apiClient.Post("agentic/enroll", _authorization, agenticEnrollRequest, + CancellationToken.None, null), Times.Once); + } + + [Fact] + private async Task ShouldUseCorrectAuthorization() + { + var agenticEnrollRequest = new AgenticEnrollRequest + { + Source = new PaymentSource { Type = PaymentSourceType.Card }, + Device = new DeviceInfo(), + Customer = new CustomerRequest() + }; + + _apiClient.Setup(apiClient => + apiClient.Post(It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(() => new AgenticEnrollResponse()); + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + await client.Enroll(agenticEnrollRequest, CancellationToken.None); + + _apiClient.Verify(apiClient => + apiClient.Post(It.IsAny(), _authorization, + It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + } + } +} \ No newline at end of file diff --git a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs new file mode 100644 index 00000000..d9a533c5 --- /dev/null +++ b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs @@ -0,0 +1,181 @@ +using System; +using System.Threading.Tasks; +using Shouldly; +using Xunit; +using Checkout.Agentic; +using Checkout.Agentic.Requests; +using Checkout.Common; + +namespace Checkout.Agentic +{ + public class AgenticIntegrationTest : SandboxTestFixture + { + public AgenticIntegrationTest() : base(PlatformType.Default) + { + } + + [Fact] + private async Task ShouldEnrollInAgenticServices() + { + var agenticEnrollRequest = new AgenticEnrollRequest + { + Source = new PaymentSource + { + Number = "4242424242424242", + ExpiryMonth = 12, + ExpiryYear = 2025, + Cvv = "123", + Type = PaymentSourceType.Card + }, + Device = new DeviceInfo + { + IpAddress = "192.168.1.100", + UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", + DeviceBrand = "Chrome", + DeviceType = "desktop" + }, + Customer = new CustomerRequest + { + Email = GenerateRandomEmail(), + Name = "Agentic Test Customer", + Phone = new Phone { CountryCode = "+1", Number = "4155552671" } + } + }; + + var response = await DefaultApi.AgenticClient().Enroll(agenticEnrollRequest); + + response.ShouldNotBeNull(); + response.TokenId.ShouldNotBeNullOrEmpty(); + response.Status.ShouldNotBeNullOrEmpty(); + response.CreatedAt.ShouldNotBe(default(DateTime)); + + // Validate that token_id follows expected pattern + response.TokenId.ShouldStartWith("nt_"); + + // Validate status is one of expected values + response.Status.ShouldBe("enrolled"); + } + + [Fact] + private async Task ShouldEnrollWithMinimalData() + { + var agenticEnrollRequest = new AgenticEnrollRequest + { + Source = new PaymentSource + { + Number = "4242424242424242", + ExpiryMonth = 6, + ExpiryYear = 2026, + Cvv = "100", + Type = PaymentSourceType.Card + }, + Device = new DeviceInfo + { + IpAddress = "10.0.0.1" + }, + Customer = new CustomerRequest + { + Email = GenerateRandomEmail() + } + }; + + var response = await DefaultApi.AgenticClient().Enroll(agenticEnrollRequest); + + response.ShouldNotBeNull(); + response.TokenId.ShouldNotBeNullOrEmpty(); + response.Status.ShouldBe("enrolled"); + } + + [Fact] + private async Task ShouldHandleDifferentCardTypes() + { + // Test with Visa card + var visaRequest = new AgenticEnrollRequest + { + Source = new PaymentSource + { + Number = "4242424242424242", // Visa test card + ExpiryMonth = 8, + ExpiryYear = 2027, + Cvv = "888", + Type = PaymentSourceType.Card + }, + Device = new DeviceInfo + { + IpAddress = "203.0.113.195", + UserAgent = "Test Agent" + }, + Customer = new CustomerRequest + { + Email = GenerateRandomEmail(), + Name = "Visa Test Customer" + } + }; + + var visaResponse = await DefaultApi.AgenticClient().Enroll(visaRequest); + visaResponse.ShouldNotBeNull(); + visaResponse.Status.ShouldBe("enrolled"); + + // Test with Mastercard + var mastercardRequest = new AgenticEnrollRequest + { + Source = new PaymentSource + { + Number = "5555555555554444", // Mastercard test card + ExpiryMonth = 9, + ExpiryYear = 2028, + Cvv = "999", + Type = PaymentSourceType.Card + }, + Device = new DeviceInfo + { + IpAddress = "198.51.100.42" + }, + Customer = new CustomerRequest + { + Email = GenerateRandomEmail(), + Name = "Mastercard Test Customer" + } + }; + + var mastercardResponse = await DefaultApi.AgenticClient().Enroll(mastercardRequest); + mastercardResponse.ShouldNotBeNull(); + mastercardResponse.Status.ShouldBe("enrolled"); + } + + [Fact] + private async Task ShouldHandleInternationalCustomers() + { + var internationalRequest = new AgenticEnrollRequest + { + Source = new PaymentSource + { + Number = "4242424242424242", + ExpiryMonth = 3, + ExpiryYear = 2029, + Cvv = "314", + Type = PaymentSourceType.Card + }, + Device = new DeviceInfo + { + IpAddress = "80.112.12.34", // European IP + UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36", + DeviceBrand = "Safari", + DeviceType = "mobile" + }, + Customer = new CustomerRequest + { + Email = GenerateRandomEmail(), + Name = "José María García-López", + Phone = new Phone { CountryCode = "+34", Number = "612345678" } + } + }; + + var response = await DefaultApi.AgenticClient().Enroll(internationalRequest); + + response.ShouldNotBeNull(); + response.Status.ShouldBe("enrolled"); + response.TokenId.ShouldStartWith("nt_"); + } + } +} \ No newline at end of file From 408892c78f7f6010469ebb0aec1aec8b0e3bb047 Mon Sep 17 00:00:00 2001 From: david ruiz Date: Tue, 18 Nov 2025 09:54:00 +0100 Subject: [PATCH 02/18] JSONProperties added to match the snake name in the examples --- .../Agentic/Requests/AgenticCustomer.cs | 29 +++++++++++++++++++ .../Agentic/Requests/AgenticEnrollRequest.cs | 7 +++-- .../Agentic/Requests/DeviceInfo.cs | 6 ++++ .../Agentic/Requests/PaymentSource.cs | 6 ++++ .../Agentic/AgenticClientTest.cs | 13 +++++---- .../Agentic/AgenticIntegrationTest.cs | 28 ++++++++++-------- 6 files changed, 70 insertions(+), 19 deletions(-) create mode 100644 src/CheckoutSdk/Agentic/Requests/AgenticCustomer.cs diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticCustomer.cs b/src/CheckoutSdk/Agentic/Requests/AgenticCustomer.cs new file mode 100644 index 00000000..2844d833 --- /dev/null +++ b/src/CheckoutSdk/Agentic/Requests/AgenticCustomer.cs @@ -0,0 +1,29 @@ +using Checkout.Common; +using Newtonsoft.Json; + +namespace Checkout.Agentic.Requests +{ + /// + /// Customer information for agentic enrollment + /// + public class AgenticCustomer + { + /// + /// Customer email address + /// + [JsonProperty("email")] + public string Email { get; set; } + + /// + /// Customer country code (ISO 3166-1 alpha-2) + /// + [JsonProperty("country_code")] + public CountryCode CountryCode { get; set; } + + /// + /// Customer language code (ISO 639-1) + /// + [JsonProperty("language_code")] + public string LanguageCode { get; set; } + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs b/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs index 86babaa2..93d25545 100644 --- a/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs +++ b/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs @@ -1,4 +1,4 @@ -using Checkout.Common; +using Newtonsoft.Json; namespace Checkout.Agentic.Requests { @@ -10,16 +10,19 @@ public class AgenticEnrollRequest /// /// Payment source information /// + [JsonProperty("source")] public PaymentSource Source { get; set; } /// /// Device information for fraud detection and analysis /// + [JsonProperty("device")] public DeviceInfo Device { get; set; } /// /// Customer information /// - public CustomerRequest Customer { get; set; } + [JsonProperty("customer")] + public AgenticCustomer Customer { get; set; } } } \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Requests/DeviceInfo.cs b/src/CheckoutSdk/Agentic/Requests/DeviceInfo.cs index 227a60f5..cf7cc9b3 100644 --- a/src/CheckoutSdk/Agentic/Requests/DeviceInfo.cs +++ b/src/CheckoutSdk/Agentic/Requests/DeviceInfo.cs @@ -1,3 +1,5 @@ +using Newtonsoft.Json; + namespace Checkout.Agentic.Requests { /// @@ -8,21 +10,25 @@ public class DeviceInfo /// /// IP address of the device /// + [JsonProperty("ip_address")] public string IpAddress { get; set; } /// /// User agent string from the browser /// + [JsonProperty("user_agent")] public string UserAgent { get; set; } /// /// Device brand (e.g., "apple", "samsung") /// + [JsonProperty("device_brand")] public string DeviceBrand { get; set; } /// /// Device type (e.g., "tablet", "mobile", "desktop") /// + [JsonProperty("device_type")] public string DeviceType { get; set; } } } \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Requests/PaymentSource.cs b/src/CheckoutSdk/Agentic/Requests/PaymentSource.cs index e1232ce0..f84b9f31 100644 --- a/src/CheckoutSdk/Agentic/Requests/PaymentSource.cs +++ b/src/CheckoutSdk/Agentic/Requests/PaymentSource.cs @@ -1,4 +1,5 @@ using Checkout.Common; +using Newtonsoft.Json; namespace Checkout.Agentic.Requests { @@ -10,26 +11,31 @@ public class PaymentSource /// /// Card number /// + [JsonProperty("number")] public string Number { get; set; } /// /// Card expiry month (1-12) /// + [JsonProperty("expiry_month")] public int? ExpiryMonth { get; set; } /// /// Card expiry year (e.g., 2025) /// + [JsonProperty("expiry_year")] public int? ExpiryYear { get; set; } /// /// Card verification value (CVV) /// + [JsonProperty("cvv")] public string Cvv { get; set; } /// /// Payment source type /// + [JsonProperty("type")] public PaymentSourceType? Type { get; set; } } } \ No newline at end of file diff --git a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs index 10eb7390..f7d01a56 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs +++ b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs @@ -44,10 +44,11 @@ private async Task ShouldEnrollInAgenticServices() IpAddress = "192.168.1.1", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" }, - Customer = new CustomerRequest + Customer = new AgenticCustomer { Email = "test@example.com", - Name = "Test Customer" + CountryCode = CountryCode.US, + LanguageCode = "en" } }; @@ -102,9 +103,11 @@ private async Task ShouldCallCorrectEndpoint() IpAddress = "192.168.1.1", UserAgent = "Mozilla/5.0 Test" }, - Customer = new CustomerRequest + Customer = new AgenticCustomer { - Email = "test@example.com" + Email = "test@example.com", + CountryCode = CountryCode.US, + LanguageCode = "en" } }; @@ -129,7 +132,7 @@ private async Task ShouldUseCorrectAuthorization() { Source = new PaymentSource { Type = PaymentSourceType.Card }, Device = new DeviceInfo(), - Customer = new CustomerRequest() + Customer = new AgenticCustomer { Email = "test@example.com", CountryCode = CountryCode.US, LanguageCode = "en" } }; _apiClient.Setup(apiClient => diff --git a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs index d9a533c5..ea23cd57 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs +++ b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs @@ -34,11 +34,11 @@ private async Task ShouldEnrollInAgenticServices() DeviceBrand = "Chrome", DeviceType = "desktop" }, - Customer = new CustomerRequest + Customer = new AgenticCustomer { Email = GenerateRandomEmail(), - Name = "Agentic Test Customer", - Phone = new Phone { CountryCode = "+1", Number = "4155552671" } + CountryCode = CountryCode.US, + LanguageCode = "en" } }; @@ -73,9 +73,11 @@ private async Task ShouldEnrollWithMinimalData() { IpAddress = "10.0.0.1" }, - Customer = new CustomerRequest + Customer = new AgenticCustomer { - Email = GenerateRandomEmail() + Email = GenerateRandomEmail(), + CountryCode = CountryCode.US, + LanguageCode = "en" } }; @@ -105,10 +107,11 @@ private async Task ShouldHandleDifferentCardTypes() IpAddress = "203.0.113.195", UserAgent = "Test Agent" }, - Customer = new CustomerRequest + Customer = new AgenticCustomer { Email = GenerateRandomEmail(), - Name = "Visa Test Customer" + CountryCode = CountryCode.US, + LanguageCode = "en" } }; @@ -131,10 +134,11 @@ private async Task ShouldHandleDifferentCardTypes() { IpAddress = "198.51.100.42" }, - Customer = new CustomerRequest + Customer = new AgenticCustomer { Email = GenerateRandomEmail(), - Name = "Mastercard Test Customer" + CountryCode = CountryCode.US, + LanguageCode = "en" } }; @@ -163,11 +167,11 @@ private async Task ShouldHandleInternationalCustomers() DeviceBrand = "Safari", DeviceType = "mobile" }, - Customer = new CustomerRequest + Customer = new AgenticCustomer { Email = GenerateRandomEmail(), - Name = "José María García-López", - Phone = new Phone { CountryCode = "+34", Number = "612345678" } + CountryCode = CountryCode.ES, + LanguageCode = "es" } }; From fc3d82246d1236e4eb556bdabdf677ae0590e7ae Mon Sep 17 00:00:00 2001 From: david ruiz Date: Tue, 18 Nov 2025 09:59:16 +0100 Subject: [PATCH 03/18] Clen the code and added JSONAtributes for the response --- src/CheckoutSdk/Agentic/AgenticClient.cs | 5 ++--- src/CheckoutSdk/Agentic/IAgenticClient.cs | 5 ++--- src/CheckoutSdk/Agentic/Requests/AgenticCustomer.cs | 2 +- src/CheckoutSdk/Agentic/Requests/PaymentSource.cs | 2 +- src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs | 5 ++++- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/CheckoutSdk/Agentic/AgenticClient.cs b/src/CheckoutSdk/Agentic/AgenticClient.cs index 6781b42c..3ad606e2 100644 --- a/src/CheckoutSdk/Agentic/AgenticClient.cs +++ b/src/CheckoutSdk/Agentic/AgenticClient.cs @@ -1,8 +1,7 @@ -using Checkout.Agentic.Requests; -using Checkout.Agentic.Responses; -using Checkout.Common; using System.Threading; using System.Threading.Tasks; +using Checkout.Agentic.Requests; +using Checkout.Agentic.Responses; namespace Checkout.Agentic { diff --git a/src/CheckoutSdk/Agentic/IAgenticClient.cs b/src/CheckoutSdk/Agentic/IAgenticClient.cs index e3937c07..091b6a2f 100644 --- a/src/CheckoutSdk/Agentic/IAgenticClient.cs +++ b/src/CheckoutSdk/Agentic/IAgenticClient.cs @@ -1,8 +1,7 @@ -using Checkout.Agentic.Requests; -using Checkout.Agentic.Responses; -using Checkout.Common; using System.Threading; using System.Threading.Tasks; +using Checkout.Agentic.Requests; +using Checkout.Agentic.Responses; namespace Checkout.Agentic { diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticCustomer.cs b/src/CheckoutSdk/Agentic/Requests/AgenticCustomer.cs index 2844d833..28721712 100644 --- a/src/CheckoutSdk/Agentic/Requests/AgenticCustomer.cs +++ b/src/CheckoutSdk/Agentic/Requests/AgenticCustomer.cs @@ -1,5 +1,5 @@ -using Checkout.Common; using Newtonsoft.Json; +using Checkout.Common; namespace Checkout.Agentic.Requests { diff --git a/src/CheckoutSdk/Agentic/Requests/PaymentSource.cs b/src/CheckoutSdk/Agentic/Requests/PaymentSource.cs index f84b9f31..2fac3bba 100644 --- a/src/CheckoutSdk/Agentic/Requests/PaymentSource.cs +++ b/src/CheckoutSdk/Agentic/Requests/PaymentSource.cs @@ -1,5 +1,5 @@ -using Checkout.Common; using Newtonsoft.Json; +using Checkout.Common; namespace Checkout.Agentic.Requests { diff --git a/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs b/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs index 784fa804..8ea1317c 100644 --- a/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs +++ b/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs @@ -1,5 +1,5 @@ using System; -using Checkout.Common; +using Newtonsoft.Json; namespace Checkout.Agentic.Responses { @@ -11,16 +11,19 @@ public class AgenticEnrollResponse : HttpMetadata /// /// The unique token identifier for the enrolled agentic service /// + [JsonProperty("token_id")] public string TokenId { get; set; } /// /// Current status of the enrollment (e.g., "enrolled") /// + [JsonProperty("status")] public string Status { get; set; } /// /// The timestamp when the enrollment was created /// + [JsonProperty("created_at")] public DateTime CreatedAt { get; set; } } } \ No newline at end of file From 3651a5f6ec30190aef6e0fa10338df3b299fbe33 Mon Sep 17 00:00:00 2001 From: david ruiz Date: Tue, 18 Nov 2025 10:37:20 +0100 Subject: [PATCH 04/18] Removed JSONAtribute, not needed, added OAuth scopem skipped integration test --- src/CheckoutSdk/Agentic/AgenticClient.cs | 2 +- src/CheckoutSdk/Agentic/Requests/AgenticCustomer.cs | 5 +---- .../Agentic/Requests/AgenticEnrollRequest.cs | 3 --- src/CheckoutSdk/Agentic/Requests/DeviceInfo.cs | 4 ---- src/CheckoutSdk/Agentic/Requests/PaymentSource.cs | 9 ++------- .../Agentic/Responses/AgenticEnrollResponse.cs | 5 +---- src/CheckoutSdk/OAuthScope.cs | 1 + test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs | 10 +++++----- test/CheckoutSdkTest/SandboxTestFixture.cs | 2 +- 9 files changed, 12 insertions(+), 29 deletions(-) diff --git a/src/CheckoutSdk/Agentic/AgenticClient.cs b/src/CheckoutSdk/Agentic/AgenticClient.cs index 3ad606e2..9d698ec7 100644 --- a/src/CheckoutSdk/Agentic/AgenticClient.cs +++ b/src/CheckoutSdk/Agentic/AgenticClient.cs @@ -14,7 +14,7 @@ public class AgenticClient : AbstractClient, IAgenticClient private const string EnrollPath = "enroll"; public AgenticClient(IApiClient apiClient, CheckoutConfiguration configuration) : - base(apiClient, configuration, SdkAuthorizationType.SecretKeyOrOAuth) + base(apiClient, configuration, SdkAuthorizationType.OAuth) { } diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticCustomer.cs b/src/CheckoutSdk/Agentic/Requests/AgenticCustomer.cs index 28721712..425d847a 100644 --- a/src/CheckoutSdk/Agentic/Requests/AgenticCustomer.cs +++ b/src/CheckoutSdk/Agentic/Requests/AgenticCustomer.cs @@ -11,19 +11,16 @@ public class AgenticCustomer /// /// Customer email address /// - [JsonProperty("email")] public string Email { get; set; } /// /// Customer country code (ISO 3166-1 alpha-2) /// - [JsonProperty("country_code")] - public CountryCode CountryCode { get; set; } + public CountryCode? CountryCode { get; set; } /// /// Customer language code (ISO 639-1) /// - [JsonProperty("language_code")] public string LanguageCode { get; set; } } } \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs b/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs index 93d25545..a6eb089f 100644 --- a/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs +++ b/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs @@ -10,19 +10,16 @@ public class AgenticEnrollRequest /// /// Payment source information /// - [JsonProperty("source")] public PaymentSource Source { get; set; } /// /// Device information for fraud detection and analysis /// - [JsonProperty("device")] public DeviceInfo Device { get; set; } /// /// Customer information /// - [JsonProperty("customer")] public AgenticCustomer Customer { get; set; } } } \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Requests/DeviceInfo.cs b/src/CheckoutSdk/Agentic/Requests/DeviceInfo.cs index cf7cc9b3..f0bfb87c 100644 --- a/src/CheckoutSdk/Agentic/Requests/DeviceInfo.cs +++ b/src/CheckoutSdk/Agentic/Requests/DeviceInfo.cs @@ -10,25 +10,21 @@ public class DeviceInfo /// /// IP address of the device /// - [JsonProperty("ip_address")] public string IpAddress { get; set; } /// /// User agent string from the browser /// - [JsonProperty("user_agent")] public string UserAgent { get; set; } /// /// Device brand (e.g., "apple", "samsung") /// - [JsonProperty("device_brand")] public string DeviceBrand { get; set; } /// /// Device type (e.g., "tablet", "mobile", "desktop") /// - [JsonProperty("device_type")] public string DeviceType { get; set; } } } \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Requests/PaymentSource.cs b/src/CheckoutSdk/Agentic/Requests/PaymentSource.cs index 2fac3bba..0141541b 100644 --- a/src/CheckoutSdk/Agentic/Requests/PaymentSource.cs +++ b/src/CheckoutSdk/Agentic/Requests/PaymentSource.cs @@ -11,31 +11,26 @@ public class PaymentSource /// /// Card number /// - [JsonProperty("number")] public string Number { get; set; } /// /// Card expiry month (1-12) /// - [JsonProperty("expiry_month")] - public int? ExpiryMonth { get; set; } + public int ExpiryMonth { get; set; } /// /// Card expiry year (e.g., 2025) /// - [JsonProperty("expiry_year")] - public int? ExpiryYear { get; set; } + public int ExpiryYear { get; set; } /// /// Card verification value (CVV) /// - [JsonProperty("cvv")] public string Cvv { get; set; } /// /// Payment source type /// - [JsonProperty("type")] public PaymentSourceType? Type { get; set; } } } \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs b/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs index 8ea1317c..a21f968e 100644 --- a/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs +++ b/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs @@ -11,19 +11,16 @@ public class AgenticEnrollResponse : HttpMetadata /// /// The unique token identifier for the enrolled agentic service /// - [JsonProperty("token_id")] public string TokenId { get; set; } /// /// Current status of the enrollment (e.g., "enrolled") /// - [JsonProperty("status")] public string Status { get; set; } /// /// The timestamp when the enrollment was created /// - [JsonProperty("created_at")] - public DateTime CreatedAt { get; set; } + public DateTime? CreatedAt { get; set; } } } \ No newline at end of file diff --git a/src/CheckoutSdk/OAuthScope.cs b/src/CheckoutSdk/OAuthScope.cs index 1ae96aa1..f2ecc1a7 100644 --- a/src/CheckoutSdk/OAuthScope.cs +++ b/src/CheckoutSdk/OAuthScope.cs @@ -53,5 +53,6 @@ public enum OAuthScope [OAuthScope("Payment Context")] PaymentContext, [OAuthScope("forward")] Forward, [OAuthScope("vault:network-tokens")] VaultNetworkTokens, + [OAuthScope("agentic:enroll")] AgenticEnroll, } } diff --git a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs index ea23cd57..fce04734 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs +++ b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs @@ -10,11 +10,11 @@ namespace Checkout.Agentic { public class AgenticIntegrationTest : SandboxTestFixture { - public AgenticIntegrationTest() : base(PlatformType.Default) + public AgenticIntegrationTest() : base(PlatformType.DefaultOAuth) { } - [Fact] + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] private async Task ShouldEnrollInAgenticServices() { var agenticEnrollRequest = new AgenticEnrollRequest @@ -56,7 +56,7 @@ private async Task ShouldEnrollInAgenticServices() response.Status.ShouldBe("enrolled"); } - [Fact] + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] private async Task ShouldEnrollWithMinimalData() { var agenticEnrollRequest = new AgenticEnrollRequest @@ -88,7 +88,7 @@ private async Task ShouldEnrollWithMinimalData() response.Status.ShouldBe("enrolled"); } - [Fact] + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] private async Task ShouldHandleDifferentCardTypes() { // Test with Visa card @@ -147,7 +147,7 @@ private async Task ShouldHandleDifferentCardTypes() mastercardResponse.Status.ShouldBe("enrolled"); } - [Fact] + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] private async Task ShouldHandleInternationalCustomers() { var internationalRequest = new AgenticEnrollRequest diff --git a/test/CheckoutSdkTest/SandboxTestFixture.cs b/test/CheckoutSdkTest/SandboxTestFixture.cs index fcecaf6a..02bed1e3 100644 --- a/test/CheckoutSdkTest/SandboxTestFixture.cs +++ b/test/CheckoutSdkTest/SandboxTestFixture.cs @@ -60,7 +60,7 @@ protected SandboxTestFixture(PlatformType platformType) OAuthScope.Accounts, OAuthScope.SessionsApp, OAuthScope.SessionsBrowser, OAuthScope.Vault, OAuthScope.PayoutsBankDetails, OAuthScope.TransfersCreate, OAuthScope.TransfersView, OAuthScope.BalancesView, OAuthScope.VaultCardMetadata, - OAuthScope.FinancialActions, OAuthScope.Forward) + OAuthScope.FinancialActions, OAuthScope.Forward, OAuthScope.AgenticEnroll) .Environment(Environment.Sandbox) //.HttpClientFactory(new CustomClientFactory("3.0")) //.EnvironmentSubdomain(System.Environment.GetEnvironmentVariable("CHECKOUT_MERCHANT_SUBDOMAIN")) From cb99f05ecd800e769dfae8c47398d47f78497417 Mon Sep 17 00:00:00 2001 From: david ruiz Date: Tue, 18 Nov 2025 12:23:45 +0100 Subject: [PATCH 05/18] new agentic createPurchaseIntent endpoint --- src/CheckoutSdk/Agentic/AgenticClient.cs | 19 ++ src/CheckoutSdk/Agentic/IAgenticClient.cs | 8 + .../AgenticCreatePurchaseIntentRequest.cs | 28 +++ .../Agentic/Requests/AgenticEnrollRequest.cs | 2 - .../{ => Entities}/AgenticCustomer.cs | 0 .../Requests/{ => Entities}/DeviceInfo.cs | 0 .../Agentic/Requests/Entities/Mandate.cs | 30 +++ .../Requests/{ => Entities}/PaymentSource.cs | 0 .../Requests/Entities/PurchaseThreshold.cs | 18 ++ .../AgenticCreatePurchaseIntentResponse.cs | 52 +++++ .../Responses/AgenticEnrollResponse.cs | 1 - .../Agentic/Responses/AgenticErrorResponse.cs | 23 ++ .../Responses/Entities/AgenticLinks.cs | 28 +++ .../Agentic/AgenticClientTest.cs | 167 ++++++++++++++- .../Agentic/AgenticIntegrationTest.cs | 196 +++++++++++++++++- 15 files changed, 560 insertions(+), 12 deletions(-) create mode 100644 src/CheckoutSdk/Agentic/Requests/AgenticCreatePurchaseIntentRequest.cs rename src/CheckoutSdk/Agentic/Requests/{ => Entities}/AgenticCustomer.cs (100%) rename src/CheckoutSdk/Agentic/Requests/{ => Entities}/DeviceInfo.cs (100%) create mode 100644 src/CheckoutSdk/Agentic/Requests/Entities/Mandate.cs rename src/CheckoutSdk/Agentic/Requests/{ => Entities}/PaymentSource.cs (100%) create mode 100644 src/CheckoutSdk/Agentic/Requests/Entities/PurchaseThreshold.cs create mode 100644 src/CheckoutSdk/Agentic/Responses/AgenticCreatePurchaseIntentResponse.cs create mode 100644 src/CheckoutSdk/Agentic/Responses/AgenticErrorResponse.cs create mode 100644 src/CheckoutSdk/Agentic/Responses/Entities/AgenticLinks.cs diff --git a/src/CheckoutSdk/Agentic/AgenticClient.cs b/src/CheckoutSdk/Agentic/AgenticClient.cs index 9d698ec7..99e90628 100644 --- a/src/CheckoutSdk/Agentic/AgenticClient.cs +++ b/src/CheckoutSdk/Agentic/AgenticClient.cs @@ -12,6 +12,8 @@ public class AgenticClient : AbstractClient, IAgenticClient { private const string AgenticPath = "agentic"; private const string EnrollPath = "enroll"; + private const string PurchaseIntentPath = "purchase-intent"; + private const string CredentialsPath = "credentials"; public AgenticClient(IApiClient apiClient, CheckoutConfiguration configuration) : base(apiClient, configuration, SdkAuthorizationType.OAuth) @@ -34,5 +36,22 @@ public Task Enroll( cancellationToken ); } + + /// + /// Create a purchase intent + /// Creates a new purchase intent for agentic commerce + /// + public Task CreatePurchaseIntent( + AgenticCreatePurchaseIntentRequest agenticCreatePurchaseIntentRequest, + CancellationToken cancellationToken = default) + { + CheckoutUtils.ValidateParams("agenticCreatePurchaseIntentRequest", agenticCreatePurchaseIntentRequest); + return ApiClient.Post( + BuildPath(AgenticPath, PurchaseIntentPath), + SdkAuthorization(), + agenticCreatePurchaseIntentRequest, + cancellationToken + ); + } } } \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/IAgenticClient.cs b/src/CheckoutSdk/Agentic/IAgenticClient.cs index 091b6a2f..ef857f46 100644 --- a/src/CheckoutSdk/Agentic/IAgenticClient.cs +++ b/src/CheckoutSdk/Agentic/IAgenticClient.cs @@ -17,5 +17,13 @@ public interface IAgenticClient Task Enroll( AgenticEnrollRequest agenticEnrollRequest, CancellationToken cancellationToken = default); + + /// + /// Create a purchase intent + /// Creates a new purchase intent for agentic commerce + /// + Task CreatePurchaseIntent( + AgenticCreatePurchaseIntentRequest agenticCreatePurchaseIntentRequest, + CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticCreatePurchaseIntentRequest.cs b/src/CheckoutSdk/Agentic/Requests/AgenticCreatePurchaseIntentRequest.cs new file mode 100644 index 00000000..352bdb15 --- /dev/null +++ b/src/CheckoutSdk/Agentic/Requests/AgenticCreatePurchaseIntentRequest.cs @@ -0,0 +1,28 @@ +namespace Checkout.Agentic.Requests +{ + /// + /// Request to create a purchase intent for agentic commerce + /// + public class AgenticCreatePurchaseIntentRequest + { + /// + /// The network token ID + /// + public string NetworkTokenId { get; set; } + + /// + /// The device information + /// + public DeviceInfo Device { get; set; } + + /// + /// Customer prompt describing the purchase intent + /// + public string CustomerPrompt { get; set; } + + /// + /// List of mandates for the purchase intent + /// + public Mandate[] Mandates { get; set; } + } +} diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs b/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs index a6eb089f..f21869b0 100644 --- a/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs +++ b/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs @@ -1,5 +1,3 @@ -using Newtonsoft.Json; - namespace Checkout.Agentic.Requests { /// diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticCustomer.cs b/src/CheckoutSdk/Agentic/Requests/Entities/AgenticCustomer.cs similarity index 100% rename from src/CheckoutSdk/Agentic/Requests/AgenticCustomer.cs rename to src/CheckoutSdk/Agentic/Requests/Entities/AgenticCustomer.cs diff --git a/src/CheckoutSdk/Agentic/Requests/DeviceInfo.cs b/src/CheckoutSdk/Agentic/Requests/Entities/DeviceInfo.cs similarity index 100% rename from src/CheckoutSdk/Agentic/Requests/DeviceInfo.cs rename to src/CheckoutSdk/Agentic/Requests/Entities/DeviceInfo.cs diff --git a/src/CheckoutSdk/Agentic/Requests/Entities/Mandate.cs b/src/CheckoutSdk/Agentic/Requests/Entities/Mandate.cs new file mode 100644 index 00000000..40428e0c --- /dev/null +++ b/src/CheckoutSdk/Agentic/Requests/Entities/Mandate.cs @@ -0,0 +1,30 @@ +using System; + +namespace Checkout.Agentic.Requests +{ + /// + /// Mandate configuration for purchase intents + /// + public class Mandate + { + /// + /// The mandate ID + /// + public string Id { get; set; } + + /// + /// Purchase threshold configuration + /// + public PurchaseThreshold PurchaseThreshold { get; set; } + + /// + /// Description of the mandate + /// + public string Description { get; set; } + + /// + /// Expiration date of the mandate in ISO 8601 format + /// + public DateTime? ExpirationDate { get; set; } + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Requests/PaymentSource.cs b/src/CheckoutSdk/Agentic/Requests/Entities/PaymentSource.cs similarity index 100% rename from src/CheckoutSdk/Agentic/Requests/PaymentSource.cs rename to src/CheckoutSdk/Agentic/Requests/Entities/PaymentSource.cs diff --git a/src/CheckoutSdk/Agentic/Requests/Entities/PurchaseThreshold.cs b/src/CheckoutSdk/Agentic/Requests/Entities/PurchaseThreshold.cs new file mode 100644 index 00000000..2499ae3b --- /dev/null +++ b/src/CheckoutSdk/Agentic/Requests/Entities/PurchaseThreshold.cs @@ -0,0 +1,18 @@ +namespace Checkout.Agentic.Requests +{ + /// + /// Purchase threshold configuration + /// + public class PurchaseThreshold + { + /// + /// The threshold amount + /// + public decimal Amount { get; set; } + + /// + /// The currency code (e.g., USD, EUR) + /// + public string CurrencyCode { get; set; } + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Responses/AgenticCreatePurchaseIntentResponse.cs b/src/CheckoutSdk/Agentic/Responses/AgenticCreatePurchaseIntentResponse.cs new file mode 100644 index 00000000..547ada79 --- /dev/null +++ b/src/CheckoutSdk/Agentic/Responses/AgenticCreatePurchaseIntentResponse.cs @@ -0,0 +1,52 @@ +using Checkout.Agentic.Requests; +using Newtonsoft.Json; + +namespace Checkout.Agentic.Responses +{ + /// + /// Response from purchase intent creation + /// + public class AgenticCreatePurchaseIntentResponse : HttpMetadata + { + /// + /// The purchase intent ID + /// + public string Id { get; set; } + + /// + /// The scheme of the purchase intent + /// + public string Scheme { get; set; } + + /// + /// The status of the purchase intent + /// + public string Status { get; set; } + + /// + /// The tokenid of the purchase intent + /// + public string TokenId { get; set; } + + /// + /// The device information of the purchase intent + /// + public DeviceInfo DeviceData { get; set; } + + /// + /// The customer prompt of the purchase intent + /// + public string CustomerPrompt { get; set; } + + /// + /// List of mandates for the purchase intent + /// + public Mandate[] Mandates { get; set; } + + /// + /// Links for related operations + /// + [JsonProperty("_links")] + public AgenticLinks Links { get; set; } + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs b/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs index a21f968e..b7471309 100644 --- a/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs +++ b/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs @@ -1,5 +1,4 @@ using System; -using Newtonsoft.Json; namespace Checkout.Agentic.Responses { diff --git a/src/CheckoutSdk/Agentic/Responses/AgenticErrorResponse.cs b/src/CheckoutSdk/Agentic/Responses/AgenticErrorResponse.cs new file mode 100644 index 00000000..9a5ac5a0 --- /dev/null +++ b/src/CheckoutSdk/Agentic/Responses/AgenticErrorResponse.cs @@ -0,0 +1,23 @@ +namespace Checkout.Agentic.Responses +{ + /// + /// Agentic Error Response launched when something went wrong + /// + public class AgenticErrorResponse : HttpMetadata + { + /// + /// The request ID + /// + public string RequestId { get; set; } + + /// + /// The error type if any + /// + public string ErrorType { get; set; } + + /// + /// List of error codes if any + /// + public string[] ErrorCodes { get; set; } + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Responses/Entities/AgenticLinks.cs b/src/CheckoutSdk/Agentic/Responses/Entities/AgenticLinks.cs new file mode 100644 index 00000000..fc5686b2 --- /dev/null +++ b/src/CheckoutSdk/Agentic/Responses/Entities/AgenticLinks.cs @@ -0,0 +1,28 @@ +namespace Checkout.Agentic.Responses +{ + /// + /// Links for agentic purchase intent operations + /// + public class AgenticLinks + { + /// + /// Link to self + /// + public string Self { get; set; } + + /// + /// Link to create credentials + /// + public string CreateCredentials { get; set; } + + /// + /// Link to update + /// + public string Update { get; set; } + + /// + /// Link to cancel + /// + public string Cancel { get; set; } + } +} \ No newline at end of file diff --git a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs index f7d01a56..0f40c62b 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs +++ b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs @@ -19,7 +19,7 @@ public class AgenticClientTest : UnitTestFixture public AgenticClientTest() { - _sdkCredentials.Setup(credentials => credentials.GetSdkAuthorization(SdkAuthorizationType.SecretKeyOrOAuth)) + _sdkCredentials.Setup(credentials => credentials.GetSdkAuthorization(SdkAuthorizationType.OAuth)) .Returns(_authorization); _configuration = new Mock(_sdkCredentials.Object, @@ -27,7 +27,7 @@ public AgenticClientTest() } [Fact] - private async Task ShouldEnrollInAgenticServices() + private async Task EnrollShouldEnroll() { var agenticEnrollRequest = new AgenticEnrollRequest { @@ -75,7 +75,7 @@ private async Task ShouldEnrollInAgenticServices() } [Fact] - private async Task ShouldThrowExceptionWhenRequestIsNull() + private async Task EnrollShouldThrowExceptionWhenEnrollRequestIsNull() { IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); @@ -86,7 +86,7 @@ private async Task ShouldThrowExceptionWhenRequestIsNull() } [Fact] - private async Task ShouldCallCorrectEndpoint() + private async Task EnrollShouldCallCorrectEnrollEndpoint() { var agenticEnrollRequest = new AgenticEnrollRequest { @@ -126,7 +126,7 @@ private async Task ShouldCallCorrectEndpoint() } [Fact] - private async Task ShouldUseCorrectAuthorization() + private async Task EnrollShouldUseCorrectAuthorizationForEnroll() { var agenticEnrollRequest = new AgenticEnrollRequest { @@ -148,5 +148,162 @@ private async Task ShouldUseCorrectAuthorization() apiClient.Post(It.IsAny(), _authorization, It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } + + [Fact] + private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() + { + var createPurchaseIntentRequest = new AgenticCreatePurchaseIntentRequest + { + NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + Device = new DeviceInfo + { + IpAddress = "192.168.1.100", + UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", + DeviceBrand = "apple", + DeviceType = "tablet" + }, + CustomerPrompt = "I'm looking for running shoes in a size 10, for under $150.", + Mandates = new[] + { + new Mandate + { + Id = "mandate_123", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 100, + CurrencyCode = "USD" + }, + Description = "Purchase running shoes in size 10.", + ExpirationDate = System.DateTime.Parse("2026-08-31T23:59:59.000Z") + } + } + }; + + var expectedResponse = new AgenticCreatePurchaseIntentResponse + { + Id = "pi_f3egwppx6rde3hg6itlqzp3h7e", + Scheme = "visa", + Status = "active", + TokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + DeviceData = new DeviceInfo + { + IpAddress = "192.168.1.100", + UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", + DeviceBrand = "apple", + DeviceType = "tablet" + }, + CustomerPrompt = "Hey AI, I need Nike running shoes in size 10 under $130.00", + Mandates = new[] + { + new Mandate + { + Id = "mandate_123", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 100, + CurrencyCode = "USD" + }, + Description = "Purchase Nike Air Max 270 running shoes in size 10", + ExpirationDate = System.DateTime.Parse("2026-08-31T23:59:59.000Z") + } + }, + Links = new AgenticLinks + { + Self = "https://api.example.com/agentic/purchase-intents/intent_123", + CreateCredentials = "https://api.example.com/agentic/purchase-intents/intent_123/credentials", + Update = "https://api.example.com/agentic/purchase-intents/intent_123", + Cancel = "https://api.example.com/agentic/purchase-intents/intent_123/cancel" + } + }; + + _apiClient.Setup(apiClient => + apiClient.Post("agentic/purchase-intent", _authorization, createPurchaseIntentRequest, + CancellationToken.None, null)) + .ReturnsAsync(() => expectedResponse); + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + var response = await client.CreatePurchaseIntent(createPurchaseIntentRequest, CancellationToken.None); + + response.ShouldNotBeNull(); + response.Id.ShouldBe(expectedResponse.Id); + response.Scheme.ShouldBe(expectedResponse.Scheme); + response.Status.ShouldBe(expectedResponse.Status); + response.TokenId.ShouldBe(expectedResponse.TokenId); + response.CustomerPrompt.ShouldBe(expectedResponse.CustomerPrompt); + response.Links.ShouldNotBeNull(); + response.Links.Self.ShouldBe(expectedResponse.Links.Self); + } + + [Fact] + private async Task CreatePurchaseIntentShouldThrowExceptionWhenCreatePurchaseIntentRequestIsNull() + { + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + var exception = await Should.ThrowAsync( + async () => await client.CreatePurchaseIntent(null, CancellationToken.None)); + + exception.Message.ShouldContain("agenticCreatePurchaseIntentRequest"); + } + + [Fact] + private async Task CreatePurchaseIntentShouldCallCorrectCreatePurchaseIntentEndpoint() + { + var createPurchaseIntentRequest = new AgenticCreatePurchaseIntentRequest + { + NetworkTokenId = "nt_test_123", + Device = new DeviceInfo + { + IpAddress = "192.168.1.100", + UserAgent = "Mozilla/5.0 Test" + }, + CustomerPrompt = "Test prompt", + Mandates = new[] + { + new Mandate + { + Id = "mandate_test", + Description = "Test mandate" + } + } + }; + + _apiClient.Setup(apiClient => + apiClient.Post("agentic/purchase-intent", _authorization, createPurchaseIntentRequest, + CancellationToken.None, null)) + .ReturnsAsync(() => new AgenticCreatePurchaseIntentResponse()); + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + await client.CreatePurchaseIntent(createPurchaseIntentRequest, CancellationToken.None); + + _apiClient.Verify(apiClient => + apiClient.Post("agentic/purchase-intent", _authorization, createPurchaseIntentRequest, + CancellationToken.None, null), Times.Once); + } + + [Fact] + private async Task CreatePurchaseIntentShouldUseCorrectAuthorizationForCreatePurchaseIntent() + { + var createPurchaseIntentRequest = new AgenticCreatePurchaseIntentRequest + { + NetworkTokenId = "nt_test_123", + Device = new DeviceInfo(), + CustomerPrompt = "Test prompt" + }; + + _apiClient.Setup(apiClient => + apiClient.Post(It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(() => new AgenticCreatePurchaseIntentResponse()); + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + await client.CreatePurchaseIntent(createPurchaseIntentRequest, CancellationToken.None); + + _apiClient.Verify(apiClient => + apiClient.Post(It.IsAny(), _authorization, + It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + } } } \ No newline at end of file diff --git a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs index fce04734..7e245e32 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs +++ b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs @@ -15,7 +15,7 @@ public AgenticIntegrationTest() : base(PlatformType.DefaultOAuth) } [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] - private async Task ShouldEnrollInAgenticServices() + private async Task EnrollShouldEnroll() { var agenticEnrollRequest = new AgenticEnrollRequest { @@ -57,7 +57,7 @@ private async Task ShouldEnrollInAgenticServices() } [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] - private async Task ShouldEnrollWithMinimalData() + private async Task EnrollShouldEnrollWithMinimalData() { var agenticEnrollRequest = new AgenticEnrollRequest { @@ -89,7 +89,7 @@ private async Task ShouldEnrollWithMinimalData() } [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] - private async Task ShouldHandleDifferentCardTypes() + private async Task EnrollShouldHandleDifferentCardTypes() { // Test with Visa card var visaRequest = new AgenticEnrollRequest @@ -148,7 +148,7 @@ private async Task ShouldHandleDifferentCardTypes() } [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] - private async Task ShouldHandleInternationalCustomers() + private async Task EnrollShouldHandleInternationalCustomers() { var internationalRequest = new AgenticEnrollRequest { @@ -181,5 +181,193 @@ private async Task ShouldHandleInternationalCustomers() response.Status.ShouldBe("enrolled"); response.TokenId.ShouldStartWith("nt_"); } + + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() + { + var createPurchaseIntentRequest = new AgenticCreatePurchaseIntentRequest + { + NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + Device = new DeviceInfo + { + IpAddress = "192.168.1.100", + UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", + DeviceBrand = "apple", + DeviceType = "tablet" + }, + CustomerPrompt = "I'm looking for running shoes in a size 10, for under $150.", + Mandates = new[] + { + new Mandate + { + Id = "mandate_123", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 100, + CurrencyCode = "USD" + }, + Description = "Purchase running shoes in size 10.", + ExpirationDate = DateTime.Parse("2026-08-31T23:59:59.000Z") + } + } + }; + + var response = await DefaultApi.AgenticClient().CreatePurchaseIntent(createPurchaseIntentRequest); + + response.ShouldNotBeNull(); + response.Id.ShouldNotBeNullOrEmpty(); + response.Status.ShouldNotBeNullOrEmpty(); + response.TokenId.ShouldNotBeNullOrEmpty(); + response.CustomerPrompt.ShouldNotBeNullOrEmpty(); + + // Validate that purchase intent ID follows expected pattern + response.Id.ShouldStartWith("pi_"); + + // Validate status is one of expected values + response.Status.ShouldBe("active"); + + // Validate links are present + response.Links.ShouldNotBeNull(); + response.Links.Self.ShouldNotBeNullOrEmpty(); + response.Links.CreateCredentials.ShouldNotBeNullOrEmpty(); + } + + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + private async Task CreatePurchaseIntentShouldCreatePurchaseIntentWithMinimalData() + { + var createPurchaseIntentRequest = new AgenticCreatePurchaseIntentRequest + { + NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + Device = new DeviceInfo + { + IpAddress = "10.0.0.1" + }, + CustomerPrompt = "Basic purchase request" + }; + + var response = await DefaultApi.AgenticClient().CreatePurchaseIntent(createPurchaseIntentRequest); + + response.ShouldNotBeNull(); + response.Id.ShouldNotBeNullOrEmpty(); + response.Status.ShouldBe("active"); + response.TokenId.ShouldNotBeNullOrEmpty(); + } + + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + private async Task CreatePurchaseIntentShouldHandleDifferentMandateTypes() + { + // Test with single mandate + var singleMandateRequest = new AgenticCreatePurchaseIntentRequest + { + NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + Device = new DeviceInfo + { + IpAddress = "203.0.113.195", + UserAgent = "Test Agent", + DeviceBrand = "chrome", + DeviceType = "desktop" + }, + CustomerPrompt = "Looking for electronics under $500", + Mandates = new[] + { + new Mandate + { + Id = "mandate_single", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 500, + CurrencyCode = "USD" + }, + Description = "Electronics purchase", + ExpirationDate = DateTime.Parse("2026-12-31T23:59:59.000Z") + } + } + }; + + var singleResponse = await DefaultApi.AgenticClient().CreatePurchaseIntent(singleMandateRequest); + singleResponse.ShouldNotBeNull(); + singleResponse.Status.ShouldBe("active"); + singleResponse.Mandates.Length.ShouldBe(1); + + // Test with multiple mandates + var multipleMandateRequest = new AgenticCreatePurchaseIntentRequest + { + NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + Device = new DeviceInfo + { + IpAddress = "198.51.100.42" + }, + CustomerPrompt = "Shopping for clothing and accessories", + Mandates = new[] + { + new Mandate + { + Id = "mandate_clothing", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 200, + CurrencyCode = "USD" + }, + Description = "Clothing purchase", + ExpirationDate = DateTime.Parse("2026-06-30T23:59:59.000Z") + }, + new Mandate + { + Id = "mandate_accessories", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 100, + CurrencyCode = "USD" + }, + Description = "Accessories purchase", + ExpirationDate = DateTime.Parse("2026-09-30T23:59:59.000Z") + } + } + }; + + var multipleResponse = await DefaultApi.AgenticClient().CreatePurchaseIntent(multipleMandateRequest); + multipleResponse.ShouldNotBeNull(); + multipleResponse.Status.ShouldBe("active"); + multipleResponse.Mandates.Length.ShouldBe(2); + } + + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + private async Task CreatePurchaseIntentShouldHandleInternationalCurrencies() + { + var internationalRequest = new AgenticCreatePurchaseIntentRequest + { + NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + Device = new DeviceInfo + { + IpAddress = "80.112.12.34", // European IP + UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36", + DeviceBrand = "safari", + DeviceType = "mobile" + }, + CustomerPrompt = "Buscando zapatos deportivos en talla 42", + Mandates = new[] + { + new Mandate + { + Id = "mandate_eur", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 150, + CurrencyCode = "EUR" + }, + Description = "Compra de zapatos deportivos", + ExpirationDate = DateTime.Parse("2026-08-31T23:59:59.000Z") + } + } + }; + + var response = await DefaultApi.AgenticClient().CreatePurchaseIntent(internationalRequest); + + response.ShouldNotBeNull(); + response.Status.ShouldBe("active"); + response.Id.ShouldStartWith("pi_"); + response.Mandates.ShouldNotBeNull(); + response.Mandates[0].PurchaseThreshold.CurrencyCode.ShouldBe("EUR"); + } } } \ No newline at end of file From 0e7cf6ca43deb8b7ce876c71d2d2e20345e29cf0 Mon Sep 17 00:00:00 2001 From: david ruiz Date: Tue, 18 Nov 2025 13:08:21 +0100 Subject: [PATCH 06/18] new naming for intent endpoints --- src/CheckoutSdk/Agentic/AgenticClient.cs | 27 ++++++++++++++---- src/CheckoutSdk/Agentic/IAgenticClient.cs | 12 ++++++-- ... => AgenticPurchaseIntentCreateRequest.cs} | 12 +------- .../AgenticPurchaseIntentUpdateRequest.cs | 18 ++++++++++++ ...se.cs => AgenticPurchaseIntentResponse.cs} | 2 +- .../Agentic/AgenticClientTest.cs | 28 +++++++++---------- .../Agentic/AgenticIntegrationTest.cs | 10 +++---- 7 files changed, 71 insertions(+), 38 deletions(-) rename src/CheckoutSdk/Agentic/Requests/{AgenticCreatePurchaseIntentRequest.cs => AgenticPurchaseIntentCreateRequest.cs} (53%) create mode 100644 src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentUpdateRequest.cs rename src/CheckoutSdk/Agentic/Responses/{AgenticCreatePurchaseIntentResponse.cs => AgenticPurchaseIntentResponse.cs} (95%) diff --git a/src/CheckoutSdk/Agentic/AgenticClient.cs b/src/CheckoutSdk/Agentic/AgenticClient.cs index 99e90628..ad616b28 100644 --- a/src/CheckoutSdk/Agentic/AgenticClient.cs +++ b/src/CheckoutSdk/Agentic/AgenticClient.cs @@ -41,15 +41,32 @@ public Task Enroll( /// Create a purchase intent /// Creates a new purchase intent for agentic commerce /// - public Task CreatePurchaseIntent( - AgenticCreatePurchaseIntentRequest agenticCreatePurchaseIntentRequest, + public Task CreatePurchaseIntent( + AgenticPurchaseIntentCreateRequest agenticPurchaseIntentCreateRequest, CancellationToken cancellationToken = default) { - CheckoutUtils.ValidateParams("agenticCreatePurchaseIntentRequest", agenticCreatePurchaseIntentRequest); - return ApiClient.Post( + CheckoutUtils.ValidateParams("agenticPurchaseIntentCreateRequest", agenticPurchaseIntentCreateRequest); + return ApiClient.Post( BuildPath(AgenticPath, PurchaseIntentPath), SdkAuthorization(), - agenticCreatePurchaseIntentRequest, + agenticPurchaseIntentCreateRequest, + cancellationToken + ); + } + + /// + /// Update a purchase intent + /// Updates a new purchase intent for agentic commerce + /// + public Task UpdatePurchaseIntent( + AgenticPurchaseIntentUpdateRequest agenticPurchaseIntentUpdateRequest, + CancellationToken cancellationToken = default) + { + CheckoutUtils.ValidateParams("agenticPurchaseIntentUpdateRequest", agenticPurchaseIntentUpdateRequest); + return ApiClient.Put( + BuildPath(AgenticPath, PurchaseIntentPath), + SdkAuthorization(), + agenticPurchaseIntentUpdateRequest, cancellationToken ); } diff --git a/src/CheckoutSdk/Agentic/IAgenticClient.cs b/src/CheckoutSdk/Agentic/IAgenticClient.cs index ef857f46..ae2129fe 100644 --- a/src/CheckoutSdk/Agentic/IAgenticClient.cs +++ b/src/CheckoutSdk/Agentic/IAgenticClient.cs @@ -22,8 +22,16 @@ Task Enroll( /// Create a purchase intent /// Creates a new purchase intent for agentic commerce /// - Task CreatePurchaseIntent( - AgenticCreatePurchaseIntentRequest agenticCreatePurchaseIntentRequest, + Task CreatePurchaseIntent( + AgenticPurchaseIntentCreateRequest agenticPurchaseIntentCreateRequest, + CancellationToken cancellationToken = default); + + /// + /// Update a purchase intent + /// Updates a purchase intent for agentic commerce + /// + Task UpdatePurchaseIntent( + AgenticPurchaseIntentUpdateRequest agenticPurchaseIntentUpdateRequest, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticCreatePurchaseIntentRequest.cs b/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCreateRequest.cs similarity index 53% rename from src/CheckoutSdk/Agentic/Requests/AgenticCreatePurchaseIntentRequest.cs rename to src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCreateRequest.cs index 352bdb15..d1462e8d 100644 --- a/src/CheckoutSdk/Agentic/Requests/AgenticCreatePurchaseIntentRequest.cs +++ b/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCreateRequest.cs @@ -3,7 +3,7 @@ namespace Checkout.Agentic.Requests /// /// Request to create a purchase intent for agentic commerce /// - public class AgenticCreatePurchaseIntentRequest + public class AgenticPurchaseIntentCreateRequest : AgenticPurchaseIntentUpdateRequest { /// /// The network token ID @@ -14,15 +14,5 @@ public class AgenticCreatePurchaseIntentRequest /// The device information /// public DeviceInfo Device { get; set; } - - /// - /// Customer prompt describing the purchase intent - /// - public string CustomerPrompt { get; set; } - - /// - /// List of mandates for the purchase intent - /// - public Mandate[] Mandates { get; set; } } } diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentUpdateRequest.cs b/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentUpdateRequest.cs new file mode 100644 index 00000000..3b7cf3f3 --- /dev/null +++ b/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentUpdateRequest.cs @@ -0,0 +1,18 @@ +namespace Checkout.Agentic.Requests +{ + /// + /// Request to update a purchase intent for agentic commerce + /// + public class AgenticPurchaseIntentUpdateRequest + { + /// + /// Customer prompt describing the purchase intent + /// + public string CustomerPrompt { get; set; } + + /// + /// List of mandates for the purchase intent + /// + public Mandate[] Mandates { get; set; } + } +} diff --git a/src/CheckoutSdk/Agentic/Responses/AgenticCreatePurchaseIntentResponse.cs b/src/CheckoutSdk/Agentic/Responses/AgenticPurchaseIntentResponse.cs similarity index 95% rename from src/CheckoutSdk/Agentic/Responses/AgenticCreatePurchaseIntentResponse.cs rename to src/CheckoutSdk/Agentic/Responses/AgenticPurchaseIntentResponse.cs index 547ada79..b9142df5 100644 --- a/src/CheckoutSdk/Agentic/Responses/AgenticCreatePurchaseIntentResponse.cs +++ b/src/CheckoutSdk/Agentic/Responses/AgenticPurchaseIntentResponse.cs @@ -6,7 +6,7 @@ namespace Checkout.Agentic.Responses /// /// Response from purchase intent creation /// - public class AgenticCreatePurchaseIntentResponse : HttpMetadata + public class AgenticPurchaseIntentResponse : HttpMetadata { /// /// The purchase intent ID diff --git a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs index 0f40c62b..05f1ae83 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs +++ b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs @@ -152,7 +152,7 @@ private async Task EnrollShouldUseCorrectAuthorizationForEnroll() [Fact] private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() { - var createPurchaseIntentRequest = new AgenticCreatePurchaseIntentRequest + var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", Device = new DeviceInfo @@ -179,7 +179,7 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() } }; - var expectedResponse = new AgenticCreatePurchaseIntentResponse + var expectedResponse = new AgenticPurchaseIntentResponse { Id = "pi_f3egwppx6rde3hg6itlqzp3h7e", Scheme = "visa", @@ -217,7 +217,7 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() }; _apiClient.Setup(apiClient => - apiClient.Post("agentic/purchase-intent", _authorization, createPurchaseIntentRequest, + apiClient.Post("agentic/purchase-intent", _authorization, createPurchaseIntentRequest, CancellationToken.None, null)) .ReturnsAsync(() => expectedResponse); @@ -243,13 +243,13 @@ private async Task CreatePurchaseIntentShouldThrowExceptionWhenCreatePurchaseInt var exception = await Should.ThrowAsync( async () => await client.CreatePurchaseIntent(null, CancellationToken.None)); - exception.Message.ShouldContain("agenticCreatePurchaseIntentRequest"); + exception.Message.ShouldContain("agenticPurchaseIntentCreateRequest"); } [Fact] private async Task CreatePurchaseIntentShouldCallCorrectCreatePurchaseIntentEndpoint() { - var createPurchaseIntentRequest = new AgenticCreatePurchaseIntentRequest + var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest { NetworkTokenId = "nt_test_123", Device = new DeviceInfo @@ -269,23 +269,23 @@ private async Task CreatePurchaseIntentShouldCallCorrectCreatePurchaseIntentEndp }; _apiClient.Setup(apiClient => - apiClient.Post("agentic/purchase-intent", _authorization, createPurchaseIntentRequest, + apiClient.Post("agentic/purchase-intent", _authorization, createPurchaseIntentRequest, CancellationToken.None, null)) - .ReturnsAsync(() => new AgenticCreatePurchaseIntentResponse()); + .ReturnsAsync(() => new AgenticPurchaseIntentResponse()); IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); await client.CreatePurchaseIntent(createPurchaseIntentRequest, CancellationToken.None); _apiClient.Verify(apiClient => - apiClient.Post("agentic/purchase-intent", _authorization, createPurchaseIntentRequest, + apiClient.Post("agentic/purchase-intent", _authorization, createPurchaseIntentRequest, CancellationToken.None, null), Times.Once); } [Fact] private async Task CreatePurchaseIntentShouldUseCorrectAuthorizationForCreatePurchaseIntent() { - var createPurchaseIntentRequest = new AgenticCreatePurchaseIntentRequest + var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest { NetworkTokenId = "nt_test_123", Device = new DeviceInfo(), @@ -293,17 +293,17 @@ private async Task CreatePurchaseIntentShouldUseCorrectAuthorizationForCreatePur }; _apiClient.Setup(apiClient => - apiClient.Post(It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(() => new AgenticCreatePurchaseIntentResponse()); + apiClient.Post(It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(() => new AgenticPurchaseIntentResponse()); IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); await client.CreatePurchaseIntent(createPurchaseIntentRequest, CancellationToken.None); _apiClient.Verify(apiClient => - apiClient.Post(It.IsAny(), _authorization, - It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + apiClient.Post(It.IsAny(), _authorization, + It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } } } \ No newline at end of file diff --git a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs index 7e245e32..e6b4e534 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs +++ b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs @@ -185,7 +185,7 @@ private async Task EnrollShouldHandleInternationalCustomers() [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() { - var createPurchaseIntentRequest = new AgenticCreatePurchaseIntentRequest + var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", Device = new DeviceInfo @@ -235,7 +235,7 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] private async Task CreatePurchaseIntentShouldCreatePurchaseIntentWithMinimalData() { - var createPurchaseIntentRequest = new AgenticCreatePurchaseIntentRequest + var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", Device = new DeviceInfo @@ -257,7 +257,7 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntentWithMinimalData private async Task CreatePurchaseIntentShouldHandleDifferentMandateTypes() { // Test with single mandate - var singleMandateRequest = new AgenticCreatePurchaseIntentRequest + var singleMandateRequest = new AgenticPurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", Device = new DeviceInfo @@ -290,7 +290,7 @@ private async Task CreatePurchaseIntentShouldHandleDifferentMandateTypes() singleResponse.Mandates.Length.ShouldBe(1); // Test with multiple mandates - var multipleMandateRequest = new AgenticCreatePurchaseIntentRequest + var multipleMandateRequest = new AgenticPurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", Device = new DeviceInfo @@ -334,7 +334,7 @@ private async Task CreatePurchaseIntentShouldHandleDifferentMandateTypes() [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] private async Task CreatePurchaseIntentShouldHandleInternationalCurrencies() { - var internationalRequest = new AgenticCreatePurchaseIntentRequest + var internationalRequest = new AgenticPurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", Device = new DeviceInfo From 93f9c44fe0ab995e37e3d292c9c6d5c673383bfe Mon Sep 17 00:00:00 2001 From: david ruiz Date: Tue, 18 Nov 2025 13:27:51 +0100 Subject: [PATCH 07/18] update and delete endpoints with id param --- src/CheckoutSdk/Agentic/AgenticClient.cs | 21 ++++++++++++++++++--- src/CheckoutSdk/Agentic/IAgenticClient.cs | 9 ++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/CheckoutSdk/Agentic/AgenticClient.cs b/src/CheckoutSdk/Agentic/AgenticClient.cs index ad616b28..4e7165be 100644 --- a/src/CheckoutSdk/Agentic/AgenticClient.cs +++ b/src/CheckoutSdk/Agentic/AgenticClient.cs @@ -58,17 +58,32 @@ public Task CreatePurchaseIntent( /// Update a purchase intent /// Updates a new purchase intent for agentic commerce /// - public Task UpdatePurchaseIntent( + public Task UpdatePurchaseIntent(string id, AgenticPurchaseIntentUpdateRequest agenticPurchaseIntentUpdateRequest, CancellationToken cancellationToken = default) { - CheckoutUtils.ValidateParams("agenticPurchaseIntentUpdateRequest", agenticPurchaseIntentUpdateRequest); + CheckoutUtils.ValidateParams("id", id, "agenticPurchaseIntentUpdateRequest", agenticPurchaseIntentUpdateRequest); return ApiClient.Put( - BuildPath(AgenticPath, PurchaseIntentPath), + BuildPath(AgenticPath, PurchaseIntentPath, id), SdkAuthorization(), agenticPurchaseIntentUpdateRequest, cancellationToken ); } + + /// + /// Delete a purchase intent + /// Deletes a purchase intent for agentic commerce + /// + public Task DeletePurchaseIntent(string id, + CancellationToken cancellationToken = default) + { + CheckoutUtils.ValidateParams("id", id); + return ApiClient.Delete( + BuildPath(AgenticPath, PurchaseIntentPath, id), + SdkAuthorization(), + cancellationToken + ); + } } } \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/IAgenticClient.cs b/src/CheckoutSdk/Agentic/IAgenticClient.cs index ae2129fe..6f0db301 100644 --- a/src/CheckoutSdk/Agentic/IAgenticClient.cs +++ b/src/CheckoutSdk/Agentic/IAgenticClient.cs @@ -30,8 +30,15 @@ Task CreatePurchaseIntent( /// Update a purchase intent /// Updates a purchase intent for agentic commerce /// - Task UpdatePurchaseIntent( + Task UpdatePurchaseIntent(string id, AgenticPurchaseIntentUpdateRequest agenticPurchaseIntentUpdateRequest, CancellationToken cancellationToken = default); + + /// + /// Delete a purchase intent + /// Deletes a purchase intent for agentic commerce + /// + Task DeletePurchaseIntent(string id, + CancellationToken cancellationToken = default); } } \ No newline at end of file From 6ec8580122b5d2444a82ed55ecb9fbcf07a7737b Mon Sep 17 00:00:00 2001 From: david ruiz Date: Tue, 18 Nov 2025 15:22:35 +0100 Subject: [PATCH 08/18] new CreatePurchaseIntentCredentials endpoint, refactor using common clases (CurrencyCode) --- src/CheckoutSdk/Agentic/AgenticClient.cs | 18 ++++++++++ src/CheckoutSdk/Agentic/IAgenticClient.cs | 8 +++++ ...cPurchaseIntentCredentialsCreateRequest.cs | 13 +++++++ .../Requests/Entities/PurchaseThreshold.cs | 8 +++-- .../Requests/Entities/TransactionAmount.cs | 20 +++++++++++ .../Requests/Entities/TransactionData.cs | 35 +++++++++++++++++++ .../Agentic/AgenticClientTest.cs | 4 +-- .../Agentic/AgenticIntegrationTest.cs | 12 +++---- 8 files changed, 107 insertions(+), 11 deletions(-) create mode 100644 src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCredentialsCreateRequest.cs create mode 100644 src/CheckoutSdk/Agentic/Requests/Entities/TransactionAmount.cs create mode 100644 src/CheckoutSdk/Agentic/Requests/Entities/TransactionData.cs diff --git a/src/CheckoutSdk/Agentic/AgenticClient.cs b/src/CheckoutSdk/Agentic/AgenticClient.cs index 4e7165be..1206bf36 100644 --- a/src/CheckoutSdk/Agentic/AgenticClient.cs +++ b/src/CheckoutSdk/Agentic/AgenticClient.cs @@ -54,6 +54,24 @@ public Task CreatePurchaseIntent( ); } + /// + /// Create a purchase intent + /// Creates a new purchase intent for agentic commerce + /// + public Task CreatePurchaseIntentCredentials(string id, + AgenticPurchaseIntentCredentialsCreateRequest agenticPurchaseIntentCredentialsCreateRequest, + CancellationToken cancellationToken = default) + { + CheckoutUtils.ValidateParams("id", id,"agenticPurchaseIntentCredentialsCreateRequest", + agenticPurchaseIntentCredentialsCreateRequest); + return ApiClient.Post( + BuildPath(AgenticPath, PurchaseIntentPath, id, CredentialsPath), + SdkAuthorization(), + agenticPurchaseIntentCredentialsCreateRequest, + cancellationToken + ); + } + /// /// Update a purchase intent /// Updates a new purchase intent for agentic commerce diff --git a/src/CheckoutSdk/Agentic/IAgenticClient.cs b/src/CheckoutSdk/Agentic/IAgenticClient.cs index 6f0db301..04978c34 100644 --- a/src/CheckoutSdk/Agentic/IAgenticClient.cs +++ b/src/CheckoutSdk/Agentic/IAgenticClient.cs @@ -26,6 +26,14 @@ Task CreatePurchaseIntent( AgenticPurchaseIntentCreateRequest agenticPurchaseIntentCreateRequest, CancellationToken cancellationToken = default); + /// + /// Create a purchase intent credentials + /// Create credentials for an agentic commerce purchase intent. + /// + Task CreatePurchaseIntentCredentials(string id, + AgenticPurchaseIntentCredentialsCreateRequest agenticPurchaseIntentCredentialsCreateRequest, + CancellationToken cancellationToken = default); + /// /// Update a purchase intent /// Updates a purchase intent for agentic commerce diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCredentialsCreateRequest.cs b/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCredentialsCreateRequest.cs new file mode 100644 index 00000000..14adb90f --- /dev/null +++ b/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCredentialsCreateRequest.cs @@ -0,0 +1,13 @@ +namespace Checkout.Agentic.Requests +{ + /// + /// Request to create a purchase intent credentials for agentic commerce + /// + public class AgenticPurchaseIntentCredentialsCreateRequest + { + /// + /// Array of transaction data for the purchase intent credentials + /// + public TransactionData[] TransactionData { get; set; } + } +} diff --git a/src/CheckoutSdk/Agentic/Requests/Entities/PurchaseThreshold.cs b/src/CheckoutSdk/Agentic/Requests/Entities/PurchaseThreshold.cs index 2499ae3b..6de8bd6e 100644 --- a/src/CheckoutSdk/Agentic/Requests/Entities/PurchaseThreshold.cs +++ b/src/CheckoutSdk/Agentic/Requests/Entities/PurchaseThreshold.cs @@ -1,3 +1,5 @@ +using Checkout.Common; + namespace Checkout.Agentic.Requests { /// @@ -8,11 +10,11 @@ public class PurchaseThreshold /// /// The threshold amount /// - public decimal Amount { get; set; } + public int Amount { get; set; } /// - /// The currency code (e.g., USD, EUR) + /// The currency for the threshold /// - public string CurrencyCode { get; set; } + public Currency? CurrencyCode { get; set; } } } \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Requests/Entities/TransactionAmount.cs b/src/CheckoutSdk/Agentic/Requests/Entities/TransactionAmount.cs new file mode 100644 index 00000000..b23f539a --- /dev/null +++ b/src/CheckoutSdk/Agentic/Requests/Entities/TransactionAmount.cs @@ -0,0 +1,20 @@ +using Checkout.Common; + +namespace Checkout.Agentic.Requests +{ + /// + /// Represents transaction amount information for agentic commerce + /// + public class TransactionAmount + { + /// + /// Transaction amount + /// + public int Amount { get; set; } + + /// + /// Currency for the transaction + /// + public Currency? CurrencyCode { get; set; } + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Requests/Entities/TransactionData.cs b/src/CheckoutSdk/Agentic/Requests/Entities/TransactionData.cs new file mode 100644 index 00000000..ae422e97 --- /dev/null +++ b/src/CheckoutSdk/Agentic/Requests/Entities/TransactionData.cs @@ -0,0 +1,35 @@ +using Checkout.Common; + +namespace Checkout.Agentic.Requests +{ + /// + /// Represents transaction data for agentic commerce + /// + public class TransactionData + { + /// + /// Merchant country code (e.g., US, GB) + /// + public CountryCode? MerchantCountryCode { get; set; } + + /// + /// Merchant name + /// + public string MerchantName { get; set; } + + /// + /// Merchant category code + /// + public string MerchantCategoryCode { get; set; } + + /// + /// Merchant website URL + /// + public string MerchantUrl { get; set; } + + /// + /// Transaction amount information + /// + public TransactionAmount TransactionAmount { get; set; } + } +} \ No newline at end of file diff --git a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs index 05f1ae83..3bb7bfd1 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs +++ b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs @@ -171,7 +171,7 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() PurchaseThreshold = new PurchaseThreshold { Amount = 100, - CurrencyCode = "USD" + CurrencyCode = Currency.USD }, Description = "Purchase running shoes in size 10.", ExpirationDate = System.DateTime.Parse("2026-08-31T23:59:59.000Z") @@ -201,7 +201,7 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() PurchaseThreshold = new PurchaseThreshold { Amount = 100, - CurrencyCode = "USD" + CurrencyCode = Currency.USD }, Description = "Purchase Nike Air Max 270 running shoes in size 10", ExpirationDate = System.DateTime.Parse("2026-08-31T23:59:59.000Z") diff --git a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs index e6b4e534..d83c8353 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs +++ b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs @@ -204,7 +204,7 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() PurchaseThreshold = new PurchaseThreshold { Amount = 100, - CurrencyCode = "USD" + CurrencyCode = Currency.USD }, Description = "Purchase running shoes in size 10.", ExpirationDate = DateTime.Parse("2026-08-31T23:59:59.000Z") @@ -276,7 +276,7 @@ private async Task CreatePurchaseIntentShouldHandleDifferentMandateTypes() PurchaseThreshold = new PurchaseThreshold { Amount = 500, - CurrencyCode = "USD" + CurrencyCode = Currency.USD }, Description = "Electronics purchase", ExpirationDate = DateTime.Parse("2026-12-31T23:59:59.000Z") @@ -306,7 +306,7 @@ private async Task CreatePurchaseIntentShouldHandleDifferentMandateTypes() PurchaseThreshold = new PurchaseThreshold { Amount = 200, - CurrencyCode = "USD" + CurrencyCode = Currency.USD }, Description = "Clothing purchase", ExpirationDate = DateTime.Parse("2026-06-30T23:59:59.000Z") @@ -317,7 +317,7 @@ private async Task CreatePurchaseIntentShouldHandleDifferentMandateTypes() PurchaseThreshold = new PurchaseThreshold { Amount = 100, - CurrencyCode = "USD" + CurrencyCode = Currency.USD }, Description = "Accessories purchase", ExpirationDate = DateTime.Parse("2026-09-30T23:59:59.000Z") @@ -353,7 +353,7 @@ private async Task CreatePurchaseIntentShouldHandleInternationalCurrencies() PurchaseThreshold = new PurchaseThreshold { Amount = 150, - CurrencyCode = "EUR" + CurrencyCode = Currency.EUR }, Description = "Compra de zapatos deportivos", ExpirationDate = DateTime.Parse("2026-08-31T23:59:59.000Z") @@ -367,7 +367,7 @@ private async Task CreatePurchaseIntentShouldHandleInternationalCurrencies() response.Status.ShouldBe("active"); response.Id.ShouldStartWith("pi_"); response.Mandates.ShouldNotBeNull(); - response.Mandates[0].PurchaseThreshold.CurrencyCode.ShouldBe("EUR"); + response.Mandates[0].PurchaseThreshold.CurrencyCode.ShouldBe(Currency.EUR); } } } \ No newline at end of file From 43061e96b10b4f37f061401bb374f1f6fa30813c Mon Sep 17 00:00:00 2001 From: david ruiz Date: Tue, 18 Nov 2025 16:00:07 +0100 Subject: [PATCH 09/18] Added all tests for the new CreatePICredentials, UpdatePI and DeletePI endpoints --- .../Agentic/AgenticClientTest.cs | 583 ++++++++++++++++ .../Agentic/AgenticIntegrationTest.cs | 639 ++++++++++++++++++ 2 files changed, 1222 insertions(+) diff --git a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs index 3bb7bfd1..1bf620ad 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs +++ b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs @@ -305,5 +305,588 @@ private async Task CreatePurchaseIntentShouldUseCorrectAuthorizationForCreatePur apiClient.Post(It.IsAny(), _authorization, It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } + + [Fact] + private async Task CreatePurchaseIntentCredentialsShouldCreatePurchaseIntentCredentials() + { + var purchaseIntentId = "pi_f3egwppx6rde3hg6itlqzp3h7e"; + var createCredentialsRequest = new AgenticPurchaseIntentCredentialsCreateRequest + { + TransactionData = new[] + { + new TransactionData + { + MerchantCountryCode = CountryCode.US, + MerchantName = "Nike Store", + MerchantCategoryCode = "5661", + MerchantUrl = "https://www.nike.com", + TransactionAmount = new TransactionAmount + { + Amount = 12999, + CurrencyCode = Currency.USD + } + } + } + }; + + var expectedResponse = new AgenticPurchaseIntentResponse + { + Id = purchaseIntentId, + Scheme = "visa", + Status = "credentials_created", + TokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + DeviceData = new DeviceInfo + { + IpAddress = "192.168.1.100", + UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", + DeviceBrand = "apple", + DeviceType = "tablet" + }, + CustomerPrompt = "Hey AI, I need Nike running shoes in size 10 under $130.00", + Mandates = new[] + { + new Mandate + { + Id = "mandate_123", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 100, + CurrencyCode = Currency.USD + }, + Description = "Purchase Nike Air Max 270 running shoes in size 10", + ExpirationDate = System.DateTime.Parse("2026-08-31T23:59:59.000Z") + } + }, + Links = new AgenticLinks + { + Self = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e", + CreateCredentials = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e/credentials", + Update = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e", + Cancel = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e/cancel" + } + }; + + _apiClient.Setup(apiClient => + apiClient.Post("agentic/purchase-intent/pi_f3egwppx6rde3hg6itlqzp3h7e/credentials", _authorization, createCredentialsRequest, + CancellationToken.None, null)) + .ReturnsAsync(() => expectedResponse); + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + var response = await client.CreatePurchaseIntentCredentials(purchaseIntentId, createCredentialsRequest, CancellationToken.None); + + response.ShouldNotBeNull(); + response.Id.ShouldBe(expectedResponse.Id); + response.Scheme.ShouldBe(expectedResponse.Scheme); + response.Status.ShouldBe(expectedResponse.Status); + response.TokenId.ShouldBe(expectedResponse.TokenId); + response.CustomerPrompt.ShouldBe(expectedResponse.CustomerPrompt); + response.Links.ShouldNotBeNull(); + response.Links.Self.ShouldBe(expectedResponse.Links.Self); + } + + [Fact] + private async Task CreatePurchaseIntentCredentialsShouldThrowExceptionWhenIdIsNull() + { + var createCredentialsRequest = new AgenticPurchaseIntentCredentialsCreateRequest + { + TransactionData = new[] + { + new TransactionData + { + MerchantName = "Test Store", + TransactionAmount = new TransactionAmount { Amount = 1000, CurrencyCode = Currency.USD } + } + } + }; + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + var exception = await Should.ThrowAsync( + async () => await client.CreatePurchaseIntentCredentials(null, createCredentialsRequest, CancellationToken.None)); + + exception.Message.ShouldContain("id"); + } + + [Fact] + private async Task CreatePurchaseIntentCredentialsShouldThrowExceptionWhenRequestIsNull() + { + var purchaseIntentId = "pi_test_123"; + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + var exception = await Should.ThrowAsync( + async () => await client.CreatePurchaseIntentCredentials(purchaseIntentId, null, CancellationToken.None)); + + exception.Message.ShouldContain("agenticPurchaseIntentCredentialsCreateRequest"); + } + + [Fact] + private async Task CreatePurchaseIntentCredentialsShouldCallCorrectEndpoint() + { + var purchaseIntentId = "pi_test_endpoint_123"; + var createCredentialsRequest = new AgenticPurchaseIntentCredentialsCreateRequest + { + TransactionData = new[] + { + new TransactionData + { + MerchantName = "Test Merchant", + MerchantCountryCode = CountryCode.US, + MerchantCategoryCode = "5411", + TransactionAmount = new TransactionAmount + { + Amount = 5000, + CurrencyCode = Currency.USD + } + } + } + }; + + _apiClient.Setup(apiClient => + apiClient.Post($"agentic/purchase-intent/{purchaseIntentId}/credentials", _authorization, createCredentialsRequest, + CancellationToken.None, null)) + .ReturnsAsync(() => new AgenticPurchaseIntentResponse()); + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + await client.CreatePurchaseIntentCredentials(purchaseIntentId, createCredentialsRequest, CancellationToken.None); + + _apiClient.Verify(apiClient => + apiClient.Post($"agentic/purchase-intent/{purchaseIntentId}/credentials", _authorization, createCredentialsRequest, + CancellationToken.None, null), Times.Once); + } + + [Fact] + private async Task CreatePurchaseIntentCredentialsShouldUseCorrectAuthorization() + { + var purchaseIntentId = "pi_auth_test_123"; + var createCredentialsRequest = new AgenticPurchaseIntentCredentialsCreateRequest + { + TransactionData = new[] + { + new TransactionData + { + MerchantName = "Auth Test Store", + TransactionAmount = new TransactionAmount { Amount = 2500, CurrencyCode = Currency.EUR } + } + } + }; + + _apiClient.Setup(apiClient => + apiClient.Post(It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(() => new AgenticPurchaseIntentResponse()); + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + await client.CreatePurchaseIntentCredentials(purchaseIntentId, createCredentialsRequest, CancellationToken.None); + + _apiClient.Verify(apiClient => + apiClient.Post(It.IsAny(), _authorization, + It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + private async Task CreatePurchaseIntentCredentialsShouldHandleMultipleTransactionData() + { + var purchaseIntentId = "pi_multi_test_123"; + var createCredentialsRequest = new AgenticPurchaseIntentCredentialsCreateRequest + { + TransactionData = new[] + { + new TransactionData + { + MerchantCountryCode = CountryCode.US, + MerchantName = "Electronics Store", + MerchantCategoryCode = "5732", + MerchantUrl = "https://electronics.example.com", + TransactionAmount = new TransactionAmount + { + Amount = 79999, + CurrencyCode = Currency.USD + } + }, + new TransactionData + { + MerchantCountryCode = CountryCode.GB, + MerchantName = "UK Fashion Store", + MerchantCategoryCode = "5651", + MerchantUrl = "https://fashion.co.uk", + TransactionAmount = new TransactionAmount + { + Amount = 4500, + CurrencyCode = Currency.GBP + } + } + } + }; + + var expectedResponse = new AgenticPurchaseIntentResponse + { + Id = purchaseIntentId, + Status = "credentials_created", + TokenId = "nt_multi_test_token" + }; + + _apiClient.Setup(apiClient => + apiClient.Post($"agentic/purchase-intent/{purchaseIntentId}/credentials", _authorization, createCredentialsRequest, + CancellationToken.None, null)) + .ReturnsAsync(() => expectedResponse); + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + var response = await client.CreatePurchaseIntentCredentials(purchaseIntentId, createCredentialsRequest, CancellationToken.None); + + response.ShouldNotBeNull(); + response.Id.ShouldBe(expectedResponse.Id); + response.Status.ShouldBe(expectedResponse.Status); + response.TokenId.ShouldBe(expectedResponse.TokenId); + } + + [Fact] + private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() + { + var purchaseIntentId = "pi_f3egwppx6rde3hg6itlqzp3h7e"; + var updatePurchaseIntentRequest = new AgenticPurchaseIntentUpdateRequest + { + CustomerPrompt = "Updated prompt: I'm looking for Nike running shoes in size 10.5, for under $200.", + Mandates = new[] + { + new Mandate + { + Id = "mandate_updated_123", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 20000, + CurrencyCode = Currency.USD + }, + Description = "Updated: Purchase Nike running shoes in size 10.5.", + ExpirationDate = System.DateTime.Parse("2026-12-31T23:59:59.000Z") + } + } + }; + + var expectedResponse = new AgenticPurchaseIntentResponse + { + Id = purchaseIntentId, + Scheme = "visa", + Status = "updated", + TokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + DeviceData = new DeviceInfo + { + IpAddress = "192.168.1.100", + UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", + DeviceBrand = "apple", + DeviceType = "tablet" + }, + CustomerPrompt = "Updated prompt: I'm looking for Nike running shoes in size 10.5, for under $200.", + Mandates = new[] + { + new Mandate + { + Id = "mandate_updated_123", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 20000, + CurrencyCode = Currency.USD + }, + Description = "Updated: Purchase Nike running shoes in size 10.5.", + ExpirationDate = System.DateTime.Parse("2026-12-31T23:59:59.000Z") + } + }, + Links = new AgenticLinks + { + Self = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e", + CreateCredentials = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e/credentials", + Update = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e", + Cancel = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e/cancel" + } + }; + + _apiClient.Setup(apiClient => + apiClient.Put($"agentic/purchase-intent/{purchaseIntentId}", _authorization, updatePurchaseIntentRequest, + CancellationToken.None, null)) + .ReturnsAsync(() => expectedResponse); + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + var response = await client.UpdatePurchaseIntent(purchaseIntentId, updatePurchaseIntentRequest, CancellationToken.None); + + response.ShouldNotBeNull(); + response.Id.ShouldBe(expectedResponse.Id); + response.Scheme.ShouldBe(expectedResponse.Scheme); + response.Status.ShouldBe(expectedResponse.Status); + response.TokenId.ShouldBe(expectedResponse.TokenId); + response.CustomerPrompt.ShouldBe(expectedResponse.CustomerPrompt); + response.Links.ShouldNotBeNull(); + response.Links.Self.ShouldBe(expectedResponse.Links.Self); + } + + [Fact] + private async Task UpdatePurchaseIntentShouldThrowExceptionWhenIdIsNull() + { + var updatePurchaseIntentRequest = new AgenticPurchaseIntentUpdateRequest + { + CustomerPrompt = "Test prompt", + Mandates = new[] + { + new Mandate + { + Id = "mandate_test", + Description = "Test mandate" + } + } + }; + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + var exception = await Should.ThrowAsync( + async () => await client.UpdatePurchaseIntent(null, updatePurchaseIntentRequest, CancellationToken.None)); + + exception.Message.ShouldContain("id"); + } + + [Fact] + private async Task UpdatePurchaseIntentShouldThrowExceptionWhenRequestIsNull() + { + var purchaseIntentId = "pi_test_123"; + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + var exception = await Should.ThrowAsync( + async () => await client.UpdatePurchaseIntent(purchaseIntentId, null, CancellationToken.None)); + + exception.Message.ShouldContain("agenticPurchaseIntentUpdateRequest"); + } + + [Fact] + private async Task UpdatePurchaseIntentShouldThrowExceptionWhenIdIsEmpty() + { + var updatePurchaseIntentRequest = new AgenticPurchaseIntentUpdateRequest + { + CustomerPrompt = "Test prompt" + }; + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + var exception = await Should.ThrowAsync( + async () => await client.UpdatePurchaseIntent("", updatePurchaseIntentRequest, CancellationToken.None)); + + exception.Message.ShouldContain("id"); + } + + [Fact] + private async Task UpdatePurchaseIntentShouldCallCorrectEndpoint() + { + var purchaseIntentId = "pi_test_update_123"; + var updatePurchaseIntentRequest = new AgenticPurchaseIntentUpdateRequest + { + CustomerPrompt = "Test update prompt", + Mandates = new[] + { + new Mandate + { + Id = "mandate_test_update", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 15000, + CurrencyCode = Currency.EUR + }, + Description = "Test update mandate" + } + } + }; + + _apiClient.Setup(apiClient => + apiClient.Put($"agentic/purchase-intent/{purchaseIntentId}", _authorization, updatePurchaseIntentRequest, + CancellationToken.None, null)) + .ReturnsAsync(() => new AgenticPurchaseIntentResponse()); + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + await client.UpdatePurchaseIntent(purchaseIntentId, updatePurchaseIntentRequest, CancellationToken.None); + + _apiClient.Verify(apiClient => + apiClient.Put($"agentic/purchase-intent/{purchaseIntentId}", _authorization, updatePurchaseIntentRequest, + CancellationToken.None, null), Times.Once); + } + + [Fact] + private async Task UpdatePurchaseIntentShouldUseCorrectAuthorization() + { + var purchaseIntentId = "pi_auth_test_123"; + var updatePurchaseIntentRequest = new AgenticPurchaseIntentUpdateRequest + { + CustomerPrompt = "Auth test prompt", + Mandates = new[] + { + new Mandate + { + Id = "mandate_auth_test", + Description = "Auth test mandate" + } + } + }; + + _apiClient.Setup(apiClient => + apiClient.Put(It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(() => new AgenticPurchaseIntentResponse()); + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + await client.UpdatePurchaseIntent(purchaseIntentId, updatePurchaseIntentRequest, CancellationToken.None); + + _apiClient.Verify(apiClient => + apiClient.Put(It.IsAny(), _authorization, + It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + } + + [Fact] + private async Task UpdatePurchaseIntentShouldHandlePartialUpdates() + { + var purchaseIntentId = "pi_partial_update_123"; + var updatePurchaseIntentRequest = new AgenticPurchaseIntentUpdateRequest + { + CustomerPrompt = "Only updating the customer prompt" + // Mandates is null - partial update + }; + + var expectedResponse = new AgenticPurchaseIntentResponse + { + Id = purchaseIntentId, + Status = "updated", + CustomerPrompt = "Only updating the customer prompt", + TokenId = "nt_partial_update_token" + }; + + _apiClient.Setup(apiClient => + apiClient.Put($"agentic/purchase-intent/{purchaseIntentId}", _authorization, updatePurchaseIntentRequest, + CancellationToken.None, null)) + .ReturnsAsync(() => expectedResponse); + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + var response = await client.UpdatePurchaseIntent(purchaseIntentId, updatePurchaseIntentRequest, CancellationToken.None); + + response.ShouldNotBeNull(); + response.Id.ShouldBe(expectedResponse.Id); + response.Status.ShouldBe(expectedResponse.Status); + response.CustomerPrompt.ShouldBe(expectedResponse.CustomerPrompt); + response.TokenId.ShouldBe(expectedResponse.TokenId); + } + + [Fact] + private async Task DeletePurchaseIntentShouldDeletePurchaseIntent() + { + var purchaseIntentId = "pi_f3egwppx6rde3hg6itlqzp3h7e"; + + var expectedResponse = new EmptyResponse + { + HttpStatusCode = 200 + }; + + _apiClient.Setup(apiClient => + apiClient.Delete($"agentic/purchase-intent/{purchaseIntentId}", _authorization, + CancellationToken.None)) + .ReturnsAsync(() => expectedResponse); + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + var response = await client.DeletePurchaseIntent(purchaseIntentId, CancellationToken.None); + + response.ShouldNotBeNull(); + response.HttpStatusCode.ShouldBe(200); + } + + [Fact] + private async Task DeletePurchaseIntentShouldThrowExceptionWhenIdIsNull() + { + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + var exception = await Should.ThrowAsync( + async () => await client.DeletePurchaseIntent(null, CancellationToken.None)); + + exception.Message.ShouldContain("id"); + } + + [Fact] + private async Task DeletePurchaseIntentShouldThrowExceptionWhenIdIsEmpty() + { + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + var exception = await Should.ThrowAsync( + async () => await client.DeletePurchaseIntent("", CancellationToken.None)); + + exception.Message.ShouldContain("id"); + } + + [Fact] + private async Task DeletePurchaseIntentShouldCallCorrectEndpoint() + { + var purchaseIntentId = "pi_test_delete_123"; + + _apiClient.Setup(apiClient => + apiClient.Delete($"agentic/purchase-intent/{purchaseIntentId}", _authorization, + CancellationToken.None)) + .ReturnsAsync(() => new EmptyResponse()); + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + await client.DeletePurchaseIntent(purchaseIntentId, CancellationToken.None); + + _apiClient.Verify(apiClient => + apiClient.Delete($"agentic/purchase-intent/{purchaseIntentId}", _authorization, + CancellationToken.None), Times.Once); + } + + [Fact] + private async Task DeletePurchaseIntentShouldUseCorrectAuthorization() + { + var purchaseIntentId = "pi_auth_test_123"; + + _apiClient.Setup(apiClient => + apiClient.Delete(It.IsAny(), It.IsAny(), + It.IsAny())) + .ReturnsAsync(() => new EmptyResponse()); + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + await client.DeletePurchaseIntent(purchaseIntentId, CancellationToken.None); + + _apiClient.Verify(apiClient => + apiClient.Delete(It.IsAny(), _authorization, + It.IsAny()), Times.Once); + } + + [Fact] + private async Task DeletePurchaseIntentShouldHandleDifferentPurchaseIntentIdFormats() + { + var testIds = new[] + { + "pi_short123", + "pi_f3egwppx6rde3hg6itlqzp3h7e", + "pi_very_long_purchase_intent_id_with_underscores_123456789" + }; + + foreach (var purchaseIntentId in testIds) + { + _apiClient.Setup(apiClient => + apiClient.Delete($"agentic/purchase-intent/{purchaseIntentId}", _authorization, + CancellationToken.None)) + .ReturnsAsync(() => new EmptyResponse()); + + IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); + + var response = await client.DeletePurchaseIntent(purchaseIntentId, CancellationToken.None); + + response.ShouldNotBeNull(); + + _apiClient.Verify(apiClient => + apiClient.Delete($"agentic/purchase-intent/{purchaseIntentId}", _authorization, + CancellationToken.None), Times.Once); + } + } } } \ No newline at end of file diff --git a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs index d83c8353..82e970ca 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs +++ b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs @@ -1,4 +1,5 @@ using System; +using System.Net; using System.Threading.Tasks; using Shouldly; using Xunit; @@ -369,5 +370,643 @@ private async Task CreatePurchaseIntentShouldHandleInternationalCurrencies() response.Mandates.ShouldNotBeNull(); response.Mandates[0].PurchaseThreshold.CurrencyCode.ShouldBe(Currency.EUR); } + + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + private async Task CreatePurchaseIntentCredentialsShouldCreateCredentials() + { + // First create a purchase intent to get an ID + var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + { + NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + Device = new DeviceInfo + { + IpAddress = "192.168.1.100", + UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + }, + CustomerPrompt = "I need running shoes in size 10, under $150.", + Mandates = new[] + { + new Mandate + { + Id = "mandate_credentials_test", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 15000, + CurrencyCode = Currency.USD + }, + Description = "Purchase running shoes for credentials test.", + ExpirationDate = DateTime.Parse("2026-12-31T23:59:59.000Z") + } + } + }; + + var purchaseIntentResponse = await DefaultApi.AgenticClient().CreatePurchaseIntent(createPurchaseIntentRequest); + purchaseIntentResponse.ShouldNotBeNull(); + purchaseIntentResponse.Id.ShouldNotBeNullOrEmpty(); + + // Now create credentials for this purchase intent + var createCredentialsRequest = new AgenticPurchaseIntentCredentialsCreateRequest + { + TransactionData = new[] + { + new TransactionData + { + MerchantCountryCode = CountryCode.US, + MerchantName = "Nike Store", + MerchantCategoryCode = "5661", + MerchantUrl = "https://www.nike.com", + TransactionAmount = new TransactionAmount + { + Amount = 12999, + CurrencyCode = Currency.USD + } + } + } + }; + + var credentialsResponse = await DefaultApi.AgenticClient().CreatePurchaseIntentCredentials( + purchaseIntentResponse.Id, createCredentialsRequest); + + credentialsResponse.ShouldNotBeNull(); + credentialsResponse.Id.ShouldBe(purchaseIntentResponse.Id); + credentialsResponse.Status.ShouldNotBeNullOrEmpty(); + credentialsResponse.TokenId.ShouldNotBeNullOrEmpty(); + + // Validate that the purchase intent ID follows expected pattern + credentialsResponse.Id.ShouldStartWith("pi_"); + + // Validate links are present and updated + credentialsResponse.Links.ShouldNotBeNull(); + credentialsResponse.Links.Self.ShouldNotBeNullOrEmpty(); + credentialsResponse.Links.CreateCredentials.ShouldNotBeNullOrEmpty(); + } + + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + private async Task CreatePurchaseIntentCredentialsShouldHandleMultipleTransactionData() + { + // First create a purchase intent + var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + { + NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + Device = new DeviceInfo + { + IpAddress = "203.0.113.195", + UserAgent = "Test Agent Multi Transaction" + }, + CustomerPrompt = "I need multiple items: electronics and clothing.", + Mandates = new[] + { + new Mandate + { + Id = "mandate_multi_transaction", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 100000, + CurrencyCode = Currency.USD + }, + Description = "Purchase multiple items across different categories.", + ExpirationDate = DateTime.Parse("2026-12-31T23:59:59.000Z") + } + } + }; + + var purchaseIntentResponse = await DefaultApi.AgenticClient().CreatePurchaseIntent(createPurchaseIntentRequest); + purchaseIntentResponse.ShouldNotBeNull(); + + // Create credentials with multiple transaction data + var createCredentialsRequest = new AgenticPurchaseIntentCredentialsCreateRequest + { + TransactionData = new[] + { + new TransactionData + { + MerchantCountryCode = CountryCode.US, + MerchantName = "Best Electronics", + MerchantCategoryCode = "5732", + MerchantUrl = "https://bestelectronics.com", + TransactionAmount = new TransactionAmount + { + Amount = 79999, + CurrencyCode = Currency.USD + } + }, + new TransactionData + { + MerchantCountryCode = CountryCode.US, + MerchantName = "Fashion Central", + MerchantCategoryCode = "5651", + MerchantUrl = "https://fashioncentral.com", + TransactionAmount = new TransactionAmount + { + Amount = 15999, + CurrencyCode = Currency.USD + } + } + } + }; + + var credentialsResponse = await DefaultApi.AgenticClient().CreatePurchaseIntentCredentials( + purchaseIntentResponse.Id, createCredentialsRequest); + + credentialsResponse.ShouldNotBeNull(); + credentialsResponse.Id.ShouldBe(purchaseIntentResponse.Id); + credentialsResponse.Status.ShouldNotBeNullOrEmpty(); + credentialsResponse.TokenId.ShouldNotBeNullOrEmpty(); + + // Validate response structure + credentialsResponse.Id.ShouldStartWith("pi_"); + credentialsResponse.Links.ShouldNotBeNull(); + } + + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + private async Task CreatePurchaseIntentCredentialsShouldHandleInternationalTransactions() + { + // Create purchase intent + var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + { + NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + Device = new DeviceInfo + { + IpAddress = "80.112.12.34", // European IP + UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36" + }, + CustomerPrompt = "Looking for luxury items in different currencies.", + Mandates = new[] + { + new Mandate + { + Id = "mandate_international", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 200000, + CurrencyCode = Currency.EUR + }, + Description = "International luxury purchases.", + ExpirationDate = DateTime.Parse("2026-12-31T23:59:59.000Z") + } + } + }; + + var purchaseIntentResponse = await DefaultApi.AgenticClient().CreatePurchaseIntent(createPurchaseIntentRequest); + purchaseIntentResponse.ShouldNotBeNull(); + + // Create credentials with international transaction data + var createCredentialsRequest = new AgenticPurchaseIntentCredentialsCreateRequest + { + TransactionData = new[] + { + new TransactionData + { + MerchantCountryCode = CountryCode.FR, + MerchantName = "Luxury Paris Boutique", + MerchantCategoryCode = "5944", + MerchantUrl = "https://luxuryparis.fr", + TransactionAmount = new TransactionAmount + { + Amount = 89999, + CurrencyCode = Currency.EUR + } + }, + new TransactionData + { + MerchantCountryCode = CountryCode.GB, + MerchantName = "London Fashion House", + MerchantCategoryCode = "5651", + MerchantUrl = "https://londonfashion.co.uk", + TransactionAmount = new TransactionAmount + { + Amount = 45000, + CurrencyCode = Currency.GBP + } + } + } + }; + + var credentialsResponse = await DefaultApi.AgenticClient().CreatePurchaseIntentCredentials( + purchaseIntentResponse.Id, createCredentialsRequest); + + credentialsResponse.ShouldNotBeNull(); + credentialsResponse.Id.ShouldBe(purchaseIntentResponse.Id); + credentialsResponse.Status.ShouldNotBeNullOrEmpty(); + credentialsResponse.TokenId.ShouldNotBeNullOrEmpty(); + + // Validate international handling + credentialsResponse.Id.ShouldStartWith("pi_"); + credentialsResponse.Links.ShouldNotBeNull(); + credentialsResponse.Links.Self.ShouldContain(purchaseIntentResponse.Id); + } + + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + private async Task CreatePurchaseIntentCredentialsShouldHandleMinimalTransactionData() + { + // Create purchase intent first + var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + { + NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + Device = new DeviceInfo + { + IpAddress = "10.0.0.1" + }, + CustomerPrompt = "Basic purchase for minimal test" + }; + + var purchaseIntentResponse = await DefaultApi.AgenticClient().CreatePurchaseIntent(createPurchaseIntentRequest); + purchaseIntentResponse.ShouldNotBeNull(); + + // Create credentials with minimal transaction data + var createCredentialsRequest = new AgenticPurchaseIntentCredentialsCreateRequest + { + TransactionData = new[] + { + new TransactionData + { + MerchantName = "Basic Store", + TransactionAmount = new TransactionAmount + { + Amount = 1000, + CurrencyCode = Currency.USD + } + } + } + }; + + var credentialsResponse = await DefaultApi.AgenticClient().CreatePurchaseIntentCredentials( + purchaseIntentResponse.Id, createCredentialsRequest); + + credentialsResponse.ShouldNotBeNull(); + credentialsResponse.Id.ShouldBe(purchaseIntentResponse.Id); + credentialsResponse.Status.ShouldNotBeNullOrEmpty(); + credentialsResponse.TokenId.ShouldNotBeNullOrEmpty(); + credentialsResponse.Id.ShouldStartWith("pi_"); + } + + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() + { + // First create a purchase intent to update + var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + { + NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + Device = new DeviceInfo + { + IpAddress = "192.168.1.100", + UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", + DeviceBrand = "apple", + DeviceType = "tablet" + }, + CustomerPrompt = "I'm looking for running shoes in a size 10, for under $150.", + Mandates = new[] + { + new Mandate + { + Id = "mandate_original_123", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 15000, + CurrencyCode = Currency.USD + }, + Description = "Purchase running shoes in size 10.", + ExpirationDate = System.DateTime.Parse("2026-08-31T23:59:59.000Z") + } + } + }; + + var purchaseIntentResponse = await DefaultApi.AgenticClient().CreatePurchaseIntent(createPurchaseIntentRequest); + + purchaseIntentResponse.ShouldNotBeNull(); + purchaseIntentResponse.Id.ShouldNotBeNullOrEmpty(); + + // Now update the purchase intent + var updatePurchaseIntentRequest = new AgenticPurchaseIntentUpdateRequest + { + CustomerPrompt = "Updated: I'm looking for Nike running shoes in size 10.5, for under $200.", + Mandates = new[] + { + new Mandate + { + Id = "mandate_updated_123", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 20000, + CurrencyCode = Currency.USD + }, + Description = "Updated: Purchase Nike running shoes in size 10.5.", + ExpirationDate = System.DateTime.Parse("2026-12-31T23:59:59.000Z") + } + } + }; + + var updateResponse = await DefaultApi.AgenticClient().UpdatePurchaseIntent(purchaseIntentResponse.Id, updatePurchaseIntentRequest); + + updateResponse.ShouldNotBeNull(); + updateResponse.Id.ShouldBe(purchaseIntentResponse.Id); + updateResponse.CustomerPrompt.ShouldBe(updatePurchaseIntentRequest.CustomerPrompt); + updateResponse.Status.ShouldNotBeNullOrEmpty(); + } + + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + private async Task UpdatePurchaseIntentShouldHandleNonExistentPurchaseIntent() + { + var nonExistentId = "pi_nonexistent_123456"; + var updatePurchaseIntentRequest = new AgenticPurchaseIntentUpdateRequest + { + CustomerPrompt = "This should fail" + }; + + var exception = await Should.ThrowAsync( + async () => await DefaultApi.AgenticClient().UpdatePurchaseIntent(nonExistentId, updatePurchaseIntentRequest)); + + exception.HttpStatusCode.ShouldBeOneOf(HttpStatusCode.NotFound, HttpStatusCode.BadRequest); + } + + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + private async Task UpdatePurchaseIntentShouldValidateInputParameters() + { + // Test with null ID + var updateRequest = new AgenticPurchaseIntentUpdateRequest + { + CustomerPrompt = "Valid prompt" + }; + + var exception = await Should.ThrowAsync( + async () => await DefaultApi.AgenticClient().UpdatePurchaseIntent(null, updateRequest)); + + exception.Message.ShouldContain("id"); + + // Test with null request + var validId = "pi_valid_123"; + var nullRequestException = await Should.ThrowAsync( + async () => await DefaultApi.AgenticClient().UpdatePurchaseIntent(validId, null)); + + nullRequestException.Message.ShouldContain("agenticPurchaseIntentUpdateRequest"); + } + + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + private async Task UpdatePurchaseIntentShouldHandlePartialUpdates() + { + // First create a purchase intent + var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + { + NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + Device = new DeviceInfo + { + IpAddress = "192.168.1.100", + UserAgent = "Mozilla/5.0 Test Browser" + }, + CustomerPrompt = "Original prompt", + Mandates = new[] + { + new Mandate + { + Id = "mandate_original", + Description = "Original mandate" + } + } + }; + + var purchaseIntentResponse = await DefaultApi.AgenticClient().CreatePurchaseIntent(createPurchaseIntentRequest); + + // Update only the customer prompt (partial update) + var partialUpdateRequest = new AgenticPurchaseIntentUpdateRequest + { + CustomerPrompt = "Updated prompt only" + // Mandates is null - testing partial update + }; + + var updateResponse = await DefaultApi.AgenticClient().UpdatePurchaseIntent( + purchaseIntentResponse.Id, partialUpdateRequest); + + updateResponse.ShouldNotBeNull(); + updateResponse.Id.ShouldBe(purchaseIntentResponse.Id); + updateResponse.CustomerPrompt.ShouldBe(partialUpdateRequest.CustomerPrompt); + } + + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + private async Task UpdatePurchaseIntentShouldHandleDifferentPurchaseIntentStates() + { + // Create purchase intent + var createRequest = new AgenticPurchaseIntentCreateRequest + { + NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + Device = new DeviceInfo + { + IpAddress = "192.168.1.100", + UserAgent = "Mozilla/5.0 State Test" + }, + CustomerPrompt = "Test different states", + Mandates = new[] + { + new Mandate + { + Id = "mandate_state_test", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 10000, + CurrencyCode = Currency.USD + }, + Description = "State test mandate" + } + } + }; + + var purchaseIntentResponse = await DefaultApi.AgenticClient().CreatePurchaseIntent(createRequest); + + // Try to update the purchase intent regardless of its current state + var updateRequest = new AgenticPurchaseIntentUpdateRequest + { + CustomerPrompt = "Updated for state testing", + Mandates = new[] + { + new Mandate + { + Id = "mandate_updated_state", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 12000, + CurrencyCode = Currency.USD + }, + Description = "Updated state test mandate" + } + } + }; + + // This should work regardless of the purchase intent's current state + var updateResponse = await DefaultApi.AgenticClient().UpdatePurchaseIntent( + purchaseIntentResponse.Id, updateRequest); + + updateResponse.ShouldNotBeNull(); + updateResponse.Id.ShouldBe(purchaseIntentResponse.Id); + } + + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + private async Task DeletePurchaseIntentShouldDeletePurchaseIntent() + { + // First create a purchase intent to delete + var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + { + NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + Device = new DeviceInfo + { + IpAddress = "192.168.1.100", + UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + }, + CustomerPrompt = "I need running shoes for deletion test.", + Mandates = new[] + { + new Mandate + { + Id = "mandate_delete_test", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 10000, + CurrencyCode = Currency.USD + }, + Description = "Purchase intent for deletion test.", + ExpirationDate = DateTime.Parse("2026-12-31T23:59:59.000Z") + } + } + }; + + var purchaseIntentResponse = await DefaultApi.AgenticClient().CreatePurchaseIntent(createPurchaseIntentRequest); + purchaseIntentResponse.ShouldNotBeNull(); + purchaseIntentResponse.Id.ShouldNotBeNullOrEmpty(); + purchaseIntentResponse.Id.ShouldStartWith("pi_"); + + // Now delete the purchase intent + var deleteResponse = await DefaultApi.AgenticClient().DeletePurchaseIntent(purchaseIntentResponse.Id); + + deleteResponse.ShouldNotBeNull(); + + // Validate successful deletion (typically 200 or 204) + deleteResponse.HttpStatusCode.ShouldBeOneOf(200, 204); + } + + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + private async Task DeletePurchaseIntentShouldHandleNonExistentPurchaseIntent() + { + var nonExistentId = "pi_non_existent_123456789"; + + // Attempt to delete non-existent purchase intent should handle gracefully + var exception = await Should.ThrowAsync( + async () => await DefaultApi.AgenticClient().DeletePurchaseIntent(nonExistentId)); + + // Should return 404 Not Found for non-existent purchase intent + exception.HttpStatusCode.ShouldBe(HttpStatusCode.NotFound); + } + + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + private async Task DeletePurchaseIntentShouldHandleAlreadyDeletedPurchaseIntent() + { + // First create a purchase intent + var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + { + NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + Device = new DeviceInfo + { + IpAddress = "203.0.113.195", + UserAgent = "Test Agent Double Delete" + }, + CustomerPrompt = "Purchase intent for double deletion test.", + Mandates = new[] + { + new Mandate + { + Id = "mandate_double_delete", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 5000, + CurrencyCode = Currency.USD + }, + Description = "Double deletion test mandate.", + ExpirationDate = DateTime.Parse("2026-12-31T23:59:59.000Z") + } + } + }; + + var purchaseIntentResponse = await DefaultApi.AgenticClient().CreatePurchaseIntent(createPurchaseIntentRequest); + purchaseIntentResponse.ShouldNotBeNull(); + + // Delete it once + var firstDeleteResponse = await DefaultApi.AgenticClient().DeletePurchaseIntent(purchaseIntentResponse.Id); + firstDeleteResponse.ShouldNotBeNull(); + + // Try to delete it again - should handle gracefully (404 or other appropriate response) + var exception = await Should.ThrowAsync( + async () => await DefaultApi.AgenticClient().DeletePurchaseIntent(purchaseIntentResponse.Id)); + + // Should return 404 Not Found for already deleted purchase intent + exception.HttpStatusCode.ShouldBe(HttpStatusCode.NotFound); + } + + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + private async Task DeletePurchaseIntentShouldHandleDifferentPurchaseIntentStates() + { + // Test deleting purchase intents in different states + var testCases = new[] + { + new { Description = "Basic purchase intent", CustomerPrompt = "Basic deletion test" }, + new { Description = "Complex purchase intent", CustomerPrompt = "Complex purchase intent with multiple mandates for deletion test" } + }; + + foreach (var testCase in testCases) + { + // Create purchase intent + var createRequest = new AgenticPurchaseIntentCreateRequest + { + NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + Device = new DeviceInfo + { + IpAddress = "10.0.0.1", + UserAgent = "Test Agent States" + }, + CustomerPrompt = testCase.CustomerPrompt, + Mandates = new[] + { + new Mandate + { + Id = $"mandate_states_{testCase.Description.Replace(" ", "_").ToLower()}", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 7500, + CurrencyCode = Currency.USD + }, + Description = $"Mandate for {testCase.Description}", + ExpirationDate = DateTime.Parse("2026-12-31T23:59:59.000Z") + } + } + }; + + var purchaseIntentResponse = await DefaultApi.AgenticClient().CreatePurchaseIntent(createRequest); + purchaseIntentResponse.ShouldNotBeNull(); + purchaseIntentResponse.Id.ShouldStartWith("pi_"); + + // Delete the purchase intent + var deleteResponse = await DefaultApi.AgenticClient().DeletePurchaseIntent(purchaseIntentResponse.Id); + + deleteResponse.ShouldNotBeNull(); + deleteResponse.HttpStatusCode.ShouldBeOneOf(200, 204); + } + } + + [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + private async Task DeletePurchaseIntentShouldHandleInvalidIdFormat() + { + var invalidIds = new[] + { + "invalid_id_format", + "pi_", + "not_a_purchase_intent_id", + "123456789" + }; + + foreach (var invalidId in invalidIds) + { + var exception = await Should.ThrowAsync( + async () => await DefaultApi.AgenticClient().DeletePurchaseIntent(invalidId)); + + // Should return 400 Bad Request or 404 Not Found for invalid ID format + exception.HttpStatusCode.ShouldBeOneOf(HttpStatusCode.BadRequest, HttpStatusCode.NotFound); + } + } } } \ No newline at end of file From d8e10cc125a772cfd76cbad5368b65c539acb687 Mon Sep 17 00:00:00 2001 From: david ruiz Date: Wed, 19 Nov 2025 11:00:48 +0100 Subject: [PATCH 10/18] Usiong Resource base instead of HttpMetadata, removed not needed Error response, adjusted new links usage in the tests. --- .../Responses/AgenticEnrollResponse.cs | 3 +- .../Agentic/Responses/AgenticErrorResponse.cs | 23 ------------ .../AgenticPurchaseIntentResponse.cs | 9 +---- .../Responses/Entities/AgenticLinks.cs | 28 -------------- .../Agentic/AgenticClientTest.cs | 37 ++++++++++--------- .../Agentic/AgenticIntegrationTest.cs | 10 ++--- 6 files changed, 28 insertions(+), 82 deletions(-) delete mode 100644 src/CheckoutSdk/Agentic/Responses/AgenticErrorResponse.cs delete mode 100644 src/CheckoutSdk/Agentic/Responses/Entities/AgenticLinks.cs diff --git a/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs b/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs index b7471309..3114b9dc 100644 --- a/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs +++ b/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs @@ -1,11 +1,12 @@ using System; +using Checkout.Common; namespace Checkout.Agentic.Responses { /// /// Agentic Enroll Response /// - public class AgenticEnrollResponse : HttpMetadata + public class AgenticEnrollResponse : Resource { /// /// The unique token identifier for the enrolled agentic service diff --git a/src/CheckoutSdk/Agentic/Responses/AgenticErrorResponse.cs b/src/CheckoutSdk/Agentic/Responses/AgenticErrorResponse.cs deleted file mode 100644 index 9a5ac5a0..00000000 --- a/src/CheckoutSdk/Agentic/Responses/AgenticErrorResponse.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Checkout.Agentic.Responses -{ - /// - /// Agentic Error Response launched when something went wrong - /// - public class AgenticErrorResponse : HttpMetadata - { - /// - /// The request ID - /// - public string RequestId { get; set; } - - /// - /// The error type if any - /// - public string ErrorType { get; set; } - - /// - /// List of error codes if any - /// - public string[] ErrorCodes { get; set; } - } -} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Responses/AgenticPurchaseIntentResponse.cs b/src/CheckoutSdk/Agentic/Responses/AgenticPurchaseIntentResponse.cs index b9142df5..ed99a9c3 100644 --- a/src/CheckoutSdk/Agentic/Responses/AgenticPurchaseIntentResponse.cs +++ b/src/CheckoutSdk/Agentic/Responses/AgenticPurchaseIntentResponse.cs @@ -1,4 +1,5 @@ using Checkout.Agentic.Requests; +using Checkout.Common; using Newtonsoft.Json; namespace Checkout.Agentic.Responses @@ -6,7 +7,7 @@ namespace Checkout.Agentic.Responses /// /// Response from purchase intent creation /// - public class AgenticPurchaseIntentResponse : HttpMetadata + public class AgenticPurchaseIntentResponse : Resource { /// /// The purchase intent ID @@ -42,11 +43,5 @@ public class AgenticPurchaseIntentResponse : HttpMetadata /// List of mandates for the purchase intent /// public Mandate[] Mandates { get; set; } - - /// - /// Links for related operations - /// - [JsonProperty("_links")] - public AgenticLinks Links { get; set; } } } \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Responses/Entities/AgenticLinks.cs b/src/CheckoutSdk/Agentic/Responses/Entities/AgenticLinks.cs deleted file mode 100644 index fc5686b2..00000000 --- a/src/CheckoutSdk/Agentic/Responses/Entities/AgenticLinks.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace Checkout.Agentic.Responses -{ - /// - /// Links for agentic purchase intent operations - /// - public class AgenticLinks - { - /// - /// Link to self - /// - public string Self { get; set; } - - /// - /// Link to create credentials - /// - public string CreateCredentials { get; set; } - - /// - /// Link to update - /// - public string Update { get; set; } - - /// - /// Link to cancel - /// - public string Cancel { get; set; } - } -} \ No newline at end of file diff --git a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs index 1bf620ad..5bd00042 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs +++ b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Moq; @@ -207,12 +208,12 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() ExpirationDate = System.DateTime.Parse("2026-08-31T23:59:59.000Z") } }, - Links = new AgenticLinks + Links = new Dictionary { - Self = "https://api.example.com/agentic/purchase-intents/intent_123", - CreateCredentials = "https://api.example.com/agentic/purchase-intents/intent_123/credentials", - Update = "https://api.example.com/agentic/purchase-intents/intent_123", - Cancel = "https://api.example.com/agentic/purchase-intents/intent_123/cancel" + { "self", new Link { Href = "https://api.example.com/agentic/purchase-intents/intent_789" } }, + { "create-credentials", new Link { Href = "https://api.example.com/agentic/purchase-intents/intent_789/credentials" } }, + { "update", new Link { Href = "https://api.example.com/agentic/purchase-intents/intent_789" } }, + { "cancel", new Link { Href = "https://api.example.com/agentic/purchase-intents/intent_789/cancel" } } } }; @@ -232,7 +233,7 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() response.TokenId.ShouldBe(expectedResponse.TokenId); response.CustomerPrompt.ShouldBe(expectedResponse.CustomerPrompt); response.Links.ShouldNotBeNull(); - response.Links.Self.ShouldBe(expectedResponse.Links.Self); + response.Links["self"].Href.ShouldBe(expectedResponse.Links["self"].Href); } [Fact] @@ -357,12 +358,12 @@ private async Task CreatePurchaseIntentCredentialsShouldCreatePurchaseIntentCred ExpirationDate = System.DateTime.Parse("2026-08-31T23:59:59.000Z") } }, - Links = new AgenticLinks + Links = new Dictionary { - Self = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e", - CreateCredentials = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e/credentials", - Update = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e", - Cancel = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e/cancel" + { "self", new Link { Href = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e" } }, + { "create-credentials", new Link { Href = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e/credentials" } }, + { "update", new Link { Href = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e" } }, + { "cancel", new Link { Href = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e/cancel" } } } }; @@ -382,7 +383,7 @@ private async Task CreatePurchaseIntentCredentialsShouldCreatePurchaseIntentCred response.TokenId.ShouldBe(expectedResponse.TokenId); response.CustomerPrompt.ShouldBe(expectedResponse.CustomerPrompt); response.Links.ShouldNotBeNull(); - response.Links.Self.ShouldBe(expectedResponse.Links.Self); + response.Links["self"].Href.ShouldBe(expectedResponse.Links["self"].Href); } [Fact] @@ -595,12 +596,12 @@ private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() ExpirationDate = System.DateTime.Parse("2026-12-31T23:59:59.000Z") } }, - Links = new AgenticLinks + Links = new Dictionary { - Self = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e", - CreateCredentials = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e/credentials", - Update = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e", - Cancel = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e/cancel" + { "self", new Link { Href = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e" } }, + { "create-credentials", new Link { Href = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e/credentials" } }, + { "update", new Link { Href = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e" } }, + { "cancel", new Link { Href = "https://api.example.com/agentic/purchase-intents/pi_f3egwppx6rde3hg6itlqzp3h7e/cancel" } } } }; @@ -620,7 +621,7 @@ private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() response.TokenId.ShouldBe(expectedResponse.TokenId); response.CustomerPrompt.ShouldBe(expectedResponse.CustomerPrompt); response.Links.ShouldNotBeNull(); - response.Links.Self.ShouldBe(expectedResponse.Links.Self); + response.Links["self"].Href.ShouldBe(expectedResponse.Links["self"].Href); } [Fact] diff --git a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs index 82e970ca..e3dfc8c2 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs +++ b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs @@ -229,8 +229,8 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() // Validate links are present response.Links.ShouldNotBeNull(); - response.Links.Self.ShouldNotBeNullOrEmpty(); - response.Links.CreateCredentials.ShouldNotBeNullOrEmpty(); + response.Links["self"].Href.ShouldNotBeNullOrEmpty(); + response.Links["create-credentials"].Href.ShouldNotBeNullOrEmpty(); } [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] @@ -437,8 +437,8 @@ private async Task CreatePurchaseIntentCredentialsShouldCreateCredentials() // Validate links are present and updated credentialsResponse.Links.ShouldNotBeNull(); - credentialsResponse.Links.Self.ShouldNotBeNullOrEmpty(); - credentialsResponse.Links.CreateCredentials.ShouldNotBeNullOrEmpty(); + credentialsResponse.Links["self"].Href.ShouldNotBeNullOrEmpty(); + credentialsResponse.Links["create-credentials"].Href.ShouldNotBeNullOrEmpty(); } [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] @@ -593,7 +593,7 @@ private async Task CreatePurchaseIntentCredentialsShouldHandleInternationalTrans // Validate international handling credentialsResponse.Id.ShouldStartWith("pi_"); credentialsResponse.Links.ShouldNotBeNull(); - credentialsResponse.Links.Self.ShouldContain(purchaseIntentResponse.Id); + credentialsResponse.Links["self"].Href.ShouldContain(purchaseIntentResponse.Id); } [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] From e4fb74eeb79e6d0491c3d8bec002915ee419ad01 Mon Sep 17 00:00:00 2001 From: david ruiz Date: Wed, 19 Nov 2025 11:11:12 +0100 Subject: [PATCH 11/18] AgenticClientTest to SecretKeyOrOAuth authentication --- test/CheckoutSdkTest/Agentic/AgenticClientTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs index 5bd00042..f4cc4368 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs +++ b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs @@ -12,9 +12,9 @@ namespace Checkout.Agentic { public class AgenticClientTest : UnitTestFixture { - private readonly SdkAuthorization _authorization = new SdkAuthorization(PlatformType.Default, ValidDefaultSk); + private readonly SdkAuthorization _authorization = new SdkAuthorization(PlatformType.DefaultOAuth, ValidDefaultSk); private readonly Mock _apiClient = new Mock(); - private readonly Mock _sdkCredentials = new Mock(PlatformType.Default); + private readonly Mock _sdkCredentials = new Mock(PlatformType.DefaultOAuth); private readonly Mock _httpClientFactory = new Mock(); private readonly Mock _configuration; From 41e1abb88f32895c7476a5f9300ed23a9e2ed049 Mon Sep 17 00:00:00 2001 From: david ruiz Date: Wed, 19 Nov 2025 15:59:44 +0100 Subject: [PATCH 12/18] isolated the entities in a new namespace away from request, updated all dependencies --- .../Agentic/{Requests => }/Entities/AgenticCustomer.cs | 2 +- src/CheckoutSdk/Agentic/{Requests => }/Entities/DeviceInfo.cs | 2 +- src/CheckoutSdk/Agentic/{Requests => }/Entities/Mandate.cs | 2 +- .../Agentic/{Requests => }/Entities/PaymentSource.cs | 2 +- .../Agentic/{Requests => }/Entities/PurchaseThreshold.cs | 2 +- .../Agentic/{Requests => }/Entities/TransactionAmount.cs | 2 +- .../Agentic/{Requests => }/Entities/TransactionData.cs | 2 +- src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs | 2 ++ .../Agentic/Requests/AgenticPurchaseIntentCreateRequest.cs | 2 ++ .../Requests/AgenticPurchaseIntentCredentialsCreateRequest.cs | 2 ++ .../Agentic/Requests/AgenticPurchaseIntentUpdateRequest.cs | 2 ++ .../Agentic/Responses/AgenticPurchaseIntentResponse.cs | 3 +-- test/CheckoutSdkTest/Agentic/AgenticClientTest.cs | 2 ++ test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs | 3 ++- 14 files changed, 20 insertions(+), 10 deletions(-) rename src/CheckoutSdk/Agentic/{Requests => }/Entities/AgenticCustomer.cs (94%) rename src/CheckoutSdk/Agentic/{Requests => }/Entities/DeviceInfo.cs (95%) rename src/CheckoutSdk/Agentic/{Requests => }/Entities/Mandate.cs (95%) rename src/CheckoutSdk/Agentic/{Requests => }/Entities/PaymentSource.cs (95%) rename src/CheckoutSdk/Agentic/{Requests => }/Entities/PurchaseThreshold.cs (91%) rename src/CheckoutSdk/Agentic/{Requests => }/Entities/TransactionAmount.cs (92%) rename src/CheckoutSdk/Agentic/{Requests => }/Entities/TransactionData.cs (95%) diff --git a/src/CheckoutSdk/Agentic/Requests/Entities/AgenticCustomer.cs b/src/CheckoutSdk/Agentic/Entities/AgenticCustomer.cs similarity index 94% rename from src/CheckoutSdk/Agentic/Requests/Entities/AgenticCustomer.cs rename to src/CheckoutSdk/Agentic/Entities/AgenticCustomer.cs index 425d847a..5db66da8 100644 --- a/src/CheckoutSdk/Agentic/Requests/Entities/AgenticCustomer.cs +++ b/src/CheckoutSdk/Agentic/Entities/AgenticCustomer.cs @@ -1,7 +1,7 @@ using Newtonsoft.Json; using Checkout.Common; -namespace Checkout.Agentic.Requests +namespace Checkout.Agentic.Entities { /// /// Customer information for agentic enrollment diff --git a/src/CheckoutSdk/Agentic/Requests/Entities/DeviceInfo.cs b/src/CheckoutSdk/Agentic/Entities/DeviceInfo.cs similarity index 95% rename from src/CheckoutSdk/Agentic/Requests/Entities/DeviceInfo.cs rename to src/CheckoutSdk/Agentic/Entities/DeviceInfo.cs index f0bfb87c..d490e0b6 100644 --- a/src/CheckoutSdk/Agentic/Requests/Entities/DeviceInfo.cs +++ b/src/CheckoutSdk/Agentic/Entities/DeviceInfo.cs @@ -1,6 +1,6 @@ using Newtonsoft.Json; -namespace Checkout.Agentic.Requests +namespace Checkout.Agentic.Entities { /// /// Device Information diff --git a/src/CheckoutSdk/Agentic/Requests/Entities/Mandate.cs b/src/CheckoutSdk/Agentic/Entities/Mandate.cs similarity index 95% rename from src/CheckoutSdk/Agentic/Requests/Entities/Mandate.cs rename to src/CheckoutSdk/Agentic/Entities/Mandate.cs index 40428e0c..516d1ccb 100644 --- a/src/CheckoutSdk/Agentic/Requests/Entities/Mandate.cs +++ b/src/CheckoutSdk/Agentic/Entities/Mandate.cs @@ -1,6 +1,6 @@ using System; -namespace Checkout.Agentic.Requests +namespace Checkout.Agentic.Entities { /// /// Mandate configuration for purchase intents diff --git a/src/CheckoutSdk/Agentic/Requests/Entities/PaymentSource.cs b/src/CheckoutSdk/Agentic/Entities/PaymentSource.cs similarity index 95% rename from src/CheckoutSdk/Agentic/Requests/Entities/PaymentSource.cs rename to src/CheckoutSdk/Agentic/Entities/PaymentSource.cs index 0141541b..1f972500 100644 --- a/src/CheckoutSdk/Agentic/Requests/Entities/PaymentSource.cs +++ b/src/CheckoutSdk/Agentic/Entities/PaymentSource.cs @@ -1,7 +1,7 @@ using Newtonsoft.Json; using Checkout.Common; -namespace Checkout.Agentic.Requests +namespace Checkout.Agentic.Entities { /// /// Payment Source for Agentic Enrollment diff --git a/src/CheckoutSdk/Agentic/Requests/Entities/PurchaseThreshold.cs b/src/CheckoutSdk/Agentic/Entities/PurchaseThreshold.cs similarity index 91% rename from src/CheckoutSdk/Agentic/Requests/Entities/PurchaseThreshold.cs rename to src/CheckoutSdk/Agentic/Entities/PurchaseThreshold.cs index 6de8bd6e..296ee36b 100644 --- a/src/CheckoutSdk/Agentic/Requests/Entities/PurchaseThreshold.cs +++ b/src/CheckoutSdk/Agentic/Entities/PurchaseThreshold.cs @@ -1,6 +1,6 @@ using Checkout.Common; -namespace Checkout.Agentic.Requests +namespace Checkout.Agentic.Entities { /// /// Purchase threshold configuration diff --git a/src/CheckoutSdk/Agentic/Requests/Entities/TransactionAmount.cs b/src/CheckoutSdk/Agentic/Entities/TransactionAmount.cs similarity index 92% rename from src/CheckoutSdk/Agentic/Requests/Entities/TransactionAmount.cs rename to src/CheckoutSdk/Agentic/Entities/TransactionAmount.cs index b23f539a..53d9214e 100644 --- a/src/CheckoutSdk/Agentic/Requests/Entities/TransactionAmount.cs +++ b/src/CheckoutSdk/Agentic/Entities/TransactionAmount.cs @@ -1,6 +1,6 @@ using Checkout.Common; -namespace Checkout.Agentic.Requests +namespace Checkout.Agentic.Entities { /// /// Represents transaction amount information for agentic commerce diff --git a/src/CheckoutSdk/Agentic/Requests/Entities/TransactionData.cs b/src/CheckoutSdk/Agentic/Entities/TransactionData.cs similarity index 95% rename from src/CheckoutSdk/Agentic/Requests/Entities/TransactionData.cs rename to src/CheckoutSdk/Agentic/Entities/TransactionData.cs index ae422e97..05fc43cc 100644 --- a/src/CheckoutSdk/Agentic/Requests/Entities/TransactionData.cs +++ b/src/CheckoutSdk/Agentic/Entities/TransactionData.cs @@ -1,6 +1,6 @@ using Checkout.Common; -namespace Checkout.Agentic.Requests +namespace Checkout.Agentic.Entities { /// /// Represents transaction data for agentic commerce diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs b/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs index f21869b0..2fd61bea 100644 --- a/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs +++ b/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs @@ -1,3 +1,5 @@ +using Checkout.Agentic.Entities; + namespace Checkout.Agentic.Requests { /// diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCreateRequest.cs b/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCreateRequest.cs index d1462e8d..968e6714 100644 --- a/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCreateRequest.cs +++ b/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCreateRequest.cs @@ -1,3 +1,5 @@ +using Checkout.Agentic.Entities; + namespace Checkout.Agentic.Requests { /// diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCredentialsCreateRequest.cs b/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCredentialsCreateRequest.cs index 14adb90f..50125e87 100644 --- a/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCredentialsCreateRequest.cs +++ b/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCredentialsCreateRequest.cs @@ -1,3 +1,5 @@ +using Checkout.Agentic.Entities; + namespace Checkout.Agentic.Requests { /// diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentUpdateRequest.cs b/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentUpdateRequest.cs index 3b7cf3f3..d499130c 100644 --- a/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentUpdateRequest.cs +++ b/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentUpdateRequest.cs @@ -1,3 +1,5 @@ +using Checkout.Agentic.Entities; + namespace Checkout.Agentic.Requests { /// diff --git a/src/CheckoutSdk/Agentic/Responses/AgenticPurchaseIntentResponse.cs b/src/CheckoutSdk/Agentic/Responses/AgenticPurchaseIntentResponse.cs index ed99a9c3..b18b6229 100644 --- a/src/CheckoutSdk/Agentic/Responses/AgenticPurchaseIntentResponse.cs +++ b/src/CheckoutSdk/Agentic/Responses/AgenticPurchaseIntentResponse.cs @@ -1,6 +1,5 @@ -using Checkout.Agentic.Requests; using Checkout.Common; -using Newtonsoft.Json; +using Checkout.Agentic.Entities; namespace Checkout.Agentic.Responses { diff --git a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs index f4cc4368..a914b3c9 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs +++ b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs @@ -4,6 +4,8 @@ using Moq; using Shouldly; using Xunit; + +using Checkout.Agentic.Entities; using Checkout.Agentic.Requests; using Checkout.Agentic.Responses; using Checkout.Common; diff --git a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs index e3dfc8c2..61534f9e 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs +++ b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs @@ -3,7 +3,8 @@ using System.Threading.Tasks; using Shouldly; using Xunit; -using Checkout.Agentic; + +using Checkout.Agentic.Entities; using Checkout.Agentic.Requests; using Checkout.Common; From a6d69570b2a636b34eee8bc04b3da1156912bc02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armando=20Rodr=C3=ADguez?= <127134616+armando-rodriguez-cko@users.noreply.github.com> Date: Fri, 21 Nov 2025 12:59:02 +0100 Subject: [PATCH 13/18] refactor names and clases --- .../Agentic/Entities/AgenticCustomer.cs | 26 -- .../Agentic/Entities/PaymentSource.cs | 36 --- .../Agentic/Entities/PurchaseThreshold.cs | 20 -- .../Agentic/Entities/TransactionAmount.cs | 20 -- src/CheckoutSdk/Agentic/IAgenticClient.cs | 52 ---- .../Agentic/Requests/AgenticEnrollRequest.cs | 25 -- .../AgenticPurchaseIntentCreateRequest.cs | 20 -- .../AgenticPurchaseIntentUpdateRequest.cs | 20 -- .../Responses/AgenticEnrollResponse.cs | 26 -- .../AgenticClient.cs | 68 +++--- .../Common/AgenticMandateBase.cs} | 17 +- .../Common/PurchaseThreshold.cs | 25 ++ .../AgenticCommerce/IAgenticClient.cs | 58 +++++ .../Requests/Common/AgenticMandateRequest.cs | 11 + .../Requests/EnrollACardRequest.cs | 23 ++ .../Requests/Entities/AgenticCustomer.cs | 29 +++ .../Requests/Entities/AgenticDevice.cs} | 20 +- .../Requests/Entities/AgenticSource.cs | 39 +++ .../Requests/Entities/TransactionAmount.cs | 25 ++ .../Requests}/Entities/TransactionData.cs | 27 +- .../Requests/PurchaseIntentCreateRequest.cs | 20 ++ ...PurchaseIntentCredentialsCreateRequest.cs} | 8 +- .../Requests/PurchaseIntentUpdateRequest.cs | 23 ++ .../Common/AgenticMandateResponse.cs | 36 +++ .../Common/PurchaseIntentStatusType.cs | 25 ++ .../Responses/EnrollACardResponse.cs | 29 +++ .../Responses/PurchaseIntentResponse.cs} | 24 +- .../Agentic/AgenticClientTest.cs | 220 ++++++++--------- .../Agentic/AgenticIntegrationTest.cs | 230 ++++++++---------- 29 files changed, 652 insertions(+), 550 deletions(-) delete mode 100644 src/CheckoutSdk/Agentic/Entities/AgenticCustomer.cs delete mode 100644 src/CheckoutSdk/Agentic/Entities/PaymentSource.cs delete mode 100644 src/CheckoutSdk/Agentic/Entities/PurchaseThreshold.cs delete mode 100644 src/CheckoutSdk/Agentic/Entities/TransactionAmount.cs delete mode 100644 src/CheckoutSdk/Agentic/IAgenticClient.cs delete mode 100644 src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs delete mode 100644 src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCreateRequest.cs delete mode 100644 src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentUpdateRequest.cs delete mode 100644 src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs rename src/CheckoutSdk/{Agentic => AgenticCommerce}/AgenticClient.cs (58%) rename src/CheckoutSdk/{Agentic/Entities/Mandate.cs => AgenticCommerce/Common/AgenticMandateBase.cs} (56%) create mode 100644 src/CheckoutSdk/AgenticCommerce/Common/PurchaseThreshold.cs create mode 100644 src/CheckoutSdk/AgenticCommerce/IAgenticClient.cs create mode 100644 src/CheckoutSdk/AgenticCommerce/Requests/Common/AgenticMandateRequest.cs create mode 100644 src/CheckoutSdk/AgenticCommerce/Requests/EnrollACardRequest.cs create mode 100644 src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticCustomer.cs rename src/CheckoutSdk/{Agentic/Entities/DeviceInfo.cs => AgenticCommerce/Requests/Entities/AgenticDevice.cs} (56%) create mode 100644 src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticSource.cs create mode 100644 src/CheckoutSdk/AgenticCommerce/Requests/Entities/TransactionAmount.cs rename src/CheckoutSdk/{Agentic => AgenticCommerce/Requests}/Entities/TransactionData.cs (53%) create mode 100644 src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentCreateRequest.cs rename src/CheckoutSdk/{Agentic/Requests/AgenticPurchaseIntentCredentialsCreateRequest.cs => AgenticCommerce/Requests/PurchaseIntentCredentialsCreateRequest.cs} (55%) create mode 100644 src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentUpdateRequest.cs create mode 100644 src/CheckoutSdk/AgenticCommerce/Responses/Common/AgenticMandateResponse.cs create mode 100644 src/CheckoutSdk/AgenticCommerce/Responses/Common/PurchaseIntentStatusType.cs create mode 100644 src/CheckoutSdk/AgenticCommerce/Responses/EnrollACardResponse.cs rename src/CheckoutSdk/{Agentic/Responses/AgenticPurchaseIntentResponse.cs => AgenticCommerce/Responses/PurchaseIntentResponse.cs} (62%) diff --git a/src/CheckoutSdk/Agentic/Entities/AgenticCustomer.cs b/src/CheckoutSdk/Agentic/Entities/AgenticCustomer.cs deleted file mode 100644 index 5db66da8..00000000 --- a/src/CheckoutSdk/Agentic/Entities/AgenticCustomer.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Newtonsoft.Json; -using Checkout.Common; - -namespace Checkout.Agentic.Entities -{ - /// - /// Customer information for agentic enrollment - /// - public class AgenticCustomer - { - /// - /// Customer email address - /// - public string Email { get; set; } - - /// - /// Customer country code (ISO 3166-1 alpha-2) - /// - public CountryCode? CountryCode { get; set; } - - /// - /// Customer language code (ISO 639-1) - /// - public string LanguageCode { get; set; } - } -} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Entities/PaymentSource.cs b/src/CheckoutSdk/Agentic/Entities/PaymentSource.cs deleted file mode 100644 index 1f972500..00000000 --- a/src/CheckoutSdk/Agentic/Entities/PaymentSource.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Newtonsoft.Json; -using Checkout.Common; - -namespace Checkout.Agentic.Entities -{ - /// - /// Payment Source for Agentic Enrollment - /// - public class PaymentSource - { - /// - /// Card number - /// - public string Number { get; set; } - - /// - /// Card expiry month (1-12) - /// - public int ExpiryMonth { get; set; } - - /// - /// Card expiry year (e.g., 2025) - /// - public int ExpiryYear { get; set; } - - /// - /// Card verification value (CVV) - /// - public string Cvv { get; set; } - - /// - /// Payment source type - /// - public PaymentSourceType? Type { get; set; } - } -} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Entities/PurchaseThreshold.cs b/src/CheckoutSdk/Agentic/Entities/PurchaseThreshold.cs deleted file mode 100644 index 296ee36b..00000000 --- a/src/CheckoutSdk/Agentic/Entities/PurchaseThreshold.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Checkout.Common; - -namespace Checkout.Agentic.Entities -{ - /// - /// Purchase threshold configuration - /// - public class PurchaseThreshold - { - /// - /// The threshold amount - /// - public int Amount { get; set; } - - /// - /// The currency for the threshold - /// - public Currency? CurrencyCode { get; set; } - } -} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Entities/TransactionAmount.cs b/src/CheckoutSdk/Agentic/Entities/TransactionAmount.cs deleted file mode 100644 index 53d9214e..00000000 --- a/src/CheckoutSdk/Agentic/Entities/TransactionAmount.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Checkout.Common; - -namespace Checkout.Agentic.Entities -{ - /// - /// Represents transaction amount information for agentic commerce - /// - public class TransactionAmount - { - /// - /// Transaction amount - /// - public int Amount { get; set; } - - /// - /// Currency for the transaction - /// - public Currency? CurrencyCode { get; set; } - } -} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/IAgenticClient.cs b/src/CheckoutSdk/Agentic/IAgenticClient.cs deleted file mode 100644 index 04978c34..00000000 --- a/src/CheckoutSdk/Agentic/IAgenticClient.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Checkout.Agentic.Requests; -using Checkout.Agentic.Responses; - -namespace Checkout.Agentic -{ - /// - /// Agentic API Client - /// - public interface IAgenticClient - { - /// - /// Enroll in agentic services - /// Enrolls a merchant or entity in agentic services - /// - Task Enroll( - AgenticEnrollRequest agenticEnrollRequest, - CancellationToken cancellationToken = default); - - /// - /// Create a purchase intent - /// Creates a new purchase intent for agentic commerce - /// - Task CreatePurchaseIntent( - AgenticPurchaseIntentCreateRequest agenticPurchaseIntentCreateRequest, - CancellationToken cancellationToken = default); - - /// - /// Create a purchase intent credentials - /// Create credentials for an agentic commerce purchase intent. - /// - Task CreatePurchaseIntentCredentials(string id, - AgenticPurchaseIntentCredentialsCreateRequest agenticPurchaseIntentCredentialsCreateRequest, - CancellationToken cancellationToken = default); - - /// - /// Update a purchase intent - /// Updates a purchase intent for agentic commerce - /// - Task UpdatePurchaseIntent(string id, - AgenticPurchaseIntentUpdateRequest agenticPurchaseIntentUpdateRequest, - CancellationToken cancellationToken = default); - - /// - /// Delete a purchase intent - /// Deletes a purchase intent for agentic commerce - /// - Task DeletePurchaseIntent(string id, - CancellationToken cancellationToken = default); - } -} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs b/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs deleted file mode 100644 index 2fd61bea..00000000 --- a/src/CheckoutSdk/Agentic/Requests/AgenticEnrollRequest.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Checkout.Agentic.Entities; - -namespace Checkout.Agentic.Requests -{ - /// - /// Agentic Enroll Request - /// - public class AgenticEnrollRequest - { - /// - /// Payment source information - /// - public PaymentSource Source { get; set; } - - /// - /// Device information for fraud detection and analysis - /// - public DeviceInfo Device { get; set; } - - /// - /// Customer information - /// - public AgenticCustomer Customer { get; set; } - } -} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCreateRequest.cs b/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCreateRequest.cs deleted file mode 100644 index 968e6714..00000000 --- a/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCreateRequest.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Checkout.Agentic.Entities; - -namespace Checkout.Agentic.Requests -{ - /// - /// Request to create a purchase intent for agentic commerce - /// - public class AgenticPurchaseIntentCreateRequest : AgenticPurchaseIntentUpdateRequest - { - /// - /// The network token ID - /// - public string NetworkTokenId { get; set; } - - /// - /// The device information - /// - public DeviceInfo Device { get; set; } - } -} diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentUpdateRequest.cs b/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentUpdateRequest.cs deleted file mode 100644 index d499130c..00000000 --- a/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentUpdateRequest.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Checkout.Agentic.Entities; - -namespace Checkout.Agentic.Requests -{ - /// - /// Request to update a purchase intent for agentic commerce - /// - public class AgenticPurchaseIntentUpdateRequest - { - /// - /// Customer prompt describing the purchase intent - /// - public string CustomerPrompt { get; set; } - - /// - /// List of mandates for the purchase intent - /// - public Mandate[] Mandates { get; set; } - } -} diff --git a/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs b/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs deleted file mode 100644 index 3114b9dc..00000000 --- a/src/CheckoutSdk/Agentic/Responses/AgenticEnrollResponse.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Checkout.Common; - -namespace Checkout.Agentic.Responses -{ - /// - /// Agentic Enroll Response - /// - public class AgenticEnrollResponse : Resource - { - /// - /// The unique token identifier for the enrolled agentic service - /// - public string TokenId { get; set; } - - /// - /// Current status of the enrollment (e.g., "enrolled") - /// - public string Status { get; set; } - - /// - /// The timestamp when the enrollment was created - /// - public DateTime? CreatedAt { get; set; } - } -} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/AgenticClient.cs b/src/CheckoutSdk/AgenticCommerce/AgenticClient.cs similarity index 58% rename from src/CheckoutSdk/Agentic/AgenticClient.cs rename to src/CheckoutSdk/AgenticCommerce/AgenticClient.cs index 1206bf36..86873fe0 100644 --- a/src/CheckoutSdk/Agentic/AgenticClient.cs +++ b/src/CheckoutSdk/AgenticCommerce/AgenticClient.cs @@ -1,12 +1,12 @@ using System.Threading; using System.Threading.Tasks; -using Checkout.Agentic.Requests; -using Checkout.Agentic.Responses; +using Checkout.AgenticCommerce.Requests; +using Checkout.AgenticCommerce.Responses; namespace Checkout.Agentic { /// - /// Agentic API Client + /// Agentic Commerce API Client /// public class AgenticClient : AbstractClient, IAgenticClient { @@ -19,72 +19,78 @@ public AgenticClient(IApiClient apiClient, CheckoutConfiguration configuration) base(apiClient, configuration, SdkAuthorizationType.OAuth) { } + + // Agentic Commerce enrollment + // ---------------------------------------------------------------- /// - /// Enroll in agentic services - /// Enrolls a card for use with agentic commerce. + /// Enroll a card for use with agentic commerce + /// [BETA] /// - public Task Enroll( - AgenticEnrollRequest agenticEnrollRequest, + public Task Enroll( + EnrollACardRequest enrollACardRequest, CancellationToken cancellationToken = default) { - CheckoutUtils.ValidateParams("agenticEnrollRequest", agenticEnrollRequest); - return ApiClient.Post( + CheckoutUtils.ValidateParams("agenticEnrollRequest", enrollACardRequest); + return ApiClient.Post( BuildPath(AgenticPath, EnrollPath), SdkAuthorization(), - agenticEnrollRequest, + enrollACardRequest, cancellationToken ); } + + // Purchase intents + // ---------------------------------------------------------------- /// - /// Create a purchase intent - /// Creates a new purchase intent for agentic commerce + /// Create an agentic commerce purchase intent + /// [BETA] /// - public Task CreatePurchaseIntent( - AgenticPurchaseIntentCreateRequest agenticPurchaseIntentCreateRequest, + public Task CreatePurchaseIntent( + PurchaseIntentCreateRequest purchaseIntentCreateRequest, CancellationToken cancellationToken = default) { - CheckoutUtils.ValidateParams("agenticPurchaseIntentCreateRequest", agenticPurchaseIntentCreateRequest); - return ApiClient.Post( + CheckoutUtils.ValidateParams("agenticPurchaseIntentCreateRequest", purchaseIntentCreateRequest); + return ApiClient.Post( BuildPath(AgenticPath, PurchaseIntentPath), SdkAuthorization(), - agenticPurchaseIntentCreateRequest, + purchaseIntentCreateRequest, cancellationToken ); } /// - /// Create a purchase intent - /// Creates a new purchase intent for agentic commerce + /// Create credentials for an agentic commerce purchase intent. + /// [BETA] /// - public Task CreatePurchaseIntentCredentials(string id, - AgenticPurchaseIntentCredentialsCreateRequest agenticPurchaseIntentCredentialsCreateRequest, + public Task CreatePurchaseIntentCredentials(string id, + PurchaseIntentCredentialsCreateRequest purchaseIntentCredentialsCreateRequest, CancellationToken cancellationToken = default) { CheckoutUtils.ValidateParams("id", id,"agenticPurchaseIntentCredentialsCreateRequest", - agenticPurchaseIntentCredentialsCreateRequest); - return ApiClient.Post( + purchaseIntentCredentialsCreateRequest); + return ApiClient.Post( BuildPath(AgenticPath, PurchaseIntentPath, id, CredentialsPath), SdkAuthorization(), - agenticPurchaseIntentCredentialsCreateRequest, + purchaseIntentCredentialsCreateRequest, cancellationToken ); } /// - /// Update a purchase intent - /// Updates a new purchase intent for agentic commerce + /// Update an agentic commerce purchase intent + /// [BETA] /// - public Task UpdatePurchaseIntent(string id, - AgenticPurchaseIntentUpdateRequest agenticPurchaseIntentUpdateRequest, + public Task UpdatePurchaseIntent(string id, + PurchaseIntentUpdateRequest purchaseIntentUpdateRequest, CancellationToken cancellationToken = default) { - CheckoutUtils.ValidateParams("id", id, "agenticPurchaseIntentUpdateRequest", agenticPurchaseIntentUpdateRequest); - return ApiClient.Put( + CheckoutUtils.ValidateParams("id", id, "agenticPurchaseIntentUpdateRequest", purchaseIntentUpdateRequest); + return ApiClient.Put( BuildPath(AgenticPath, PurchaseIntentPath, id), SdkAuthorization(), - agenticPurchaseIntentUpdateRequest, + purchaseIntentUpdateRequest, cancellationToken ); } diff --git a/src/CheckoutSdk/Agentic/Entities/Mandate.cs b/src/CheckoutSdk/AgenticCommerce/Common/AgenticMandateBase.cs similarity index 56% rename from src/CheckoutSdk/Agentic/Entities/Mandate.cs rename to src/CheckoutSdk/AgenticCommerce/Common/AgenticMandateBase.cs index 516d1ccb..0ad6e7a7 100644 --- a/src/CheckoutSdk/Agentic/Entities/Mandate.cs +++ b/src/CheckoutSdk/AgenticCommerce/Common/AgenticMandateBase.cs @@ -1,29 +1,28 @@ using System; -namespace Checkout.Agentic.Entities +namespace Checkout.AgenticCommerce.Common { /// /// Mandate configuration for purchase intents /// - public class Mandate + public class AgenticMandateBase { - /// - /// The mandate ID - /// - public string Id { get; set; } - /// /// Purchase threshold configuration /// public PurchaseThreshold PurchaseThreshold { get; set; } /// - /// Description of the mandate + /// A brief description of the purchase intent + /// <= 255 characters + /// [Required] /// public string Description { get; set; } /// - /// Expiration date of the mandate in ISO 8601 format + /// The date and time when the purchase intent expires, in ISO 8601 format. + /// This value must be set to a date and time in the future + /// [Required] /// public DateTime? ExpirationDate { get; set; } } diff --git a/src/CheckoutSdk/AgenticCommerce/Common/PurchaseThreshold.cs b/src/CheckoutSdk/AgenticCommerce/Common/PurchaseThreshold.cs new file mode 100644 index 00000000..02666a7f --- /dev/null +++ b/src/CheckoutSdk/AgenticCommerce/Common/PurchaseThreshold.cs @@ -0,0 +1,25 @@ +using Checkout.Common; + +namespace Checkout.AgenticCommerce.Common +{ + /// + /// Purchase threshold configuration + /// + public class PurchaseThreshold + { + /// + /// The maximum amount for the purchase + /// Format the amount (https://www.checkout.com/docs/payments/accept-payments/format-the-amount-value) + /// according to the currency_code + /// [Required] + /// + public int Amount { get; set; } + + /// + /// The currency to use for the purchase, as an ISO 4217 currency code + /// (https://www.checkout.com/docs/resources/codes/currency-codes) + /// [Required] + /// + public Currency? CurrencyCode { get; set; } + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/AgenticCommerce/IAgenticClient.cs b/src/CheckoutSdk/AgenticCommerce/IAgenticClient.cs new file mode 100644 index 00000000..2850d7cb --- /dev/null +++ b/src/CheckoutSdk/AgenticCommerce/IAgenticClient.cs @@ -0,0 +1,58 @@ +using System.Threading; +using System.Threading.Tasks; +using Checkout.AgenticCommerce.Requests; +using Checkout.AgenticCommerce.Responses; + +namespace Checkout.Agentic +{ + /// + /// Agentic Commerce API Client + /// + public interface IAgenticClient + { + // Agentic Commerce enrollment + // ---------------------------------------------------------------- + + /// + /// Enroll a card for use with agentic commerce + /// [BETA] + /// + Task Enroll( + EnrollACardRequest enrollACardRequest, + CancellationToken cancellationToken = default); + + // Purchase intents + // ---------------------------------------------------------------- + + /// + /// Create an agentic commerce purchase intent + /// [BETA] + /// + Task CreatePurchaseIntent( + PurchaseIntentCreateRequest purchaseIntentCreateRequest, + CancellationToken cancellationToken = default); + + /// + /// Create credentials for an agentic commerce purchase intent. + /// [BETA] + /// + Task CreatePurchaseIntentCredentials(string id, + PurchaseIntentCredentialsCreateRequest purchaseIntentCredentialsCreateRequest, + CancellationToken cancellationToken = default); + + /// + /// Update an agentic commerce purchase intent + /// [BETA] + /// + Task UpdatePurchaseIntent(string id, + PurchaseIntentUpdateRequest purchaseIntentUpdateRequest, + CancellationToken cancellationToken = default); + + /// + /// Cancel an agentic commerce purchase intent + /// [BETA] + /// + Task DeletePurchaseIntent(string id, + CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/AgenticCommerce/Requests/Common/AgenticMandateRequest.cs b/src/CheckoutSdk/AgenticCommerce/Requests/Common/AgenticMandateRequest.cs new file mode 100644 index 00000000..37477402 --- /dev/null +++ b/src/CheckoutSdk/AgenticCommerce/Requests/Common/AgenticMandateRequest.cs @@ -0,0 +1,11 @@ +using Checkout.AgenticCommerce.Common; + +namespace Checkout.AgenticCommerce.Requests.Common +{ + /// + /// Mandate configuration for purchase intents + /// + public class AgenticMandateRequest : AgenticMandateBase + { + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/AgenticCommerce/Requests/EnrollACardRequest.cs b/src/CheckoutSdk/AgenticCommerce/Requests/EnrollACardRequest.cs new file mode 100644 index 00000000..f825fdf0 --- /dev/null +++ b/src/CheckoutSdk/AgenticCommerce/Requests/EnrollACardRequest.cs @@ -0,0 +1,23 @@ +namespace Checkout.AgenticCommerce.Requests +{ + /// + /// Enroll a card for use with agentic commerce + /// + public class EnrollACardRequest + { + /// + /// The payment source to enroll + /// + public AgenticSource Source { get; set; } + + /// + /// The user's device + /// + public AgenticDevice Device { get; set; } + + /// + /// The customer's details + /// + public AgenticCustomer Customer { get; set; } + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticCustomer.cs b/src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticCustomer.cs new file mode 100644 index 00000000..937a24da --- /dev/null +++ b/src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticCustomer.cs @@ -0,0 +1,29 @@ +using Checkout.Common; + +namespace Checkout.AgenticCommerce.Requests +{ + /// + /// The customer's details + /// + public class AgenticCustomer + { + /// + /// The customer's email address + /// [Required] + /// + public string Email { get; set; } + + /// + /// The customer's country, as a two-letter ISO 3166 country code + /// (https://www.checkout.com/docs/resources/codes/country-codes) + /// [Required] + /// + public CountryCode? CountryCode { get; set; } + + /// + /// The customer's language, as a two-letter ISO 639 language code + /// [Required] + /// + public string LanguageCode { get; set; } + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Entities/DeviceInfo.cs b/src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticDevice.cs similarity index 56% rename from src/CheckoutSdk/Agentic/Entities/DeviceInfo.cs rename to src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticDevice.cs index d490e0b6..f54037e8 100644 --- a/src/CheckoutSdk/Agentic/Entities/DeviceInfo.cs +++ b/src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticDevice.cs @@ -1,29 +1,31 @@ -using Newtonsoft.Json; - -namespace Checkout.Agentic.Entities +namespace Checkout.AgenticCommerce.Requests { /// - /// Device Information + /// The user's device /// - public class DeviceInfo + public class AgenticDevice { /// - /// IP address of the device + /// The device's IP address + /// [Required] /// public string IpAddress { get; set; } /// - /// User agent string from the browser + /// The device's user agent + /// [Required] /// public string UserAgent { get; set; } /// - /// Device brand (e.g., "apple", "samsung") + /// The device brand + /// [Optional] /// public string DeviceBrand { get; set; } /// - /// Device type (e.g., "tablet", "mobile", "desktop") + /// The device type + /// [Optional] /// public string DeviceType { get; set; } } diff --git a/src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticSource.cs b/src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticSource.cs new file mode 100644 index 00000000..14dbc487 --- /dev/null +++ b/src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticSource.cs @@ -0,0 +1,39 @@ +namespace Checkout.AgenticCommerce.Requests +{ + /// + /// The payment source to enroll + /// + public class AgenticSource + { + /// + /// The full card number, without separators + /// [Required] + /// + public string Number { get; set; } + + /// + /// Card expiry month + /// [ 13 .. 19 ] characters + /// [Required] + /// + public int ExpiryMonth { get; set; } + + /// + /// Card expiry year + /// [Required] + /// + public int ExpiryYear { get; set; } + + /// + /// Payment source type + /// [Required] + /// + public string Type { get; set; } + + /// + /// The card's 3 or 4 digit card verification value (CVV) or security code + /// [Optional] + /// + public string Cvv { get; set; } + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/AgenticCommerce/Requests/Entities/TransactionAmount.cs b/src/CheckoutSdk/AgenticCommerce/Requests/Entities/TransactionAmount.cs new file mode 100644 index 00000000..ad35469c --- /dev/null +++ b/src/CheckoutSdk/AgenticCommerce/Requests/Entities/TransactionAmount.cs @@ -0,0 +1,25 @@ +using Checkout.Common; + +namespace Checkout.AgenticCommerce.Requests +{ + /// + /// Transaction amount information + /// + public class TransactionAmount + { + /// + /// The transaction amount + /// Format the amount (https://www.checkout.com/docs/payments/accept-payments/format-the-amount-value) + /// according to the currency_code + /// [Required] + /// + public int Amount { get; set; } + + /// + /// The transaction currency, as an ISO 4217 currency code + /// (https://www.checkout.com/docs/resources/codes/currency-codes) + /// [Required] + /// + public Currency? CurrencyCode { get; set; } + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Entities/TransactionData.cs b/src/CheckoutSdk/AgenticCommerce/Requests/Entities/TransactionData.cs similarity index 53% rename from src/CheckoutSdk/Agentic/Entities/TransactionData.cs rename to src/CheckoutSdk/AgenticCommerce/Requests/Entities/TransactionData.cs index 05fc43cc..0d60ac4c 100644 --- a/src/CheckoutSdk/Agentic/Entities/TransactionData.cs +++ b/src/CheckoutSdk/AgenticCommerce/Requests/Entities/TransactionData.cs @@ -1,35 +1,42 @@ using Checkout.Common; -namespace Checkout.Agentic.Entities +namespace Checkout.AgenticCommerce.Requests { /// - /// Represents transaction data for agentic commerce + /// List of transaction data for the purchase /// public class TransactionData { /// - /// Merchant country code (e.g., US, GB) + /// The merchant's country, as a two-letter ISO 3166 country code + /// (https://www.checkout.com/docs/resources/codes/country-codes) + /// [Required] /// public CountryCode? MerchantCountryCode { get; set; } /// - /// Merchant name + /// The merchant's name + /// [Required] /// public string MerchantName { get; set; } /// - /// Merchant category code + /// The merchant's category code + /// (https://www.checkout.com/docs/developer-resources/codes/merchant-category-codes) + /// [Required] /// public string MerchantCategoryCode { get; set; } - /// - /// Merchant website URL - /// - public string MerchantUrl { get; set; } - /// /// Transaction amount information + /// [Required] /// public TransactionAmount TransactionAmount { get; set; } + + /// + /// The merchant's website URL + /// [Optional] + /// + public string MerchantUrl { get; set; } } } \ No newline at end of file diff --git a/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentCreateRequest.cs b/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentCreateRequest.cs new file mode 100644 index 00000000..71667ef2 --- /dev/null +++ b/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentCreateRequest.cs @@ -0,0 +1,20 @@ +namespace Checkout.AgenticCommerce.Requests +{ + /// + /// Create an agentic commerce purchase intent + /// + public class PurchaseIntentCreateRequest : PurchaseIntentUpdateRequest + { + /// + /// The unique identifier for the network token + /// [Required] + /// + public string NetworkTokenId { get; set; } + + /// + /// The user's device + /// [Required] + /// + public AgenticDevice Device { get; set; } + } +} diff --git a/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCredentialsCreateRequest.cs b/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentCredentialsCreateRequest.cs similarity index 55% rename from src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCredentialsCreateRequest.cs rename to src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentCredentialsCreateRequest.cs index 50125e87..a1d3697d 100644 --- a/src/CheckoutSdk/Agentic/Requests/AgenticPurchaseIntentCredentialsCreateRequest.cs +++ b/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentCredentialsCreateRequest.cs @@ -1,15 +1,15 @@ -using Checkout.Agentic.Entities; +using System.Collections.Generic; -namespace Checkout.Agentic.Requests +namespace Checkout.AgenticCommerce.Requests { /// /// Request to create a purchase intent credentials for agentic commerce /// - public class AgenticPurchaseIntentCredentialsCreateRequest + public class PurchaseIntentCredentialsCreateRequest { /// /// Array of transaction data for the purchase intent credentials /// - public TransactionData[] TransactionData { get; set; } + public IList TransactionData { get; set; } } } diff --git a/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentUpdateRequest.cs b/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentUpdateRequest.cs new file mode 100644 index 00000000..c0147f52 --- /dev/null +++ b/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentUpdateRequest.cs @@ -0,0 +1,23 @@ +using Checkout.AgenticCommerce.Requests.Common; +using System.Collections.Generic; + +namespace Checkout.AgenticCommerce.Requests +{ + /// + /// Create an agentic commerce purchase intent + /// + public class PurchaseIntentUpdateRequest + { + /// + /// A list of mandates associated with the purchase intent + /// + public IList Mandates { get; set; } + + /// + /// A prompt or message for the customer. You can display this during the purchase process to provide additional + /// context or instructions + /// <= 4098 characters + /// + public string CustomerPrompt { get; set; } + } +} diff --git a/src/CheckoutSdk/AgenticCommerce/Responses/Common/AgenticMandateResponse.cs b/src/CheckoutSdk/AgenticCommerce/Responses/Common/AgenticMandateResponse.cs new file mode 100644 index 00000000..c907ff69 --- /dev/null +++ b/src/CheckoutSdk/AgenticCommerce/Responses/Common/AgenticMandateResponse.cs @@ -0,0 +1,36 @@ +using Checkout.AgenticCommerce.Common; +using System; + +namespace Checkout.AgenticCommerce.Responses.Common +{ + /// + /// Mandate configuration for purchase intents + /// + public class AgenticMandateResponse: AgenticMandateBase + { + /// + /// The unique identifier for the mandate + /// [Required] + /// + public string Id { get; set; } + + /// + /// Purchase threshold configuration + /// + public PurchaseThreshold PurchaseThreshold { get; set; } + + /// + /// A brief description of the purchase intent + /// <= 255 characters + /// [Required] + /// + public string Description { get; set; } + + /// + /// The date and time when the purchase intent expires, in ISO 8601 format. + /// This value must be set to a date and time in the future + /// [Required] + /// + public DateTime? ExpirationDate { get; set; } + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/AgenticCommerce/Responses/Common/PurchaseIntentStatusType.cs b/src/CheckoutSdk/AgenticCommerce/Responses/Common/PurchaseIntentStatusType.cs new file mode 100644 index 00000000..2d502bfc --- /dev/null +++ b/src/CheckoutSdk/AgenticCommerce/Responses/Common/PurchaseIntentStatusType.cs @@ -0,0 +1,25 @@ +using System.Runtime.Serialization; + +namespace Checkout.AgenticCommerce.Responses.Common +{ + public enum PurchaseIntentStatusType + { + [EnumMember(Value = "active")] + Active, + + [EnumMember(Value = "created")] + Created, + + [EnumMember(Value = "cancelled")] + Cancelled, + + [EnumMember(Value = "expired")] + Expired, + + [EnumMember(Value = "declined")] + Declined, + + [EnumMember(Value = "completed")] + Completed, + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/AgenticCommerce/Responses/EnrollACardResponse.cs b/src/CheckoutSdk/AgenticCommerce/Responses/EnrollACardResponse.cs new file mode 100644 index 00000000..579db638 --- /dev/null +++ b/src/CheckoutSdk/AgenticCommerce/Responses/EnrollACardResponse.cs @@ -0,0 +1,29 @@ +using System; + +namespace Checkout.AgenticCommerce.Responses +{ + /// + /// Card enrolled successfully + /// + public class EnrollACardResponse : HttpMetadata + { + /// + /// The unique identifier for the provisioned token + /// [Required] + /// + public string TokenId { get; set; } + + /// + /// The status of the enrollment + /// Value: "enrolled" + /// [Required] + /// + public string Status { get; set; } + + /// + /// The date and time the enrollment was created, in ISO 8601 format + /// [Optional] + /// + public DateTime? CreatedAt { get; set; } + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/Agentic/Responses/AgenticPurchaseIntentResponse.cs b/src/CheckoutSdk/AgenticCommerce/Responses/PurchaseIntentResponse.cs similarity index 62% rename from src/CheckoutSdk/Agentic/Responses/AgenticPurchaseIntentResponse.cs rename to src/CheckoutSdk/AgenticCommerce/Responses/PurchaseIntentResponse.cs index b18b6229..09a59186 100644 --- a/src/CheckoutSdk/Agentic/Responses/AgenticPurchaseIntentResponse.cs +++ b/src/CheckoutSdk/AgenticCommerce/Responses/PurchaseIntentResponse.cs @@ -1,27 +1,29 @@ +using Checkout.AgenticCommerce.Requests; +using Checkout.AgenticCommerce.Responses.Common; using Checkout.Common; -using Checkout.Agentic.Entities; +using System.Collections.Generic; -namespace Checkout.Agentic.Responses +namespace Checkout.AgenticCommerce.Responses { /// /// Response from purchase intent creation /// - public class AgenticPurchaseIntentResponse : Resource + public class PurchaseIntentResponse : Resource { /// - /// The purchase intent ID + /// The unique identifier for the purchase intent /// public string Id { get; set; } - + /// - /// The scheme of the purchase intent + /// The current status of the purchase intent /// - public string Scheme { get; set; } + public PurchaseIntentStatusType? Status { get; set; } /// - /// The status of the purchase intent + /// The scheme of the purchase intent /// - public string Status { get; set; } + public string Scheme { get; set; } /// /// The tokenid of the purchase intent @@ -31,7 +33,7 @@ public class AgenticPurchaseIntentResponse : Resource /// /// The device information of the purchase intent /// - public DeviceInfo DeviceData { get; set; } + public AgenticDevice DeviceData { get; set; } /// /// The customer prompt of the purchase intent @@ -41,6 +43,6 @@ public class AgenticPurchaseIntentResponse : Resource /// /// List of mandates for the purchase intent /// - public Mandate[] Mandates { get; set; } + public IList Mandates { get; set; } } } \ No newline at end of file diff --git a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs index a914b3c9..d2aaaebc 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs +++ b/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs @@ -1,14 +1,16 @@ +using Checkout.AgenticCommerce.Common; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Moq; using Shouldly; using Xunit; - -using Checkout.Agentic.Entities; -using Checkout.Agentic.Requests; -using Checkout.Agentic.Responses; +using Checkout.AgenticCommerce.Requests; +using Checkout.AgenticCommerce.Requests.Common; +using Checkout.AgenticCommerce.Responses; +using Checkout.AgenticCommerce.Responses.Common; using Checkout.Common; +using System; namespace Checkout.Agentic { @@ -32,17 +34,17 @@ public AgenticClientTest() [Fact] private async Task EnrollShouldEnroll() { - var agenticEnrollRequest = new AgenticEnrollRequest + var agenticEnrollRequest = new EnrollACardRequest { - Source = new PaymentSource + Source = new AgenticSource { Number = "4242424242424242", ExpiryMonth = 12, ExpiryYear = 2025, Cvv = "123", - Type = PaymentSourceType.Card + Type = "Card" }, - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "192.168.1.1", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" @@ -55,7 +57,7 @@ private async Task EnrollShouldEnroll() } }; - var expectedResponse = new AgenticEnrollResponse + var expectedResponse = new EnrollACardResponse { TokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", Status = "enrolled", @@ -63,7 +65,7 @@ private async Task EnrollShouldEnroll() }; _apiClient.Setup(apiClient => - apiClient.Post("agentic/enroll", _authorization, agenticEnrollRequest, + apiClient.Post("agentic/enroll", _authorization, agenticEnrollRequest, CancellationToken.None, null)) .ReturnsAsync(() => expectedResponse); @@ -91,17 +93,17 @@ private async Task EnrollShouldThrowExceptionWhenEnrollRequestIsNull() [Fact] private async Task EnrollShouldCallCorrectEnrollEndpoint() { - var agenticEnrollRequest = new AgenticEnrollRequest + var agenticEnrollRequest = new EnrollACardRequest { - Source = new PaymentSource + Source = new AgenticSource { Number = "4242424242424242", ExpiryMonth = 12, ExpiryYear = 2025, Cvv = "123", - Type = PaymentSourceType.Card + Type = "card" }, - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "192.168.1.1", UserAgent = "Mozilla/5.0 Test" @@ -115,50 +117,50 @@ private async Task EnrollShouldCallCorrectEnrollEndpoint() }; _apiClient.Setup(apiClient => - apiClient.Post("agentic/enroll", _authorization, agenticEnrollRequest, + apiClient.Post("agentic/enroll", _authorization, agenticEnrollRequest, CancellationToken.None, null)) - .ReturnsAsync(() => new AgenticEnrollResponse()); + .ReturnsAsync(() => new EnrollACardResponse()); IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); await client.Enroll(agenticEnrollRequest, CancellationToken.None); _apiClient.Verify(apiClient => - apiClient.Post("agentic/enroll", _authorization, agenticEnrollRequest, + apiClient.Post("agentic/enroll", _authorization, agenticEnrollRequest, CancellationToken.None, null), Times.Once); } [Fact] private async Task EnrollShouldUseCorrectAuthorizationForEnroll() { - var agenticEnrollRequest = new AgenticEnrollRequest + var agenticEnrollRequest = new EnrollACardRequest { - Source = new PaymentSource { Type = PaymentSourceType.Card }, - Device = new DeviceInfo(), + Source = new AgenticSource { Type = "card" }, + Device = new AgenticDevice(), Customer = new AgenticCustomer { Email = "test@example.com", CountryCode = CountryCode.US, LanguageCode = "en" } }; _apiClient.Setup(apiClient => - apiClient.Post(It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(() => new AgenticEnrollResponse()); + apiClient.Post(It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(() => new EnrollACardResponse()); IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); await client.Enroll(agenticEnrollRequest, CancellationToken.None); _apiClient.Verify(apiClient => - apiClient.Post(It.IsAny(), _authorization, - It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + apiClient.Post(It.IsAny(), _authorization, + It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } [Fact] private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() { - var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", @@ -166,29 +168,28 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() DeviceType = "tablet" }, CustomerPrompt = "I'm looking for running shoes in a size 10, for under $150.", - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateRequest { - Id = "mandate_123", PurchaseThreshold = new PurchaseThreshold { Amount = 100, CurrencyCode = Currency.USD }, Description = "Purchase running shoes in size 10.", - ExpirationDate = System.DateTime.Parse("2026-08-31T23:59:59.000Z") + ExpirationDate = DateTime.Parse("2026-08-31T23:59:59.000Z") } } }; - var expectedResponse = new AgenticPurchaseIntentResponse + var expectedResponse = new PurchaseIntentResponse { Id = "pi_f3egwppx6rde3hg6itlqzp3h7e", Scheme = "visa", - Status = "active", + Status = PurchaseIntentStatusType.Created, TokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - DeviceData = new DeviceInfo + DeviceData = new AgenticDevice { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", @@ -196,9 +197,9 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() DeviceType = "tablet" }, CustomerPrompt = "Hey AI, I need Nike running shoes in size 10 under $130.00", - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateResponse { Id = "mandate_123", PurchaseThreshold = new PurchaseThreshold @@ -220,7 +221,7 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() }; _apiClient.Setup(apiClient => - apiClient.Post("agentic/purchase-intent", _authorization, createPurchaseIntentRequest, + apiClient.Post("agentic/purchase-intent", _authorization, createPurchaseIntentRequest, CancellationToken.None, null)) .ReturnsAsync(() => expectedResponse); @@ -252,70 +253,69 @@ private async Task CreatePurchaseIntentShouldThrowExceptionWhenCreatePurchaseInt [Fact] private async Task CreatePurchaseIntentShouldCallCorrectCreatePurchaseIntentEndpoint() { - var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_test_123", - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 Test" }, CustomerPrompt = "Test prompt", - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateRequest { - Id = "mandate_test", Description = "Test mandate" } } }; _apiClient.Setup(apiClient => - apiClient.Post("agentic/purchase-intent", _authorization, createPurchaseIntentRequest, + apiClient.Post("agentic/purchase-intent", _authorization, createPurchaseIntentRequest, CancellationToken.None, null)) - .ReturnsAsync(() => new AgenticPurchaseIntentResponse()); + .ReturnsAsync(() => new PurchaseIntentResponse()); IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); await client.CreatePurchaseIntent(createPurchaseIntentRequest, CancellationToken.None); _apiClient.Verify(apiClient => - apiClient.Post("agentic/purchase-intent", _authorization, createPurchaseIntentRequest, + apiClient.Post("agentic/purchase-intent", _authorization, createPurchaseIntentRequest, CancellationToken.None, null), Times.Once); } [Fact] private async Task CreatePurchaseIntentShouldUseCorrectAuthorizationForCreatePurchaseIntent() { - var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_test_123", - Device = new DeviceInfo(), + Device = new AgenticDevice(), CustomerPrompt = "Test prompt" }; _apiClient.Setup(apiClient => - apiClient.Post(It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(() => new AgenticPurchaseIntentResponse()); + apiClient.Post(It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(() => new PurchaseIntentResponse()); IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); await client.CreatePurchaseIntent(createPurchaseIntentRequest, CancellationToken.None); _apiClient.Verify(apiClient => - apiClient.Post(It.IsAny(), _authorization, - It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + apiClient.Post(It.IsAny(), _authorization, + It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } [Fact] private async Task CreatePurchaseIntentCredentialsShouldCreatePurchaseIntentCredentials() { var purchaseIntentId = "pi_f3egwppx6rde3hg6itlqzp3h7e"; - var createCredentialsRequest = new AgenticPurchaseIntentCredentialsCreateRequest + var createCredentialsRequest = new PurchaseIntentCredentialsCreateRequest { - TransactionData = new[] + TransactionData = new List { new TransactionData { @@ -332,13 +332,13 @@ private async Task CreatePurchaseIntentCredentialsShouldCreatePurchaseIntentCred } }; - var expectedResponse = new AgenticPurchaseIntentResponse + var expectedResponse = new PurchaseIntentResponse { Id = purchaseIntentId, Scheme = "visa", - Status = "credentials_created", + Status = PurchaseIntentStatusType.Created, TokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - DeviceData = new DeviceInfo + DeviceData = new AgenticDevice { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", @@ -348,7 +348,7 @@ private async Task CreatePurchaseIntentCredentialsShouldCreatePurchaseIntentCred CustomerPrompt = "Hey AI, I need Nike running shoes in size 10 under $130.00", Mandates = new[] { - new Mandate + new AgenticMandateResponse { Id = "mandate_123", PurchaseThreshold = new PurchaseThreshold @@ -370,7 +370,7 @@ private async Task CreatePurchaseIntentCredentialsShouldCreatePurchaseIntentCred }; _apiClient.Setup(apiClient => - apiClient.Post("agentic/purchase-intent/pi_f3egwppx6rde3hg6itlqzp3h7e/credentials", _authorization, createCredentialsRequest, + apiClient.Post("agentic/purchase-intent/pi_f3egwppx6rde3hg6itlqzp3h7e/credentials", _authorization, createCredentialsRequest, CancellationToken.None, null)) .ReturnsAsync(() => expectedResponse); @@ -391,7 +391,7 @@ private async Task CreatePurchaseIntentCredentialsShouldCreatePurchaseIntentCred [Fact] private async Task CreatePurchaseIntentCredentialsShouldThrowExceptionWhenIdIsNull() { - var createCredentialsRequest = new AgenticPurchaseIntentCredentialsCreateRequest + var createCredentialsRequest = new PurchaseIntentCredentialsCreateRequest { TransactionData = new[] { @@ -428,7 +428,7 @@ private async Task CreatePurchaseIntentCredentialsShouldThrowExceptionWhenReques private async Task CreatePurchaseIntentCredentialsShouldCallCorrectEndpoint() { var purchaseIntentId = "pi_test_endpoint_123"; - var createCredentialsRequest = new AgenticPurchaseIntentCredentialsCreateRequest + var createCredentialsRequest = new PurchaseIntentCredentialsCreateRequest { TransactionData = new[] { @@ -447,16 +447,16 @@ private async Task CreatePurchaseIntentCredentialsShouldCallCorrectEndpoint() }; _apiClient.Setup(apiClient => - apiClient.Post($"agentic/purchase-intent/{purchaseIntentId}/credentials", _authorization, createCredentialsRequest, + apiClient.Post($"agentic/purchase-intent/{purchaseIntentId}/credentials", _authorization, createCredentialsRequest, CancellationToken.None, null)) - .ReturnsAsync(() => new AgenticPurchaseIntentResponse()); + .ReturnsAsync(() => new PurchaseIntentResponse()); IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); await client.CreatePurchaseIntentCredentials(purchaseIntentId, createCredentialsRequest, CancellationToken.None); _apiClient.Verify(apiClient => - apiClient.Post($"agentic/purchase-intent/{purchaseIntentId}/credentials", _authorization, createCredentialsRequest, + apiClient.Post($"agentic/purchase-intent/{purchaseIntentId}/credentials", _authorization, createCredentialsRequest, CancellationToken.None, null), Times.Once); } @@ -464,7 +464,7 @@ private async Task CreatePurchaseIntentCredentialsShouldCallCorrectEndpoint() private async Task CreatePurchaseIntentCredentialsShouldUseCorrectAuthorization() { var purchaseIntentId = "pi_auth_test_123"; - var createCredentialsRequest = new AgenticPurchaseIntentCredentialsCreateRequest + var createCredentialsRequest = new PurchaseIntentCredentialsCreateRequest { TransactionData = new[] { @@ -477,24 +477,24 @@ private async Task CreatePurchaseIntentCredentialsShouldUseCorrectAuthorization( }; _apiClient.Setup(apiClient => - apiClient.Post(It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(() => new AgenticPurchaseIntentResponse()); + apiClient.Post(It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(() => new PurchaseIntentResponse()); IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); await client.CreatePurchaseIntentCredentials(purchaseIntentId, createCredentialsRequest, CancellationToken.None); _apiClient.Verify(apiClient => - apiClient.Post(It.IsAny(), _authorization, - It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + apiClient.Post(It.IsAny(), _authorization, + It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } [Fact] private async Task CreatePurchaseIntentCredentialsShouldHandleMultipleTransactionData() { var purchaseIntentId = "pi_multi_test_123"; - var createCredentialsRequest = new AgenticPurchaseIntentCredentialsCreateRequest + var createCredentialsRequest = new PurchaseIntentCredentialsCreateRequest { TransactionData = new[] { @@ -525,15 +525,15 @@ private async Task CreatePurchaseIntentCredentialsShouldHandleMultipleTransactio } }; - var expectedResponse = new AgenticPurchaseIntentResponse + var expectedResponse = new PurchaseIntentResponse { Id = purchaseIntentId, - Status = "credentials_created", + Status = PurchaseIntentStatusType.Created, TokenId = "nt_multi_test_token" }; _apiClient.Setup(apiClient => - apiClient.Post($"agentic/purchase-intent/{purchaseIntentId}/credentials", _authorization, createCredentialsRequest, + apiClient.Post($"agentic/purchase-intent/{purchaseIntentId}/credentials", _authorization, createCredentialsRequest, CancellationToken.None, null)) .ReturnsAsync(() => expectedResponse); @@ -551,14 +551,13 @@ private async Task CreatePurchaseIntentCredentialsShouldHandleMultipleTransactio private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() { var purchaseIntentId = "pi_f3egwppx6rde3hg6itlqzp3h7e"; - var updatePurchaseIntentRequest = new AgenticPurchaseIntentUpdateRequest + var updatePurchaseIntentRequest = new PurchaseIntentUpdateRequest { CustomerPrompt = "Updated prompt: I'm looking for Nike running shoes in size 10.5, for under $200.", - Mandates = new[] + Mandates = new List() { - new Mandate + new AgenticMandateRequest { - Id = "mandate_updated_123", PurchaseThreshold = new PurchaseThreshold { Amount = 20000, @@ -570,13 +569,13 @@ private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() } }; - var expectedResponse = new AgenticPurchaseIntentResponse + var expectedResponse = new PurchaseIntentResponse { Id = purchaseIntentId, Scheme = "visa", - Status = "updated", + Status = PurchaseIntentStatusType.Active, TokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - DeviceData = new DeviceInfo + DeviceData = new AgenticDevice { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", @@ -586,7 +585,7 @@ private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() CustomerPrompt = "Updated prompt: I'm looking for Nike running shoes in size 10.5, for under $200.", Mandates = new[] { - new Mandate + new AgenticMandateResponse { Id = "mandate_updated_123", PurchaseThreshold = new PurchaseThreshold @@ -608,7 +607,7 @@ private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() }; _apiClient.Setup(apiClient => - apiClient.Put($"agentic/purchase-intent/{purchaseIntentId}", _authorization, updatePurchaseIntentRequest, + apiClient.Put($"agentic/purchase-intent/{purchaseIntentId}", _authorization, updatePurchaseIntentRequest, CancellationToken.None, null)) .ReturnsAsync(() => expectedResponse); @@ -629,14 +628,13 @@ private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() [Fact] private async Task UpdatePurchaseIntentShouldThrowExceptionWhenIdIsNull() { - var updatePurchaseIntentRequest = new AgenticPurchaseIntentUpdateRequest + var updatePurchaseIntentRequest = new PurchaseIntentUpdateRequest { CustomerPrompt = "Test prompt", - Mandates = new[] + Mandates = new List() { - new Mandate + new AgenticMandateRequest { - Id = "mandate_test", Description = "Test mandate" } } @@ -666,7 +664,7 @@ private async Task UpdatePurchaseIntentShouldThrowExceptionWhenRequestIsNull() [Fact] private async Task UpdatePurchaseIntentShouldThrowExceptionWhenIdIsEmpty() { - var updatePurchaseIntentRequest = new AgenticPurchaseIntentUpdateRequest + var updatePurchaseIntentRequest = new PurchaseIntentUpdateRequest { CustomerPrompt = "Test prompt" }; @@ -683,14 +681,13 @@ private async Task UpdatePurchaseIntentShouldThrowExceptionWhenIdIsEmpty() private async Task UpdatePurchaseIntentShouldCallCorrectEndpoint() { var purchaseIntentId = "pi_test_update_123"; - var updatePurchaseIntentRequest = new AgenticPurchaseIntentUpdateRequest + var updatePurchaseIntentRequest = new PurchaseIntentUpdateRequest { CustomerPrompt = "Test update prompt", - Mandates = new[] + Mandates = new List() { - new Mandate + new AgenticMandateRequest { - Id = "mandate_test_update", PurchaseThreshold = new PurchaseThreshold { Amount = 15000, @@ -702,16 +699,16 @@ private async Task UpdatePurchaseIntentShouldCallCorrectEndpoint() }; _apiClient.Setup(apiClient => - apiClient.Put($"agentic/purchase-intent/{purchaseIntentId}", _authorization, updatePurchaseIntentRequest, + apiClient.Put($"agentic/purchase-intent/{purchaseIntentId}", _authorization, updatePurchaseIntentRequest, CancellationToken.None, null)) - .ReturnsAsync(() => new AgenticPurchaseIntentResponse()); + .ReturnsAsync(() => new PurchaseIntentResponse()); IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); await client.UpdatePurchaseIntent(purchaseIntentId, updatePurchaseIntentRequest, CancellationToken.None); _apiClient.Verify(apiClient => - apiClient.Put($"agentic/purchase-intent/{purchaseIntentId}", _authorization, updatePurchaseIntentRequest, + apiClient.Put($"agentic/purchase-intent/{purchaseIntentId}", _authorization, updatePurchaseIntentRequest, CancellationToken.None, null), Times.Once); } @@ -719,53 +716,58 @@ private async Task UpdatePurchaseIntentShouldCallCorrectEndpoint() private async Task UpdatePurchaseIntentShouldUseCorrectAuthorization() { var purchaseIntentId = "pi_auth_test_123"; - var updatePurchaseIntentRequest = new AgenticPurchaseIntentUpdateRequest + var updatePurchaseIntentRequest = new PurchaseIntentUpdateRequest { CustomerPrompt = "Auth test prompt", - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateRequest { - Id = "mandate_auth_test", - Description = "Auth test mandate" + Description = "Auth test mandate", + PurchaseThreshold = new PurchaseThreshold + { + Amount = 0, + CurrencyCode = null + }, + ExpirationDate = new DateTime() } } }; _apiClient.Setup(apiClient => - apiClient.Put(It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(() => new AgenticPurchaseIntentResponse()); + apiClient.Put(It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(() => new PurchaseIntentResponse()); IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); await client.UpdatePurchaseIntent(purchaseIntentId, updatePurchaseIntentRequest, CancellationToken.None); _apiClient.Verify(apiClient => - apiClient.Put(It.IsAny(), _authorization, - It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); + apiClient.Put(It.IsAny(), _authorization, + It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); } [Fact] private async Task UpdatePurchaseIntentShouldHandlePartialUpdates() { var purchaseIntentId = "pi_partial_update_123"; - var updatePurchaseIntentRequest = new AgenticPurchaseIntentUpdateRequest + var updatePurchaseIntentRequest = new PurchaseIntentUpdateRequest { CustomerPrompt = "Only updating the customer prompt" // Mandates is null - partial update }; - var expectedResponse = new AgenticPurchaseIntentResponse + var expectedResponse = new PurchaseIntentResponse { Id = purchaseIntentId, - Status = "updated", + Status = PurchaseIntentStatusType.Created, CustomerPrompt = "Only updating the customer prompt", TokenId = "nt_partial_update_token" }; _apiClient.Setup(apiClient => - apiClient.Put($"agentic/purchase-intent/{purchaseIntentId}", _authorization, updatePurchaseIntentRequest, + apiClient.Put($"agentic/purchase-intent/{purchaseIntentId}", _authorization, updatePurchaseIntentRequest, CancellationToken.None, null)) .ReturnsAsync(() => expectedResponse); diff --git a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs index 61534f9e..32bbcaa3 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs +++ b/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs @@ -3,10 +3,12 @@ using System.Threading.Tasks; using Shouldly; using Xunit; - -using Checkout.Agentic.Entities; -using Checkout.Agentic.Requests; +using Checkout.AgenticCommerce.Common; +using Checkout.AgenticCommerce.Requests; +using Checkout.AgenticCommerce.Requests.Common; +using Checkout.AgenticCommerce.Responses.Common; using Checkout.Common; +using System.Collections.Generic; namespace Checkout.Agentic { @@ -19,17 +21,17 @@ public AgenticIntegrationTest() : base(PlatformType.DefaultOAuth) [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] private async Task EnrollShouldEnroll() { - var agenticEnrollRequest = new AgenticEnrollRequest + var agenticEnrollRequest = new EnrollACardRequest { - Source = new PaymentSource + Source = new AgenticSource { Number = "4242424242424242", ExpiryMonth = 12, ExpiryYear = 2025, Cvv = "123", - Type = PaymentSourceType.Card + Type = "card" }, - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", @@ -61,17 +63,17 @@ private async Task EnrollShouldEnroll() [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] private async Task EnrollShouldEnrollWithMinimalData() { - var agenticEnrollRequest = new AgenticEnrollRequest + var agenticEnrollRequest = new EnrollACardRequest { - Source = new PaymentSource + Source = new AgenticSource { Number = "4242424242424242", ExpiryMonth = 6, ExpiryYear = 2026, Cvv = "100", - Type = PaymentSourceType.Card + Type = "card" }, - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "10.0.0.1" }, @@ -94,17 +96,17 @@ private async Task EnrollShouldEnrollWithMinimalData() private async Task EnrollShouldHandleDifferentCardTypes() { // Test with Visa card - var visaRequest = new AgenticEnrollRequest + var visaRequest = new EnrollACardRequest { - Source = new PaymentSource + Source = new AgenticSource { Number = "4242424242424242", // Visa test card ExpiryMonth = 8, ExpiryYear = 2027, Cvv = "888", - Type = PaymentSourceType.Card + Type = "card" }, - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "203.0.113.195", UserAgent = "Test Agent" @@ -122,17 +124,17 @@ private async Task EnrollShouldHandleDifferentCardTypes() visaResponse.Status.ShouldBe("enrolled"); // Test with Mastercard - var mastercardRequest = new AgenticEnrollRequest + var mastercardRequest = new EnrollACardRequest { - Source = new PaymentSource + Source = new AgenticSource { Number = "5555555555554444", // Mastercard test card ExpiryMonth = 9, ExpiryYear = 2028, Cvv = "999", - Type = PaymentSourceType.Card + Type = "card" }, - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "198.51.100.42" }, @@ -152,17 +154,17 @@ private async Task EnrollShouldHandleDifferentCardTypes() [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] private async Task EnrollShouldHandleInternationalCustomers() { - var internationalRequest = new AgenticEnrollRequest + var internationalRequest = new EnrollACardRequest { - Source = new PaymentSource + Source = new AgenticSource { Number = "4242424242424242", ExpiryMonth = 3, ExpiryYear = 2029, Cvv = "314", - Type = PaymentSourceType.Card + Type = "card" }, - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "80.112.12.34", // European IP UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36", @@ -187,10 +189,10 @@ private async Task EnrollShouldHandleInternationalCustomers() [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() { - var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", @@ -198,11 +200,10 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() DeviceType = "tablet" }, CustomerPrompt = "I'm looking for running shoes in a size 10, for under $150.", - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateRequest { - Id = "mandate_123", PurchaseThreshold = new PurchaseThreshold { Amount = 100, @@ -218,7 +219,7 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() response.ShouldNotBeNull(); response.Id.ShouldNotBeNullOrEmpty(); - response.Status.ShouldNotBeNullOrEmpty(); + response.Status.ShouldNotBeNull(); response.TokenId.ShouldNotBeNullOrEmpty(); response.CustomerPrompt.ShouldNotBeNullOrEmpty(); @@ -226,7 +227,7 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() response.Id.ShouldStartWith("pi_"); // Validate status is one of expected values - response.Status.ShouldBe("active"); + response.Status.ShouldBe(PurchaseIntentStatusType.Active); // Validate links are present response.Links.ShouldNotBeNull(); @@ -237,10 +238,10 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] private async Task CreatePurchaseIntentShouldCreatePurchaseIntentWithMinimalData() { - var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "10.0.0.1" }, @@ -251,7 +252,7 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntentWithMinimalData response.ShouldNotBeNull(); response.Id.ShouldNotBeNullOrEmpty(); - response.Status.ShouldBe("active"); + response.Status.ShouldBe(PurchaseIntentStatusType.Active); response.TokenId.ShouldNotBeNullOrEmpty(); } @@ -259,10 +260,10 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntentWithMinimalData private async Task CreatePurchaseIntentShouldHandleDifferentMandateTypes() { // Test with single mandate - var singleMandateRequest = new AgenticPurchaseIntentCreateRequest + var singleMandateRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "203.0.113.195", UserAgent = "Test Agent", @@ -270,11 +271,10 @@ private async Task CreatePurchaseIntentShouldHandleDifferentMandateTypes() DeviceType = "desktop" }, CustomerPrompt = "Looking for electronics under $500", - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateRequest { - Id = "mandate_single", PurchaseThreshold = new PurchaseThreshold { Amount = 500, @@ -288,23 +288,22 @@ private async Task CreatePurchaseIntentShouldHandleDifferentMandateTypes() var singleResponse = await DefaultApi.AgenticClient().CreatePurchaseIntent(singleMandateRequest); singleResponse.ShouldNotBeNull(); - singleResponse.Status.ShouldBe("active"); - singleResponse.Mandates.Length.ShouldBe(1); + singleResponse.Status.ShouldBe(PurchaseIntentStatusType.Active); + singleResponse.Mandates.Count.ShouldBe(1); // Test with multiple mandates - var multipleMandateRequest = new AgenticPurchaseIntentCreateRequest + var multipleMandateRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "198.51.100.42" }, CustomerPrompt = "Shopping for clothing and accessories", - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateRequest { - Id = "mandate_clothing", PurchaseThreshold = new PurchaseThreshold { Amount = 200, @@ -313,9 +312,8 @@ private async Task CreatePurchaseIntentShouldHandleDifferentMandateTypes() Description = "Clothing purchase", ExpirationDate = DateTime.Parse("2026-06-30T23:59:59.000Z") }, - new Mandate + new AgenticMandateRequest { - Id = "mandate_accessories", PurchaseThreshold = new PurchaseThreshold { Amount = 100, @@ -329,17 +327,17 @@ private async Task CreatePurchaseIntentShouldHandleDifferentMandateTypes() var multipleResponse = await DefaultApi.AgenticClient().CreatePurchaseIntent(multipleMandateRequest); multipleResponse.ShouldNotBeNull(); - multipleResponse.Status.ShouldBe("active"); - multipleResponse.Mandates.Length.ShouldBe(2); + multipleResponse.Status.ShouldBe(PurchaseIntentStatusType.Active); + multipleResponse.Mandates.Count.ShouldBe(2); } [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] private async Task CreatePurchaseIntentShouldHandleInternationalCurrencies() { - var internationalRequest = new AgenticPurchaseIntentCreateRequest + var internationalRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "80.112.12.34", // European IP UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36", @@ -347,11 +345,10 @@ private async Task CreatePurchaseIntentShouldHandleInternationalCurrencies() DeviceType = "mobile" }, CustomerPrompt = "Buscando zapatos deportivos en talla 42", - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateRequest { - Id = "mandate_eur", PurchaseThreshold = new PurchaseThreshold { Amount = 150, @@ -366,7 +363,7 @@ private async Task CreatePurchaseIntentShouldHandleInternationalCurrencies() var response = await DefaultApi.AgenticClient().CreatePurchaseIntent(internationalRequest); response.ShouldNotBeNull(); - response.Status.ShouldBe("active"); + response.Status.ShouldBe(PurchaseIntentStatusType.Active); response.Id.ShouldStartWith("pi_"); response.Mandates.ShouldNotBeNull(); response.Mandates[0].PurchaseThreshold.CurrencyCode.ShouldBe(Currency.EUR); @@ -376,20 +373,19 @@ private async Task CreatePurchaseIntentShouldHandleInternationalCurrencies() private async Task CreatePurchaseIntentCredentialsShouldCreateCredentials() { // First create a purchase intent to get an ID - var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" }, CustomerPrompt = "I need running shoes in size 10, under $150.", - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateRequest { - Id = "mandate_credentials_test", PurchaseThreshold = new PurchaseThreshold { Amount = 15000, @@ -406,7 +402,7 @@ private async Task CreatePurchaseIntentCredentialsShouldCreateCredentials() purchaseIntentResponse.Id.ShouldNotBeNullOrEmpty(); // Now create credentials for this purchase intent - var createCredentialsRequest = new AgenticPurchaseIntentCredentialsCreateRequest + var createCredentialsRequest = new PurchaseIntentCredentialsCreateRequest { TransactionData = new[] { @@ -430,7 +426,7 @@ private async Task CreatePurchaseIntentCredentialsShouldCreateCredentials() credentialsResponse.ShouldNotBeNull(); credentialsResponse.Id.ShouldBe(purchaseIntentResponse.Id); - credentialsResponse.Status.ShouldNotBeNullOrEmpty(); + credentialsResponse.Status.ShouldNotBeNull(); credentialsResponse.TokenId.ShouldNotBeNullOrEmpty(); // Validate that the purchase intent ID follows expected pattern @@ -446,20 +442,19 @@ private async Task CreatePurchaseIntentCredentialsShouldCreateCredentials() private async Task CreatePurchaseIntentCredentialsShouldHandleMultipleTransactionData() { // First create a purchase intent - var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "203.0.113.195", UserAgent = "Test Agent Multi Transaction" }, CustomerPrompt = "I need multiple items: electronics and clothing.", - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateRequest { - Id = "mandate_multi_transaction", PurchaseThreshold = new PurchaseThreshold { Amount = 100000, @@ -475,7 +470,7 @@ private async Task CreatePurchaseIntentCredentialsShouldHandleMultipleTransactio purchaseIntentResponse.ShouldNotBeNull(); // Create credentials with multiple transaction data - var createCredentialsRequest = new AgenticPurchaseIntentCredentialsCreateRequest + var createCredentialsRequest = new PurchaseIntentCredentialsCreateRequest { TransactionData = new[] { @@ -511,7 +506,7 @@ private async Task CreatePurchaseIntentCredentialsShouldHandleMultipleTransactio credentialsResponse.ShouldNotBeNull(); credentialsResponse.Id.ShouldBe(purchaseIntentResponse.Id); - credentialsResponse.Status.ShouldNotBeNullOrEmpty(); + credentialsResponse.Status.ShouldNotBeNull(); credentialsResponse.TokenId.ShouldNotBeNullOrEmpty(); // Validate response structure @@ -523,20 +518,19 @@ private async Task CreatePurchaseIntentCredentialsShouldHandleMultipleTransactio private async Task CreatePurchaseIntentCredentialsShouldHandleInternationalTransactions() { // Create purchase intent - var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "80.112.12.34", // European IP UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36" }, CustomerPrompt = "Looking for luxury items in different currencies.", - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateRequest { - Id = "mandate_international", PurchaseThreshold = new PurchaseThreshold { Amount = 200000, @@ -552,7 +546,7 @@ private async Task CreatePurchaseIntentCredentialsShouldHandleInternationalTrans purchaseIntentResponse.ShouldNotBeNull(); // Create credentials with international transaction data - var createCredentialsRequest = new AgenticPurchaseIntentCredentialsCreateRequest + var createCredentialsRequest = new PurchaseIntentCredentialsCreateRequest { TransactionData = new[] { @@ -588,7 +582,7 @@ private async Task CreatePurchaseIntentCredentialsShouldHandleInternationalTrans credentialsResponse.ShouldNotBeNull(); credentialsResponse.Id.ShouldBe(purchaseIntentResponse.Id); - credentialsResponse.Status.ShouldNotBeNullOrEmpty(); + credentialsResponse.Status.ShouldNotBeNull(); credentialsResponse.TokenId.ShouldNotBeNullOrEmpty(); // Validate international handling @@ -601,10 +595,10 @@ private async Task CreatePurchaseIntentCredentialsShouldHandleInternationalTrans private async Task CreatePurchaseIntentCredentialsShouldHandleMinimalTransactionData() { // Create purchase intent first - var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "10.0.0.1" }, @@ -615,7 +609,7 @@ private async Task CreatePurchaseIntentCredentialsShouldHandleMinimalTransaction purchaseIntentResponse.ShouldNotBeNull(); // Create credentials with minimal transaction data - var createCredentialsRequest = new AgenticPurchaseIntentCredentialsCreateRequest + var createCredentialsRequest = new PurchaseIntentCredentialsCreateRequest { TransactionData = new[] { @@ -636,7 +630,7 @@ private async Task CreatePurchaseIntentCredentialsShouldHandleMinimalTransaction credentialsResponse.ShouldNotBeNull(); credentialsResponse.Id.ShouldBe(purchaseIntentResponse.Id); - credentialsResponse.Status.ShouldNotBeNullOrEmpty(); + credentialsResponse.Status.ShouldNotBeNull(); credentialsResponse.TokenId.ShouldNotBeNullOrEmpty(); credentialsResponse.Id.ShouldStartWith("pi_"); } @@ -645,10 +639,10 @@ private async Task CreatePurchaseIntentCredentialsShouldHandleMinimalTransaction private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() { // First create a purchase intent to update - var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", @@ -656,11 +650,10 @@ private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() DeviceType = "tablet" }, CustomerPrompt = "I'm looking for running shoes in a size 10, for under $150.", - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateRequest { - Id = "mandate_original_123", PurchaseThreshold = new PurchaseThreshold { Amount = 15000, @@ -678,14 +671,13 @@ private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() purchaseIntentResponse.Id.ShouldNotBeNullOrEmpty(); // Now update the purchase intent - var updatePurchaseIntentRequest = new AgenticPurchaseIntentUpdateRequest + var updatePurchaseIntentRequest = new PurchaseIntentUpdateRequest { CustomerPrompt = "Updated: I'm looking for Nike running shoes in size 10.5, for under $200.", - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateRequest { - Id = "mandate_updated_123", PurchaseThreshold = new PurchaseThreshold { Amount = 20000, @@ -702,14 +694,14 @@ private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() updateResponse.ShouldNotBeNull(); updateResponse.Id.ShouldBe(purchaseIntentResponse.Id); updateResponse.CustomerPrompt.ShouldBe(updatePurchaseIntentRequest.CustomerPrompt); - updateResponse.Status.ShouldNotBeNullOrEmpty(); + updateResponse.Status.ShouldNotBeNull(); } [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] private async Task UpdatePurchaseIntentShouldHandleNonExistentPurchaseIntent() { var nonExistentId = "pi_nonexistent_123456"; - var updatePurchaseIntentRequest = new AgenticPurchaseIntentUpdateRequest + var updatePurchaseIntentRequest = new PurchaseIntentUpdateRequest { CustomerPrompt = "This should fail" }; @@ -724,7 +716,7 @@ private async Task UpdatePurchaseIntentShouldHandleNonExistentPurchaseIntent() private async Task UpdatePurchaseIntentShouldValidateInputParameters() { // Test with null ID - var updateRequest = new AgenticPurchaseIntentUpdateRequest + var updateRequest = new PurchaseIntentUpdateRequest { CustomerPrompt = "Valid prompt" }; @@ -746,20 +738,19 @@ private async Task UpdatePurchaseIntentShouldValidateInputParameters() private async Task UpdatePurchaseIntentShouldHandlePartialUpdates() { // First create a purchase intent - var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 Test Browser" }, CustomerPrompt = "Original prompt", - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateRequest { - Id = "mandate_original", Description = "Original mandate" } } @@ -768,7 +759,7 @@ private async Task UpdatePurchaseIntentShouldHandlePartialUpdates() var purchaseIntentResponse = await DefaultApi.AgenticClient().CreatePurchaseIntent(createPurchaseIntentRequest); // Update only the customer prompt (partial update) - var partialUpdateRequest = new AgenticPurchaseIntentUpdateRequest + var partialUpdateRequest = new PurchaseIntentUpdateRequest { CustomerPrompt = "Updated prompt only" // Mandates is null - testing partial update @@ -786,20 +777,19 @@ private async Task UpdatePurchaseIntentShouldHandlePartialUpdates() private async Task UpdatePurchaseIntentShouldHandleDifferentPurchaseIntentStates() { // Create purchase intent - var createRequest = new AgenticPurchaseIntentCreateRequest + var createRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 State Test" }, CustomerPrompt = "Test different states", - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateRequest { - Id = "mandate_state_test", PurchaseThreshold = new PurchaseThreshold { Amount = 10000, @@ -813,14 +803,13 @@ private async Task UpdatePurchaseIntentShouldHandleDifferentPurchaseIntentStates var purchaseIntentResponse = await DefaultApi.AgenticClient().CreatePurchaseIntent(createRequest); // Try to update the purchase intent regardless of its current state - var updateRequest = new AgenticPurchaseIntentUpdateRequest + var updateRequest = new PurchaseIntentUpdateRequest { CustomerPrompt = "Updated for state testing", - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateRequest { - Id = "mandate_updated_state", PurchaseThreshold = new PurchaseThreshold { Amount = 12000, @@ -843,20 +832,19 @@ private async Task UpdatePurchaseIntentShouldHandleDifferentPurchaseIntentStates private async Task DeletePurchaseIntentShouldDeletePurchaseIntent() { // First create a purchase intent to delete - var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" }, CustomerPrompt = "I need running shoes for deletion test.", - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateRequest { - Id = "mandate_delete_test", PurchaseThreshold = new PurchaseThreshold { Amount = 10000, @@ -899,20 +887,19 @@ private async Task DeletePurchaseIntentShouldHandleNonExistentPurchaseIntent() private async Task DeletePurchaseIntentShouldHandleAlreadyDeletedPurchaseIntent() { // First create a purchase intent - var createPurchaseIntentRequest = new AgenticPurchaseIntentCreateRequest + var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "203.0.113.195", UserAgent = "Test Agent Double Delete" }, CustomerPrompt = "Purchase intent for double deletion test.", - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateRequest { - Id = "mandate_double_delete", PurchaseThreshold = new PurchaseThreshold { Amount = 5000, @@ -952,20 +939,19 @@ private async Task DeletePurchaseIntentShouldHandleDifferentPurchaseIntentStates foreach (var testCase in testCases) { // Create purchase intent - var createRequest = new AgenticPurchaseIntentCreateRequest + var createRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new DeviceInfo + Device = new AgenticDevice { IpAddress = "10.0.0.1", UserAgent = "Test Agent States" }, CustomerPrompt = testCase.CustomerPrompt, - Mandates = new[] + Mandates = new List { - new Mandate + new AgenticMandateRequest { - Id = $"mandate_states_{testCase.Description.Replace(" ", "_").ToLower()}", PurchaseThreshold = new PurchaseThreshold { Amount = 7500, From ba2c2af6aff2d70f8bae3d5dc371c862501fd559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armando=20Rodr=C3=ADguez?= <127134616+armando-rodriguez-cko@users.noreply.github.com> Date: Fri, 21 Nov 2025 13:44:38 +0100 Subject: [PATCH 14/18] WIP Integrations tests --- .../AgenticCommerce/AgenticClient.cs | 4 +- .../AgenticCommerce/IAgenticClient.cs | 4 +- .../Responses/EnrollACardResponse.cs | 6 ++ src/CheckoutSdk/CheckoutApi.cs | 2 +- src/CheckoutSdk/ICheckoutApi.cs | 2 +- .../AgenticClientTest.cs | 10 +- .../AgenticIntegrationTest.cs | 98 +++---------------- 7 files changed, 32 insertions(+), 94 deletions(-) rename test/CheckoutSdkTest/{Agentic => AgenticCommerce}/AgenticClientTest.cs (99%) rename test/CheckoutSdkTest/{Agentic => AgenticCommerce}/AgenticIntegrationTest.cs (92%) diff --git a/src/CheckoutSdk/AgenticCommerce/AgenticClient.cs b/src/CheckoutSdk/AgenticCommerce/AgenticClient.cs index 86873fe0..56477868 100644 --- a/src/CheckoutSdk/AgenticCommerce/AgenticClient.cs +++ b/src/CheckoutSdk/AgenticCommerce/AgenticClient.cs @@ -3,7 +3,7 @@ using Checkout.AgenticCommerce.Requests; using Checkout.AgenticCommerce.Responses; -namespace Checkout.Agentic +namespace Checkout.AgenticCommerce { /// /// Agentic Commerce API Client @@ -27,7 +27,7 @@ public AgenticClient(IApiClient apiClient, CheckoutConfiguration configuration) /// Enroll a card for use with agentic commerce /// [BETA] /// - public Task Enroll( + public Task EnrollACard( EnrollACardRequest enrollACardRequest, CancellationToken cancellationToken = default) { diff --git a/src/CheckoutSdk/AgenticCommerce/IAgenticClient.cs b/src/CheckoutSdk/AgenticCommerce/IAgenticClient.cs index 2850d7cb..d716d4cf 100644 --- a/src/CheckoutSdk/AgenticCommerce/IAgenticClient.cs +++ b/src/CheckoutSdk/AgenticCommerce/IAgenticClient.cs @@ -3,7 +3,7 @@ using Checkout.AgenticCommerce.Requests; using Checkout.AgenticCommerce.Responses; -namespace Checkout.Agentic +namespace Checkout.AgenticCommerce { /// /// Agentic Commerce API Client @@ -17,7 +17,7 @@ public interface IAgenticClient /// Enroll a card for use with agentic commerce /// [BETA] /// - Task Enroll( + Task EnrollACard( EnrollACardRequest enrollACardRequest, CancellationToken cancellationToken = default); diff --git a/src/CheckoutSdk/AgenticCommerce/Responses/EnrollACardResponse.cs b/src/CheckoutSdk/AgenticCommerce/Responses/EnrollACardResponse.cs index 579db638..65bb1373 100644 --- a/src/CheckoutSdk/AgenticCommerce/Responses/EnrollACardResponse.cs +++ b/src/CheckoutSdk/AgenticCommerce/Responses/EnrollACardResponse.cs @@ -12,6 +12,12 @@ public class EnrollACardResponse : HttpMetadata /// [Required] /// public string TokenId { get; set; } + + /// + /// The unique identifier for the provisioned token + /// [Required] + /// + public string NetworkTokenId { get; set; } /// /// The status of the enrollment diff --git a/src/CheckoutSdk/CheckoutApi.cs b/src/CheckoutSdk/CheckoutApi.cs index d9d0b119..afe2b4f5 100644 --- a/src/CheckoutSdk/CheckoutApi.cs +++ b/src/CheckoutSdk/CheckoutApi.cs @@ -1,5 +1,5 @@ using Checkout.Accounts; -using Checkout.Agentic; +using Checkout.AgenticCommerce; using Checkout.Authentication; using Checkout.Balances; using Checkout.Issuing; diff --git a/src/CheckoutSdk/ICheckoutApi.cs b/src/CheckoutSdk/ICheckoutApi.cs index 77e51028..3959c5f2 100644 --- a/src/CheckoutSdk/ICheckoutApi.cs +++ b/src/CheckoutSdk/ICheckoutApi.cs @@ -1,5 +1,5 @@ using Checkout.Accounts; -using Checkout.Agentic; +using Checkout.AgenticCommerce; using Checkout.Authentication; using Checkout.Balances; using Checkout.Issuing; diff --git a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs b/test/CheckoutSdkTest/AgenticCommerce/AgenticClientTest.cs similarity index 99% rename from test/CheckoutSdkTest/Agentic/AgenticClientTest.cs rename to test/CheckoutSdkTest/AgenticCommerce/AgenticClientTest.cs index d2aaaebc..8f8abd14 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticClientTest.cs +++ b/test/CheckoutSdkTest/AgenticCommerce/AgenticClientTest.cs @@ -12,7 +12,7 @@ using Checkout.Common; using System; -namespace Checkout.Agentic +namespace Checkout.AgenticCommerce { public class AgenticClientTest : UnitTestFixture { @@ -71,7 +71,7 @@ private async Task EnrollShouldEnroll() IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); - var response = await client.Enroll(agenticEnrollRequest, CancellationToken.None); + var response = await client.EnrollACard(agenticEnrollRequest, CancellationToken.None); response.ShouldNotBeNull(); response.TokenId.ShouldBe(expectedResponse.TokenId); @@ -85,7 +85,7 @@ private async Task EnrollShouldThrowExceptionWhenEnrollRequestIsNull() IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); var exception = await Should.ThrowAsync( - async () => await client.Enroll(null, CancellationToken.None)); + async () => await client.EnrollACard(null, CancellationToken.None)); exception.Message.ShouldContain("agenticEnrollRequest"); } @@ -123,7 +123,7 @@ private async Task EnrollShouldCallCorrectEnrollEndpoint() IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); - await client.Enroll(agenticEnrollRequest, CancellationToken.None); + await client.EnrollACard(agenticEnrollRequest, CancellationToken.None); _apiClient.Verify(apiClient => apiClient.Post("agentic/enroll", _authorization, agenticEnrollRequest, @@ -147,7 +147,7 @@ private async Task EnrollShouldUseCorrectAuthorizationForEnroll() IAgenticClient client = new AgenticClient(_apiClient.Object, _configuration.Object); - await client.Enroll(agenticEnrollRequest, CancellationToken.None); + await client.EnrollACard(agenticEnrollRequest, CancellationToken.None); _apiClient.Verify(apiClient => apiClient.Post(It.IsAny(), _authorization, diff --git a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs b/test/CheckoutSdkTest/AgenticCommerce/AgenticIntegrationTest.cs similarity index 92% rename from test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs rename to test/CheckoutSdkTest/AgenticCommerce/AgenticIntegrationTest.cs index 32bbcaa3..edb5b7b3 100644 --- a/test/CheckoutSdkTest/Agentic/AgenticIntegrationTest.cs +++ b/test/CheckoutSdkTest/AgenticCommerce/AgenticIntegrationTest.cs @@ -10,7 +10,7 @@ using Checkout.Common; using System.Collections.Generic; -namespace Checkout.Agentic +namespace Checkout.AgenticCommerce { public class AgenticIntegrationTest : SandboxTestFixture { @@ -18,17 +18,17 @@ public AgenticIntegrationTest() : base(PlatformType.DefaultOAuth) { } - [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + [Fact] private async Task EnrollShouldEnroll() { var agenticEnrollRequest = new EnrollACardRequest { Source = new AgenticSource { - Number = "4242424242424242", + Number = "4543474002249996", ExpiryMonth = 12, ExpiryYear = 2025, - Cvv = "123", + Cvv = "100", Type = "card" }, Device = new AgenticDevice @@ -46,36 +46,29 @@ private async Task EnrollShouldEnroll() } }; - var response = await DefaultApi.AgenticClient().Enroll(agenticEnrollRequest); + var response = await DefaultApi.AgenticClient().EnrollACard(agenticEnrollRequest); response.ShouldNotBeNull(); - response.TokenId.ShouldNotBeNullOrEmpty(); - response.Status.ShouldNotBeNullOrEmpty(); - response.CreatedAt.ShouldNotBe(default(DateTime)); - - // Validate that token_id follows expected pattern - response.TokenId.ShouldStartWith("nt_"); - - // Validate status is one of expected values response.Status.ShouldBe("enrolled"); + response.CreatedAt.ShouldNotBe(default(DateTime)); } - [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + [Fact] private async Task EnrollShouldEnrollWithMinimalData() { var agenticEnrollRequest = new EnrollACardRequest { Source = new AgenticSource { - Number = "4242424242424242", + Number = "4543474002249996", ExpiryMonth = 6, ExpiryYear = 2026, - Cvv = "100", Type = "card" }, Device = new AgenticDevice { - IpAddress = "10.0.0.1" + IpAddress = "10.0.0.1", + UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", }, Customer = new AgenticCustomer { @@ -85,83 +78,23 @@ private async Task EnrollShouldEnrollWithMinimalData() } }; - var response = await DefaultApi.AgenticClient().Enroll(agenticEnrollRequest); + var response = await DefaultApi.AgenticClient().EnrollACard(agenticEnrollRequest); response.ShouldNotBeNull(); - response.TokenId.ShouldNotBeNullOrEmpty(); response.Status.ShouldBe("enrolled"); } - [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] - private async Task EnrollShouldHandleDifferentCardTypes() - { - // Test with Visa card - var visaRequest = new EnrollACardRequest - { - Source = new AgenticSource - { - Number = "4242424242424242", // Visa test card - ExpiryMonth = 8, - ExpiryYear = 2027, - Cvv = "888", - Type = "card" - }, - Device = new AgenticDevice - { - IpAddress = "203.0.113.195", - UserAgent = "Test Agent" - }, - Customer = new AgenticCustomer - { - Email = GenerateRandomEmail(), - CountryCode = CountryCode.US, - LanguageCode = "en" - } - }; - - var visaResponse = await DefaultApi.AgenticClient().Enroll(visaRequest); - visaResponse.ShouldNotBeNull(); - visaResponse.Status.ShouldBe("enrolled"); - - // Test with Mastercard - var mastercardRequest = new EnrollACardRequest - { - Source = new AgenticSource - { - Number = "5555555555554444", // Mastercard test card - ExpiryMonth = 9, - ExpiryYear = 2028, - Cvv = "999", - Type = "card" - }, - Device = new AgenticDevice - { - IpAddress = "198.51.100.42" - }, - Customer = new AgenticCustomer - { - Email = GenerateRandomEmail(), - CountryCode = CountryCode.US, - LanguageCode = "en" - } - }; - - var mastercardResponse = await DefaultApi.AgenticClient().Enroll(mastercardRequest); - mastercardResponse.ShouldNotBeNull(); - mastercardResponse.Status.ShouldBe("enrolled"); - } - - [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] + [Fact] private async Task EnrollShouldHandleInternationalCustomers() { var internationalRequest = new EnrollACardRequest { Source = new AgenticSource { - Number = "4242424242424242", + Number = "4543474002249996", ExpiryMonth = 3, ExpiryYear = 2029, - Cvv = "314", + Cvv = "100", Type = "card" }, Device = new AgenticDevice @@ -179,11 +112,10 @@ private async Task EnrollShouldHandleInternationalCustomers() } }; - var response = await DefaultApi.AgenticClient().Enroll(internationalRequest); + var response = await DefaultApi.AgenticClient().EnrollACard(internationalRequest); response.ShouldNotBeNull(); response.Status.ShouldBe("enrolled"); - response.TokenId.ShouldStartWith("nt_"); } [Fact(Skip = "This test is unsupported currently, not ready to test in the sandbox")] From 777e58da7bd27d0a48045e38009e65c2e5c8900e Mon Sep 17 00:00:00 2001 From: david ruiz Date: Fri, 21 Nov 2025 14:14:59 +0100 Subject: [PATCH 15/18] Changes done at names classes and distribution of files --- .../AgenticCustomer.cs => Common/Customer.cs} | 4 +- .../AgenticDevice.cs => Common/Device.cs} | 4 +- .../AgenticCommerce/Common/Mandate.cs | 9 ++ .../{AgenticMandateBase.cs => MandateBase.cs} | 2 +- .../MandateExtended.cs} | 5 +- .../Common/PurchaseIntentStatusType.cs | 2 +- .../AgenticSource.cs => Common/Source.cs} | 4 +- .../Entities => Common}/TransactionAmount.cs | 2 +- .../Entities => Common}/TransactionData.cs | 2 +- .../Requests/Common/AgenticMandateRequest.cs | 11 -- .../Requests/EnrollACardRequest.cs | 8 +- .../Requests/PurchaseIntentCreateRequest.cs | 4 +- .../PurchaseIntentCredentialsCreateRequest.cs | 1 + .../Requests/PurchaseIntentUpdateRequest.cs | 4 +- .../Responses/PurchaseIntentResponse.cs | 9 +- .../AgenticCommerce/AgenticClientTest.cs | 69 ++++++----- .../AgenticCommerce/AgenticIntegrationTest.cs | 115 +++++++++--------- 17 files changed, 127 insertions(+), 128 deletions(-) rename src/CheckoutSdk/AgenticCommerce/{Requests/Entities/AgenticCustomer.cs => Common/Customer.cs} (90%) rename src/CheckoutSdk/AgenticCommerce/{Requests/Entities/AgenticDevice.cs => Common/Device.cs} (89%) create mode 100644 src/CheckoutSdk/AgenticCommerce/Common/Mandate.cs rename src/CheckoutSdk/AgenticCommerce/Common/{AgenticMandateBase.cs => MandateBase.cs} (95%) rename src/CheckoutSdk/AgenticCommerce/{Responses/Common/AgenticMandateResponse.cs => Common/MandateExtended.cs} (85%) rename src/CheckoutSdk/AgenticCommerce/{Responses => }/Common/PurchaseIntentStatusType.cs (90%) rename src/CheckoutSdk/AgenticCommerce/{Requests/Entities/AgenticSource.cs => Common/Source.cs} (92%) rename src/CheckoutSdk/AgenticCommerce/{Requests/Entities => Common}/TransactionAmount.cs (94%) rename src/CheckoutSdk/AgenticCommerce/{Requests/Entities => Common}/TransactionData.cs (96%) delete mode 100644 src/CheckoutSdk/AgenticCommerce/Requests/Common/AgenticMandateRequest.cs diff --git a/src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticCustomer.cs b/src/CheckoutSdk/AgenticCommerce/Common/Customer.cs similarity index 90% rename from src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticCustomer.cs rename to src/CheckoutSdk/AgenticCommerce/Common/Customer.cs index 937a24da..be345e08 100644 --- a/src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticCustomer.cs +++ b/src/CheckoutSdk/AgenticCommerce/Common/Customer.cs @@ -1,11 +1,11 @@ using Checkout.Common; -namespace Checkout.AgenticCommerce.Requests +namespace Checkout.AgenticCommerce.Common { /// /// The customer's details /// - public class AgenticCustomer + public class Customer { /// /// The customer's email address diff --git a/src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticDevice.cs b/src/CheckoutSdk/AgenticCommerce/Common/Device.cs similarity index 89% rename from src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticDevice.cs rename to src/CheckoutSdk/AgenticCommerce/Common/Device.cs index f54037e8..fb7c18e5 100644 --- a/src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticDevice.cs +++ b/src/CheckoutSdk/AgenticCommerce/Common/Device.cs @@ -1,9 +1,9 @@ -namespace Checkout.AgenticCommerce.Requests +namespace Checkout.AgenticCommerce.Common { /// /// The user's device /// - public class AgenticDevice + public class Device { /// /// The device's IP address diff --git a/src/CheckoutSdk/AgenticCommerce/Common/Mandate.cs b/src/CheckoutSdk/AgenticCommerce/Common/Mandate.cs new file mode 100644 index 00000000..4d98c511 --- /dev/null +++ b/src/CheckoutSdk/AgenticCommerce/Common/Mandate.cs @@ -0,0 +1,9 @@ +namespace Checkout.AgenticCommerce.Common +{ + /// + /// Mandate configuration for purchase intents + /// + public class Mandate : MandateBase + { + } +} \ No newline at end of file diff --git a/src/CheckoutSdk/AgenticCommerce/Common/AgenticMandateBase.cs b/src/CheckoutSdk/AgenticCommerce/Common/MandateBase.cs similarity index 95% rename from src/CheckoutSdk/AgenticCommerce/Common/AgenticMandateBase.cs rename to src/CheckoutSdk/AgenticCommerce/Common/MandateBase.cs index 0ad6e7a7..edb142e3 100644 --- a/src/CheckoutSdk/AgenticCommerce/Common/AgenticMandateBase.cs +++ b/src/CheckoutSdk/AgenticCommerce/Common/MandateBase.cs @@ -5,7 +5,7 @@ namespace Checkout.AgenticCommerce.Common /// /// Mandate configuration for purchase intents /// - public class AgenticMandateBase + public class MandateBase { /// /// Purchase threshold configuration diff --git a/src/CheckoutSdk/AgenticCommerce/Responses/Common/AgenticMandateResponse.cs b/src/CheckoutSdk/AgenticCommerce/Common/MandateExtended.cs similarity index 85% rename from src/CheckoutSdk/AgenticCommerce/Responses/Common/AgenticMandateResponse.cs rename to src/CheckoutSdk/AgenticCommerce/Common/MandateExtended.cs index c907ff69..c88ba9b8 100644 --- a/src/CheckoutSdk/AgenticCommerce/Responses/Common/AgenticMandateResponse.cs +++ b/src/CheckoutSdk/AgenticCommerce/Common/MandateExtended.cs @@ -1,12 +1,11 @@ -using Checkout.AgenticCommerce.Common; using System; -namespace Checkout.AgenticCommerce.Responses.Common +namespace Checkout.AgenticCommerce.Common { /// /// Mandate configuration for purchase intents /// - public class AgenticMandateResponse: AgenticMandateBase + public class MandateExtended: MandateBase { /// /// The unique identifier for the mandate diff --git a/src/CheckoutSdk/AgenticCommerce/Responses/Common/PurchaseIntentStatusType.cs b/src/CheckoutSdk/AgenticCommerce/Common/PurchaseIntentStatusType.cs similarity index 90% rename from src/CheckoutSdk/AgenticCommerce/Responses/Common/PurchaseIntentStatusType.cs rename to src/CheckoutSdk/AgenticCommerce/Common/PurchaseIntentStatusType.cs index 2d502bfc..801d2080 100644 --- a/src/CheckoutSdk/AgenticCommerce/Responses/Common/PurchaseIntentStatusType.cs +++ b/src/CheckoutSdk/AgenticCommerce/Common/PurchaseIntentStatusType.cs @@ -1,6 +1,6 @@ using System.Runtime.Serialization; -namespace Checkout.AgenticCommerce.Responses.Common +namespace Checkout.AgenticCommerce.Common { public enum PurchaseIntentStatusType { diff --git a/src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticSource.cs b/src/CheckoutSdk/AgenticCommerce/Common/Source.cs similarity index 92% rename from src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticSource.cs rename to src/CheckoutSdk/AgenticCommerce/Common/Source.cs index 14dbc487..714e1a35 100644 --- a/src/CheckoutSdk/AgenticCommerce/Requests/Entities/AgenticSource.cs +++ b/src/CheckoutSdk/AgenticCommerce/Common/Source.cs @@ -1,9 +1,9 @@ -namespace Checkout.AgenticCommerce.Requests +namespace Checkout.AgenticCommerce.Common { /// /// The payment source to enroll /// - public class AgenticSource + public class Source { /// /// The full card number, without separators diff --git a/src/CheckoutSdk/AgenticCommerce/Requests/Entities/TransactionAmount.cs b/src/CheckoutSdk/AgenticCommerce/Common/TransactionAmount.cs similarity index 94% rename from src/CheckoutSdk/AgenticCommerce/Requests/Entities/TransactionAmount.cs rename to src/CheckoutSdk/AgenticCommerce/Common/TransactionAmount.cs index ad35469c..e875de9a 100644 --- a/src/CheckoutSdk/AgenticCommerce/Requests/Entities/TransactionAmount.cs +++ b/src/CheckoutSdk/AgenticCommerce/Common/TransactionAmount.cs @@ -1,6 +1,6 @@ using Checkout.Common; -namespace Checkout.AgenticCommerce.Requests +namespace Checkout.AgenticCommerce.Common { /// /// Transaction amount information diff --git a/src/CheckoutSdk/AgenticCommerce/Requests/Entities/TransactionData.cs b/src/CheckoutSdk/AgenticCommerce/Common/TransactionData.cs similarity index 96% rename from src/CheckoutSdk/AgenticCommerce/Requests/Entities/TransactionData.cs rename to src/CheckoutSdk/AgenticCommerce/Common/TransactionData.cs index 0d60ac4c..fca8c8e4 100644 --- a/src/CheckoutSdk/AgenticCommerce/Requests/Entities/TransactionData.cs +++ b/src/CheckoutSdk/AgenticCommerce/Common/TransactionData.cs @@ -1,6 +1,6 @@ using Checkout.Common; -namespace Checkout.AgenticCommerce.Requests +namespace Checkout.AgenticCommerce.Common { /// /// List of transaction data for the purchase diff --git a/src/CheckoutSdk/AgenticCommerce/Requests/Common/AgenticMandateRequest.cs b/src/CheckoutSdk/AgenticCommerce/Requests/Common/AgenticMandateRequest.cs deleted file mode 100644 index 37477402..00000000 --- a/src/CheckoutSdk/AgenticCommerce/Requests/Common/AgenticMandateRequest.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Checkout.AgenticCommerce.Common; - -namespace Checkout.AgenticCommerce.Requests.Common -{ - /// - /// Mandate configuration for purchase intents - /// - public class AgenticMandateRequest : AgenticMandateBase - { - } -} \ No newline at end of file diff --git a/src/CheckoutSdk/AgenticCommerce/Requests/EnrollACardRequest.cs b/src/CheckoutSdk/AgenticCommerce/Requests/EnrollACardRequest.cs index f825fdf0..9ab748fa 100644 --- a/src/CheckoutSdk/AgenticCommerce/Requests/EnrollACardRequest.cs +++ b/src/CheckoutSdk/AgenticCommerce/Requests/EnrollACardRequest.cs @@ -1,3 +1,5 @@ +using Checkout.AgenticCommerce.Common; + namespace Checkout.AgenticCommerce.Requests { /// @@ -8,16 +10,16 @@ public class EnrollACardRequest /// /// The payment source to enroll /// - public AgenticSource Source { get; set; } + public Source Source { get; set; } /// /// The user's device /// - public AgenticDevice Device { get; set; } + public Device Device { get; set; } /// /// The customer's details /// - public AgenticCustomer Customer { get; set; } + public Customer Customer { get; set; } } } \ No newline at end of file diff --git a/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentCreateRequest.cs b/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentCreateRequest.cs index 71667ef2..99409362 100644 --- a/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentCreateRequest.cs +++ b/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentCreateRequest.cs @@ -1,3 +1,5 @@ +using Checkout.AgenticCommerce.Common; + namespace Checkout.AgenticCommerce.Requests { /// @@ -15,6 +17,6 @@ public class PurchaseIntentCreateRequest : PurchaseIntentUpdateRequest /// The user's device /// [Required] /// - public AgenticDevice Device { get; set; } + public Device Device { get; set; } } } diff --git a/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentCredentialsCreateRequest.cs b/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentCredentialsCreateRequest.cs index a1d3697d..c7065f9c 100644 --- a/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentCredentialsCreateRequest.cs +++ b/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentCredentialsCreateRequest.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Checkout.AgenticCommerce.Common; namespace Checkout.AgenticCommerce.Requests { diff --git a/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentUpdateRequest.cs b/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentUpdateRequest.cs index c0147f52..aa9af295 100644 --- a/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentUpdateRequest.cs +++ b/src/CheckoutSdk/AgenticCommerce/Requests/PurchaseIntentUpdateRequest.cs @@ -1,5 +1,5 @@ -using Checkout.AgenticCommerce.Requests.Common; using System.Collections.Generic; +using Checkout.AgenticCommerce.Common; namespace Checkout.AgenticCommerce.Requests { @@ -11,7 +11,7 @@ public class PurchaseIntentUpdateRequest /// /// A list of mandates associated with the purchase intent /// - public IList Mandates { get; set; } + public IList Mandates { get; set; } /// /// A prompt or message for the customer. You can display this during the purchase process to provide additional diff --git a/src/CheckoutSdk/AgenticCommerce/Responses/PurchaseIntentResponse.cs b/src/CheckoutSdk/AgenticCommerce/Responses/PurchaseIntentResponse.cs index 09a59186..266fffcc 100644 --- a/src/CheckoutSdk/AgenticCommerce/Responses/PurchaseIntentResponse.cs +++ b/src/CheckoutSdk/AgenticCommerce/Responses/PurchaseIntentResponse.cs @@ -1,7 +1,6 @@ -using Checkout.AgenticCommerce.Requests; -using Checkout.AgenticCommerce.Responses.Common; -using Checkout.Common; using System.Collections.Generic; +using Checkout.AgenticCommerce.Common; +using Checkout.Common; namespace Checkout.AgenticCommerce.Responses { @@ -33,7 +32,7 @@ public class PurchaseIntentResponse : Resource /// /// The device information of the purchase intent /// - public AgenticDevice DeviceData { get; set; } + public Device DeviceData { get; set; } /// /// The customer prompt of the purchase intent @@ -43,6 +42,6 @@ public class PurchaseIntentResponse : Resource /// /// List of mandates for the purchase intent /// - public IList Mandates { get; set; } + public IList Mandates { get; set; } } } \ No newline at end of file diff --git a/test/CheckoutSdkTest/AgenticCommerce/AgenticClientTest.cs b/test/CheckoutSdkTest/AgenticCommerce/AgenticClientTest.cs index 8f8abd14..7ce42f26 100644 --- a/test/CheckoutSdkTest/AgenticCommerce/AgenticClientTest.cs +++ b/test/CheckoutSdkTest/AgenticCommerce/AgenticClientTest.cs @@ -1,16 +1,15 @@ -using Checkout.AgenticCommerce.Common; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Moq; using Shouldly; using Xunit; + +using Checkout.AgenticCommerce.Common; using Checkout.AgenticCommerce.Requests; -using Checkout.AgenticCommerce.Requests.Common; using Checkout.AgenticCommerce.Responses; -using Checkout.AgenticCommerce.Responses.Common; using Checkout.Common; -using System; namespace Checkout.AgenticCommerce { @@ -36,7 +35,7 @@ private async Task EnrollShouldEnroll() { var agenticEnrollRequest = new EnrollACardRequest { - Source = new AgenticSource + Source = new Source { Number = "4242424242424242", ExpiryMonth = 12, @@ -44,12 +43,12 @@ private async Task EnrollShouldEnroll() Cvv = "123", Type = "Card" }, - Device = new AgenticDevice + Device = new Device { IpAddress = "192.168.1.1", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" }, - Customer = new AgenticCustomer + Customer = new Customer { Email = "test@example.com", CountryCode = CountryCode.US, @@ -95,7 +94,7 @@ private async Task EnrollShouldCallCorrectEnrollEndpoint() { var agenticEnrollRequest = new EnrollACardRequest { - Source = new AgenticSource + Source = new Source { Number = "4242424242424242", ExpiryMonth = 12, @@ -103,12 +102,12 @@ private async Task EnrollShouldCallCorrectEnrollEndpoint() Cvv = "123", Type = "card" }, - Device = new AgenticDevice + Device = new Device { IpAddress = "192.168.1.1", UserAgent = "Mozilla/5.0 Test" }, - Customer = new AgenticCustomer + Customer = new Customer { Email = "test@example.com", CountryCode = CountryCode.US, @@ -135,9 +134,9 @@ private async Task EnrollShouldUseCorrectAuthorizationForEnroll() { var agenticEnrollRequest = new EnrollACardRequest { - Source = new AgenticSource { Type = "card" }, - Device = new AgenticDevice(), - Customer = new AgenticCustomer { Email = "test@example.com", CountryCode = CountryCode.US, LanguageCode = "en" } + Source = new Source { Type = "card" }, + Device = new Device(), + Customer = new Customer { Email = "test@example.com", CountryCode = CountryCode.US, LanguageCode = "en" } }; _apiClient.Setup(apiClient => @@ -160,7 +159,7 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new AgenticDevice + Device = new Device { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", @@ -168,9 +167,9 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() DeviceType = "tablet" }, CustomerPrompt = "I'm looking for running shoes in a size 10, for under $150.", - Mandates = new List + Mandates = new List { - new AgenticMandateRequest + new Mandate { PurchaseThreshold = new PurchaseThreshold { @@ -189,7 +188,7 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() Scheme = "visa", Status = PurchaseIntentStatusType.Created, TokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - DeviceData = new AgenticDevice + DeviceData = new Device { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", @@ -197,9 +196,9 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() DeviceType = "tablet" }, CustomerPrompt = "Hey AI, I need Nike running shoes in size 10 under $130.00", - Mandates = new List + Mandates = new List { - new AgenticMandateResponse + new MandateExtended { Id = "mandate_123", PurchaseThreshold = new PurchaseThreshold @@ -256,15 +255,15 @@ private async Task CreatePurchaseIntentShouldCallCorrectCreatePurchaseIntentEndp var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_test_123", - Device = new AgenticDevice + Device = new Device { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 Test" }, CustomerPrompt = "Test prompt", - Mandates = new List + Mandates = new List { - new AgenticMandateRequest + new Mandate { Description = "Test mandate" } @@ -291,7 +290,7 @@ private async Task CreatePurchaseIntentShouldUseCorrectAuthorizationForCreatePur var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_test_123", - Device = new AgenticDevice(), + Device = new Device(), CustomerPrompt = "Test prompt" }; @@ -338,7 +337,7 @@ private async Task CreatePurchaseIntentCredentialsShouldCreatePurchaseIntentCred Scheme = "visa", Status = PurchaseIntentStatusType.Created, TokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - DeviceData = new AgenticDevice + DeviceData = new Device { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", @@ -348,7 +347,7 @@ private async Task CreatePurchaseIntentCredentialsShouldCreatePurchaseIntentCred CustomerPrompt = "Hey AI, I need Nike running shoes in size 10 under $130.00", Mandates = new[] { - new AgenticMandateResponse + new MandateExtended { Id = "mandate_123", PurchaseThreshold = new PurchaseThreshold @@ -554,9 +553,9 @@ private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() var updatePurchaseIntentRequest = new PurchaseIntentUpdateRequest { CustomerPrompt = "Updated prompt: I'm looking for Nike running shoes in size 10.5, for under $200.", - Mandates = new List() + Mandates = new List() { - new AgenticMandateRequest + new Mandate { PurchaseThreshold = new PurchaseThreshold { @@ -575,7 +574,7 @@ private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() Scheme = "visa", Status = PurchaseIntentStatusType.Active, TokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - DeviceData = new AgenticDevice + DeviceData = new Device { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", @@ -585,7 +584,7 @@ private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() CustomerPrompt = "Updated prompt: I'm looking for Nike running shoes in size 10.5, for under $200.", Mandates = new[] { - new AgenticMandateResponse + new MandateExtended { Id = "mandate_updated_123", PurchaseThreshold = new PurchaseThreshold @@ -631,9 +630,9 @@ private async Task UpdatePurchaseIntentShouldThrowExceptionWhenIdIsNull() var updatePurchaseIntentRequest = new PurchaseIntentUpdateRequest { CustomerPrompt = "Test prompt", - Mandates = new List() + Mandates = new List() { - new AgenticMandateRequest + new Mandate { Description = "Test mandate" } @@ -684,9 +683,9 @@ private async Task UpdatePurchaseIntentShouldCallCorrectEndpoint() var updatePurchaseIntentRequest = new PurchaseIntentUpdateRequest { CustomerPrompt = "Test update prompt", - Mandates = new List() + Mandates = new List() { - new AgenticMandateRequest + new Mandate { PurchaseThreshold = new PurchaseThreshold { @@ -719,9 +718,9 @@ private async Task UpdatePurchaseIntentShouldUseCorrectAuthorization() var updatePurchaseIntentRequest = new PurchaseIntentUpdateRequest { CustomerPrompt = "Auth test prompt", - Mandates = new List + Mandates = new List { - new AgenticMandateRequest + new Mandate { Description = "Auth test mandate", PurchaseThreshold = new PurchaseThreshold diff --git a/test/CheckoutSdkTest/AgenticCommerce/AgenticIntegrationTest.cs b/test/CheckoutSdkTest/AgenticCommerce/AgenticIntegrationTest.cs index edb5b7b3..fbf54cd3 100644 --- a/test/CheckoutSdkTest/AgenticCommerce/AgenticIntegrationTest.cs +++ b/test/CheckoutSdkTest/AgenticCommerce/AgenticIntegrationTest.cs @@ -1,14 +1,13 @@ using System; using System.Net; using System.Threading.Tasks; +using System.Collections.Generic; using Shouldly; using Xunit; + using Checkout.AgenticCommerce.Common; using Checkout.AgenticCommerce.Requests; -using Checkout.AgenticCommerce.Requests.Common; -using Checkout.AgenticCommerce.Responses.Common; using Checkout.Common; -using System.Collections.Generic; namespace Checkout.AgenticCommerce { @@ -23,7 +22,7 @@ private async Task EnrollShouldEnroll() { var agenticEnrollRequest = new EnrollACardRequest { - Source = new AgenticSource + Source = new Source { Number = "4543474002249996", ExpiryMonth = 12, @@ -31,14 +30,14 @@ private async Task EnrollShouldEnroll() Cvv = "100", Type = "card" }, - Device = new AgenticDevice + Device = new Device { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", DeviceBrand = "Chrome", DeviceType = "desktop" }, - Customer = new AgenticCustomer + Customer = new Customer { Email = GenerateRandomEmail(), CountryCode = CountryCode.US, @@ -58,19 +57,19 @@ private async Task EnrollShouldEnrollWithMinimalData() { var agenticEnrollRequest = new EnrollACardRequest { - Source = new AgenticSource + Source = new Source { Number = "4543474002249996", ExpiryMonth = 6, ExpiryYear = 2026, Type = "card" }, - Device = new AgenticDevice + Device = new Device { IpAddress = "10.0.0.1", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", }, - Customer = new AgenticCustomer + Customer = new Customer { Email = GenerateRandomEmail(), CountryCode = CountryCode.US, @@ -89,7 +88,7 @@ private async Task EnrollShouldHandleInternationalCustomers() { var internationalRequest = new EnrollACardRequest { - Source = new AgenticSource + Source = new Source { Number = "4543474002249996", ExpiryMonth = 3, @@ -97,14 +96,14 @@ private async Task EnrollShouldHandleInternationalCustomers() Cvv = "100", Type = "card" }, - Device = new AgenticDevice + Device = new Device { IpAddress = "80.112.12.34", // European IP UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36", DeviceBrand = "Safari", DeviceType = "mobile" }, - Customer = new AgenticCustomer + Customer = new Customer { Email = GenerateRandomEmail(), CountryCode = CountryCode.ES, @@ -124,7 +123,7 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new AgenticDevice + Device = new Device { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", @@ -132,9 +131,9 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntent() DeviceType = "tablet" }, CustomerPrompt = "I'm looking for running shoes in a size 10, for under $150.", - Mandates = new List + Mandates = new List { - new AgenticMandateRequest + new Mandate { PurchaseThreshold = new PurchaseThreshold { @@ -173,7 +172,7 @@ private async Task CreatePurchaseIntentShouldCreatePurchaseIntentWithMinimalData var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new AgenticDevice + Device = new Device { IpAddress = "10.0.0.1" }, @@ -195,7 +194,7 @@ private async Task CreatePurchaseIntentShouldHandleDifferentMandateTypes() var singleMandateRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new AgenticDevice + Device = new Device { IpAddress = "203.0.113.195", UserAgent = "Test Agent", @@ -203,9 +202,9 @@ private async Task CreatePurchaseIntentShouldHandleDifferentMandateTypes() DeviceType = "desktop" }, CustomerPrompt = "Looking for electronics under $500", - Mandates = new List + Mandates = new List { - new AgenticMandateRequest + new Mandate { PurchaseThreshold = new PurchaseThreshold { @@ -227,14 +226,14 @@ private async Task CreatePurchaseIntentShouldHandleDifferentMandateTypes() var multipleMandateRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new AgenticDevice + Device = new Device { IpAddress = "198.51.100.42" }, CustomerPrompt = "Shopping for clothing and accessories", - Mandates = new List + Mandates = new List { - new AgenticMandateRequest + new Mandate { PurchaseThreshold = new PurchaseThreshold { @@ -244,7 +243,7 @@ private async Task CreatePurchaseIntentShouldHandleDifferentMandateTypes() Description = "Clothing purchase", ExpirationDate = DateTime.Parse("2026-06-30T23:59:59.000Z") }, - new AgenticMandateRequest + new Mandate { PurchaseThreshold = new PurchaseThreshold { @@ -269,7 +268,7 @@ private async Task CreatePurchaseIntentShouldHandleInternationalCurrencies() var internationalRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new AgenticDevice + Device = new Device { IpAddress = "80.112.12.34", // European IP UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36", @@ -277,9 +276,9 @@ private async Task CreatePurchaseIntentShouldHandleInternationalCurrencies() DeviceType = "mobile" }, CustomerPrompt = "Buscando zapatos deportivos en talla 42", - Mandates = new List + Mandates = new List { - new AgenticMandateRequest + new Mandate { PurchaseThreshold = new PurchaseThreshold { @@ -308,15 +307,15 @@ private async Task CreatePurchaseIntentCredentialsShouldCreateCredentials() var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new AgenticDevice + Device = new Device { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" }, CustomerPrompt = "I need running shoes in size 10, under $150.", - Mandates = new List + Mandates = new List { - new AgenticMandateRequest + new Mandate { PurchaseThreshold = new PurchaseThreshold { @@ -377,15 +376,15 @@ private async Task CreatePurchaseIntentCredentialsShouldHandleMultipleTransactio var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new AgenticDevice + Device = new Device { IpAddress = "203.0.113.195", UserAgent = "Test Agent Multi Transaction" }, CustomerPrompt = "I need multiple items: electronics and clothing.", - Mandates = new List + Mandates = new List { - new AgenticMandateRequest + new Mandate { PurchaseThreshold = new PurchaseThreshold { @@ -453,15 +452,15 @@ private async Task CreatePurchaseIntentCredentialsShouldHandleInternationalTrans var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new AgenticDevice + Device = new Device { IpAddress = "80.112.12.34", // European IP UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36" }, CustomerPrompt = "Looking for luxury items in different currencies.", - Mandates = new List + Mandates = new List { - new AgenticMandateRequest + new Mandate { PurchaseThreshold = new PurchaseThreshold { @@ -530,7 +529,7 @@ private async Task CreatePurchaseIntentCredentialsShouldHandleMinimalTransaction var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new AgenticDevice + Device = new Device { IpAddress = "10.0.0.1" }, @@ -574,7 +573,7 @@ private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new AgenticDevice + Device = new Device { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", @@ -582,9 +581,9 @@ private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() DeviceType = "tablet" }, CustomerPrompt = "I'm looking for running shoes in a size 10, for under $150.", - Mandates = new List + Mandates = new List { - new AgenticMandateRequest + new Mandate { PurchaseThreshold = new PurchaseThreshold { @@ -606,9 +605,9 @@ private async Task UpdatePurchaseIntentShouldUpdatePurchaseIntent() var updatePurchaseIntentRequest = new PurchaseIntentUpdateRequest { CustomerPrompt = "Updated: I'm looking for Nike running shoes in size 10.5, for under $200.", - Mandates = new List + Mandates = new List { - new AgenticMandateRequest + new Mandate { PurchaseThreshold = new PurchaseThreshold { @@ -673,15 +672,15 @@ private async Task UpdatePurchaseIntentShouldHandlePartialUpdates() var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new AgenticDevice + Device = new Device { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 Test Browser" }, CustomerPrompt = "Original prompt", - Mandates = new List + Mandates = new List { - new AgenticMandateRequest + new Mandate { Description = "Original mandate" } @@ -712,15 +711,15 @@ private async Task UpdatePurchaseIntentShouldHandleDifferentPurchaseIntentStates var createRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new AgenticDevice + Device = new Device { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 State Test" }, CustomerPrompt = "Test different states", - Mandates = new List + Mandates = new List { - new AgenticMandateRequest + new Mandate { PurchaseThreshold = new PurchaseThreshold { @@ -738,9 +737,9 @@ private async Task UpdatePurchaseIntentShouldHandleDifferentPurchaseIntentStates var updateRequest = new PurchaseIntentUpdateRequest { CustomerPrompt = "Updated for state testing", - Mandates = new List + Mandates = new List { - new AgenticMandateRequest + new Mandate { PurchaseThreshold = new PurchaseThreshold { @@ -767,15 +766,15 @@ private async Task DeletePurchaseIntentShouldDeletePurchaseIntent() var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new AgenticDevice + Device = new Device { IpAddress = "192.168.1.100", UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" }, CustomerPrompt = "I need running shoes for deletion test.", - Mandates = new List + Mandates = new List { - new AgenticMandateRequest + new Mandate { PurchaseThreshold = new PurchaseThreshold { @@ -822,15 +821,15 @@ private async Task DeletePurchaseIntentShouldHandleAlreadyDeletedPurchaseIntent( var createPurchaseIntentRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new AgenticDevice + Device = new Device { IpAddress = "203.0.113.195", UserAgent = "Test Agent Double Delete" }, CustomerPrompt = "Purchase intent for double deletion test.", - Mandates = new List + Mandates = new List { - new AgenticMandateRequest + new Mandate { PurchaseThreshold = new PurchaseThreshold { @@ -874,15 +873,15 @@ private async Task DeletePurchaseIntentShouldHandleDifferentPurchaseIntentStates var createRequest = new PurchaseIntentCreateRequest { NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new AgenticDevice + Device = new Device { IpAddress = "10.0.0.1", UserAgent = "Test Agent States" }, CustomerPrompt = testCase.CustomerPrompt, - Mandates = new List + Mandates = new List { - new AgenticMandateRequest + new Mandate { PurchaseThreshold = new PurchaseThreshold { From 27c3cdf211316726f405fbdb5c27a1bc9bec63ef Mon Sep 17 00:00:00 2001 From: david ruiz Date: Fri, 21 Nov 2025 19:43:55 +0100 Subject: [PATCH 16/18] MandateExtended does not need inherited members --- .../AgenticCommerce/Common/MandateExtended.cs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/CheckoutSdk/AgenticCommerce/Common/MandateExtended.cs b/src/CheckoutSdk/AgenticCommerce/Common/MandateExtended.cs index c88ba9b8..ee35ba26 100644 --- a/src/CheckoutSdk/AgenticCommerce/Common/MandateExtended.cs +++ b/src/CheckoutSdk/AgenticCommerce/Common/MandateExtended.cs @@ -12,24 +12,5 @@ public class MandateExtended: MandateBase /// [Required] /// public string Id { get; set; } - - /// - /// Purchase threshold configuration - /// - public PurchaseThreshold PurchaseThreshold { get; set; } - - /// - /// A brief description of the purchase intent - /// <= 255 characters - /// [Required] - /// - public string Description { get; set; } - - /// - /// The date and time when the purchase intent expires, in ISO 8601 format. - /// This value must be set to a date and time in the future - /// [Required] - /// - public DateTime? ExpirationDate { get; set; } } } \ No newline at end of file From 256afafe730a87fd93f2abe79057e520972993e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armando=20Rodr=C3=ADguez?= <127134616+armando-rodriguez-cko@users.noreply.github.com> Date: Mon, 24 Nov 2025 10:48:26 +0100 Subject: [PATCH 17/18] remove unnecessary customer default assertion in InstrumentsIntegrationTest --- test/CheckoutSdkTest/Instruments/InstrumentsIntegrationTest.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/test/CheckoutSdkTest/Instruments/InstrumentsIntegrationTest.cs b/test/CheckoutSdkTest/Instruments/InstrumentsIntegrationTest.cs index abdc840c..61505b42 100644 --- a/test/CheckoutSdkTest/Instruments/InstrumentsIntegrationTest.cs +++ b/test/CheckoutSdkTest/Instruments/InstrumentsIntegrationTest.cs @@ -149,7 +149,6 @@ private async Task ShouldUpdateCardInstrument() cardResponse.Fingerprint.ShouldNotBeNull(); cardResponse.ExpiryMonth.ShouldBe(12); cardResponse.ExpiryYear.ShouldBe(2030); - cardResponse.Customer.Default.ShouldBeTrue(); cardResponse.AccountHolder.FirstName.ShouldBe("John"); cardResponse.AccountHolder.LastName.ShouldBe("Doe"); cardResponse.CardType.ShouldNotBeNull(); From e15d0f07ef56df9c8f5c0bc950c80890c1ce9e9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armando=20Rodr=C3=ADguez?= <127134616+armando-rodriguez-cko@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:15:10 +0100 Subject: [PATCH 18/18] Potential fix for code scanning alert no. 173: Missed opportunity to use Select Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .../AgenticCommerce/AgenticIntegrationTest.cs | 42 +++++++++---------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/test/CheckoutSdkTest/AgenticCommerce/AgenticIntegrationTest.cs b/test/CheckoutSdkTest/AgenticCommerce/AgenticIntegrationTest.cs index fbf54cd3..aca8ec43 100644 --- a/test/CheckoutSdkTest/AgenticCommerce/AgenticIntegrationTest.cs +++ b/test/CheckoutSdkTest/AgenticCommerce/AgenticIntegrationTest.cs @@ -2,6 +2,7 @@ using System.Net; using System.Threading.Tasks; using System.Collections.Generic; +using System.Linq; using Shouldly; using Xunit; @@ -867,33 +868,30 @@ private async Task DeletePurchaseIntentShouldHandleDifferentPurchaseIntentStates new { Description = "Complex purchase intent", CustomerPrompt = "Complex purchase intent with multiple mandates for deletion test" } }; - foreach (var testCase in testCases) + foreach (var createRequest in testCases.Select(testCase => new PurchaseIntentCreateRequest { - // Create purchase intent - var createRequest = new PurchaseIntentCreateRequest + NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", + Device = new Device { - NetworkTokenId = "nt_e7fjr77crbgmlhpjvuq3bj6jba", - Device = new Device - { - IpAddress = "10.0.0.1", - UserAgent = "Test Agent States" - }, - CustomerPrompt = testCase.CustomerPrompt, - Mandates = new List + IpAddress = "10.0.0.1", + UserAgent = "Test Agent States" + }, + CustomerPrompt = testCase.CustomerPrompt, + Mandates = new List + { + new Mandate { - new Mandate + PurchaseThreshold = new PurchaseThreshold { - PurchaseThreshold = new PurchaseThreshold - { - Amount = 7500, - CurrencyCode = Currency.USD - }, - Description = $"Mandate for {testCase.Description}", - ExpirationDate = DateTime.Parse("2026-12-31T23:59:59.000Z") - } + Amount = 7500, + CurrencyCode = Currency.USD + }, + Description = $"Mandate for {testCase.Description}", + ExpirationDate = DateTime.Parse("2026-12-31T23:59:59.000Z") } - }; - + } + })) + { var purchaseIntentResponse = await DefaultApi.AgenticClient().CreatePurchaseIntent(createRequest); purchaseIntentResponse.ShouldNotBeNull(); purchaseIntentResponse.Id.ShouldStartWith("pi_");