diff --git a/.gitignore b/.gitignore index 80bea46..ac2a1cc 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,7 @@ coveragereport/ **/Generated/AuthServerClient.cs* **/Generated/WalletAddressClient.cs* **/Generated/ResourceServerClient.cs* + +.env +.idea/ + diff --git a/.idea/.idea.OpenPayments/.idea/.gitignore b/.idea/.idea.OpenPayments/.idea/.gitignore new file mode 100644 index 0000000..5fdcece --- /dev/null +++ b/.idea/.idea.OpenPayments/.idea/.gitignore @@ -0,0 +1,15 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/modules.xml +/.idea.OpenPayments.iml +/contentModel.xml +/projectSettingsUpdater.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/.idea.OpenPayments/.idea/.name b/.idea/.idea.OpenPayments/.idea/.name new file mode 100644 index 0000000..c93e51a --- /dev/null +++ b/.idea/.idea.OpenPayments/.idea/.name @@ -0,0 +1 @@ +OpenPayments \ No newline at end of file diff --git a/.idea/.idea.OpenPayments/.idea/encodings.xml b/.idea/.idea.OpenPayments/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.OpenPayments/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.OpenPayments/.idea/indexLayout.xml b/.idea/.idea.OpenPayments/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.OpenPayments/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.OpenPayments/.idea/inspectionProfiles/Project_Default.xml b/.idea/.idea.OpenPayments/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..67139c5 --- /dev/null +++ b/.idea/.idea.OpenPayments/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.OpenPayments/.idea/vcs.xml b/.idea/.idea.OpenPayments/.idea/vcs.xml new file mode 100644 index 0000000..870877b --- /dev/null +++ b/.idea/.idea.OpenPayments/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Makefile b/Makefile index 964520d..f14db67 100644 --- a/Makefile +++ b/Makefile @@ -2,21 +2,21 @@ auth-server-generate: npx swagger-cli bundle open-payments-specifications/openapi/auth-server.yaml -o OpenPayments.Sdk/tmp/auth-bundled.json -t json && \ - nswag openapi2csclient /input:OpenPayments.Sdk/tmp/auth-bundled.json /output:OpenPayments.Sdk/Generated/AuthServerClient.cs /namespace:OpenPayments.Sdk.Generated.Auth /classname:AuthServerClient /injectHttpClient:true && \ + nswag openapi2csclient /input:OpenPayments.Sdk/tmp/auth-bundled.json /output:OpenPayments.Sdk/Generated/Auth/AuthServerClient.g.cs /namespace:OpenPayments.Sdk.Generated.Auth /classname:AuthServerClient /injectHttpClient:true && \ rm -rf OpenPayments.Sdk/tmp/auth-bundled.json as-models: auth-server-generate resource-server-generate: npx swagger-cli bundle open-payments-specifications/openapi/resource-server.yaml -o OpenPayments.Sdk/tmp/resource-bundled.json -t json && \ - nswag openapi2csclient /input:OpenPayments.Sdk/tmp/resource-bundled.json /output:OpenPayments.Sdk/Generated/ResourceServerClient.cs /namespace:OpenPayments.Sdk.Generated.Resource /classname:ResourceServerClient /injectHttpClient:true && \ + nswag openapi2csclient /input:OpenPayments.Sdk/tmp/resource-bundled.json /output:OpenPayments.Sdk/Generated/Resource/ResourceServerClient.g.cs /namespace:OpenPayments.Sdk.Generated.Resource /classname:ResourceServerClient /injectHttpClient:true && \ rm -rf OpenPayments.Sdk/tmp/resource-bundled.json rs-models: resource-server-generate wallet-address-models: npx swagger-cli bundle open-payments-specifications/openapi/wallet-address-server.yaml -o OpenPayments.Sdk/tmp/wallet-bundled.json -t json && \ - nswag openapi2csclient /input:OpenPayments.Sdk/tmp/wallet-bundled.json /output:OpenPayments.Sdk/Generated/WalletAddressClient.cs /namespace:OpenPayments.Sdk.Generated.Wallet /classname:WalletAddressClient /injectHttpClient:true && \ + nswag openapi2csclient /input:OpenPayments.Sdk/tmp/wallet-bundled.json /output:OpenPayments.Sdk/Generated/Wallet/WalletAddressClient.g.cs /namespace:OpenPayments.Sdk.Generated.Wallet /classname:WalletAddressClient /injectHttpClient:true && \ rm -rf OpenPayments.Sdk/tmp/wallet-bundled.json wa-models: wallet-address-models diff --git a/OpenPayments.Sdk.HttpSignatureUtils/Extensions/HttpClientSignatureExtensions.cs b/OpenPayments.Sdk.HttpSignatureUtils/Extensions/HttpClientSignatureExtensions.cs index aa50b84..ed6d35a 100644 --- a/OpenPayments.Sdk.HttpSignatureUtils/Extensions/HttpClientSignatureExtensions.cs +++ b/OpenPayments.Sdk.HttpSignatureUtils/Extensions/HttpClientSignatureExtensions.cs @@ -1,5 +1,9 @@ using OpenPayments.Sdk.HttpSignatureUtils; +/// +/// Provides extension methods for configuring and adding signature-based authentication +/// to instances of . +/// public static class HttpClientSignatureExtensions { // public static async Task ValidateRequestSignatureAsync( diff --git a/OpenPayments.Sdk.HttpSignatureUtils/HttpRequestSigner.cs b/OpenPayments.Sdk.HttpSignatureUtils/HttpRequestSigner.cs index accdd17..320f900 100644 --- a/OpenPayments.Sdk.HttpSignatureUtils/HttpRequestSigner.cs +++ b/OpenPayments.Sdk.HttpSignatureUtils/HttpRequestSigner.cs @@ -5,26 +5,39 @@ [assembly: InternalsVisibleTo("OpenPayments.Sdk.HttpSignatureUtils.Tests")] -internal class SignatureHeaders +/// +/// Signature headers returned by the HttpRequestSigner. +/// + +public class SignatureHeaders { + /// + /// Signature header value. + /// public string Signature { get; set; } = string.Empty; + /// + /// Signature input header value. + /// public string SignatureInput { get; set; } = string.Empty; } -internal static class HttpRequestSigner +/// +/// Signs an HTTP request using the Ed25519 signature algorithm. +/// +public static class HttpRequestSigner { private static string BuildSignatureInput(List components, string keyId, long created) { - string fields = string.Join(" ", components.Select(h => $"\"{h}\"")); - return $"({fields});created={created};keyid=\"{keyId}\""; + var fields = string.Join(" ", components.Select(h => $"\"{h}\"")); + return $"({fields});created={created};keyid=\"{keyId}\";alg=\"ed25519\""; } - + private static string ComputeContentDigest(string body) { - byte[] hash = SHA256.HashData(Encoding.UTF8.GetBytes(body)); + var hash = SHA512.HashData(Encoding.UTF8.GetBytes(body)); return Convert.ToBase64String(hash); } - + private static async Task TryGetHeaderValueAsync(HttpRequestMessage request, string name) { name = name.ToLowerInvariant(); @@ -41,14 +54,15 @@ private static async Task TryGetHeaderValueAsync(HttpRequestMessage requ if (name == "content-digest" && request.Content != null) { - string body = await request.Content.ReadAsStringAsync(); - return $"sha-256=:{ComputeContentDigest(body)}:"; + var body = await request.Content.ReadAsStringAsync(); + return $"sha-512=:{ComputeContentDigest(body)}:"; } return ""; } - private static async Task BuildSignatureBaseAsync(HttpRequestMessage request, List components, long created, string keyId) + private static async Task BuildSignatureBaseAsync(HttpRequestMessage request, List components, + long created, string keyId) { var lines = new List(); @@ -57,25 +71,34 @@ private static async Task BuildSignatureBaseAsync(HttpRequestMessage req switch (component) { case "@method": - lines.Add($"@method: {request.Method.Method.ToLower()}"); + lines.Add($"\"@method\": {request.Method.Method.ToUpper()}"); break; case "@target-uri": - lines.Add($"@target-uri: {request.RequestUri}"); + lines.Add($"\"@target-uri\": {request.RequestUri}"); break; default: - string value = await TryGetHeaderValueAsync(request, component); - lines.Add($"{component.ToLower()}: {value}"); + var value = await TryGetHeaderValueAsync(request, component); + lines.Add($"\"{component.ToLower()}\": {value}"); break; } } - string fieldList = string.Join(" ", components.Select(c => $"\"{c}\"")); - lines.Add($"\"@signature-params\": ({fieldList});created={created};keyid=\"{keyId}\""); + var fieldList = string.Join(" ", components.Select(c => $"\"{c}\"")); + lines.Add($"\"@signature-params\": ({fieldList});created={created};keyid=\"{keyId}\";alg=\"ed25519\""); return string.Join("\n", lines); } - public static async Task SignHttpRequestAsync(HttpRequestMessage request, Key privateKey, string keyId) + /// + /// Signs an HTTP request using the Ed25519 signature algorithm. + /// + /// + /// + /// + /// + /// + public static async Task SignHttpRequestAsync(HttpRequestMessage request, Key privateKey, + string keyId) { ArgumentNullException.ThrowIfNull(request); ArgumentNullException.ThrowIfNull(privateKey); @@ -91,15 +114,20 @@ public static async Task SignHttpRequestAsync(HttpRequestMessa if (request.Content != null) { - string content = await request.Content.ReadAsStringAsync(); + var content = await request.Content.ReadAsStringAsync(); if (!string.IsNullOrEmpty(content)) { components.AddRange(["content-digest", "content-length", "content-type"]); - string digest = ComputeContentDigest(content); + var digest = ComputeContentDigest(content); - request.Content.Headers.TryAddWithoutValidation("Content-Digest", $"sha-256=:{digest}:"); - request.Content.Headers.TryAddWithoutValidation("Content-Length", Encoding.UTF8.GetByteCount(content).ToString()); + request.Content.Headers.TryAddWithoutValidation("Content-Digest", $"sha-512=:{digest}:"); + + if (!request.Content.Headers.Contains("Content-Length")) + { + request.Content.Headers.TryAddWithoutValidation("Content-Length", + Encoding.UTF8.GetByteCount(content).ToString()); + } if (!request.Content.Headers.Contains("Content-Type")) { @@ -108,12 +136,11 @@ public static async Task SignHttpRequestAsync(HttpRequestMessa } } - long created = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); - string signatureInput = BuildSignatureInput(components, keyId, created); - string signatureBase = await BuildSignatureBaseAsync(request, components, created, keyId); - - byte[] signatureBytes = SignatureAlgorithm.Ed25519.Sign(privateKey, Encoding.UTF8.GetBytes(signatureBase)); - string base64Signature = Convert.ToBase64String(signatureBytes); + var created = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + var signatureInput = BuildSignatureInput(components, keyId, created); + var signatureBase = await BuildSignatureBaseAsync(request, components, created, keyId); + var signatureBytes = SignatureAlgorithm.Ed25519.Sign(privateKey, Encoding.UTF8.GetBytes(signatureBase)); + var base64Signature = Convert.ToBase64String(signatureBytes); return new SignatureHeaders { diff --git a/OpenPayments.Sdk.HttpSignatureUtils/KeyUtils.cs b/OpenPayments.Sdk.HttpSignatureUtils/KeyUtils.cs index 6b2d629..1fa2397 100644 --- a/OpenPayments.Sdk.HttpSignatureUtils/KeyUtils.cs +++ b/OpenPayments.Sdk.HttpSignatureUtils/KeyUtils.cs @@ -1,6 +1,9 @@ using System.Diagnostics; using NSec.Cryptography; using System.Security.Cryptography; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.OpenSsl; namespace OpenPayments.Sdk.HttpSignatureUtils; @@ -29,7 +32,7 @@ public static class KeyUtils /// public static Key LoadBase64Key(string base64Key) { - byte[] keyBytes = Convert.FromBase64String(base64Key); + var keyBytes = Convert.FromBase64String(base64Key); if (keyBytes.Length != 32 && keyBytes.Length != 64) { @@ -41,6 +44,53 @@ public static Key LoadBase64Key(string base64Key) return Key.Import(algorithm, seed, KeyBlobFormat.RawPrivateKey); } + /// + /// Loads an Ed25519 private key from a PEM-encoded PKCS#8 string. + /// + /// + /// + /// + public static Key LoadPem(string pem) + { + // Read PEM -> DER object + using var sr = new StringReader(pem); + var pemObj = new PemReader(sr).ReadPemObject(); + if (pemObj == null) + throw new ArgumentException("Invalid PEM"); + + // Parse PKCS#8 PrivateKeyInfo + var privInfo = PrivateKeyInfo.GetInstance(Asn1Object.FromByteArray(pemObj.Content)); + + // Ensure Ed25519 OID (1.3.101.112) + var oid = privInfo.PrivateKeyAlgorithm.Algorithm.Id; + if (oid != "1.3.101.112") + throw new ArgumentException($"Unexpected algorithm OID: {oid}. Expected Ed25519 (1.3.101.112)."); + + // Extract the inner OCTET STRING (seed). For Ed25519 in PKCS#8, this is 32 bytes. + var privateKeyOctets = Asn1OctetString.GetInstance(privInfo.ParsePrivateKey()); + var privateKeyBytes = privateKeyOctets.GetOctets(); + + // Some toolchains wrap the seed in another OCTET STRING layer: + // If length is not 32, try one more unwrap. + if (privateKeyBytes.Length != 32) + { + try + { + var inner = Asn1OctetString.GetInstance(Asn1Object.FromByteArray(privateKeyBytes)); + privateKeyBytes = inner.GetOctets(); + } + catch { /* ignore */ } + } + + if (privateKeyBytes.Length != 32) + throw new ArgumentException($"Ed25519 seed must be 32 bytes, got {privateKeyBytes.Length}."); + + // Import into NSec as raw private key (seed) + var algorithm = SignatureAlgorithm.Ed25519; + var seed = privateKeyBytes.AsSpan(0, 32); + return Key.Import(algorithm, seed, KeyBlobFormat.RawPrivateKey); + } + /// /// Generates a JSON Web Key (JWK) representing an Ed25519 public key. /// @@ -71,7 +121,7 @@ public static Jwk GenerateJwk(string keyId, Key? privateKey = null) ExportPolicy = KeyExportPolicies.AllowPlaintextExport }); - byte[] publicKeyBytes = privateKey.PublicKey.Export(KeyBlobFormat.RawPublicKey); + var publicKeyBytes = privateKey.PublicKey.Export(KeyBlobFormat.RawPublicKey); return new Jwk { @@ -105,7 +155,7 @@ public static Key LoadKey(string keyFilePath) throw new ArgumentException("File was loaded, but key was not a valid Ed25519 private key (must be 32 or 64 bytes)."); } - byte[] seed = keyBytes.AsSpan(0, 32).ToArray(); + var seed = keyBytes.AsSpan(0, 32).ToArray(); try { @@ -132,9 +182,8 @@ public static Key GenerateKey(GenerateKeyArgs? args = null) if (args?.Dir is not null) { Directory.CreateDirectory(args.Dir); - string fileName = args.Filename ?? $"private-key-{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}.pem"; - - string path = Path.Combine(args.Dir, fileName); + var fileName = args.Filename ?? $"private-key-{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}.pem"; + var path = Path.Combine(args.Dir, fileName); key.ToPem(path); } diff --git a/OpenPayments.Sdk.Tests/Clients/AuthenticatedClient_Collection.cs b/OpenPayments.Sdk.Tests/Clients/AuthenticatedClient_Collection.cs new file mode 100644 index 0000000..6c1ee19 --- /dev/null +++ b/OpenPayments.Sdk.Tests/Clients/AuthenticatedClient_Collection.cs @@ -0,0 +1,6 @@ +namespace OpenPayments.Sdk.Tests.Clients; + +[CollectionDefinition("AuthenticatedClient")] +public class AuthenticatedClientCollection : ICollectionFixture { + +} \ No newline at end of file diff --git a/OpenPayments.Sdk.Tests/Clients/AuthenticatedClient_Fixture.cs b/OpenPayments.Sdk.Tests/Clients/AuthenticatedClient_Fixture.cs new file mode 100644 index 0000000..8ccc5fe --- /dev/null +++ b/OpenPayments.Sdk.Tests/Clients/AuthenticatedClient_Fixture.cs @@ -0,0 +1,191 @@ +using System.Net; +using System.Runtime.InteropServices.JavaScript; +using System.Text; +using Moq; +using Moq.Protected; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using NSec.Cryptography; +using OpenPayments.Sdk.Clients; +using OpenPayments.Sdk.Generated.Auth; +using OpenPayments.Sdk.Generated.Resource; +using Amount = OpenPayments.Sdk.Generated.Resource.Amount; + +namespace OpenPayments.Sdk.Tests.Clients; + +public class AuthenticatedClientFixture +{ + public string BaseUrl => "https://example.com"; + public Key PrivateKey => Key.Create(SignatureAlgorithm.Ed25519); + public string KeyId => "1234"; + public Uri ClientUrl => new(BaseUrl); + + public RequestArgs RequestGrantArgs => new() + { + Url = new Uri("https://example.com/auth") + }; + + public AuthRequestArgs GrantWithTokenArgs => new() + { + Url = new Uri("https://example.com/auth"), + AccessToken = "1234" + }; + + public GrantCreateBody RequestGrantBody => new() + { + AccessToken = new AccessToken() + { + Access = + [ + new AccessItem() + { + Type = AccessType.IncomingPayment, + Actions = [Actions.Create, Actions.Read, Actions.List, Actions.Complete] + } + ] + } + }; + + public GrantContinueBody ContinueGrantBody => new() + { + InteractRef = "1231323" + }; + + public AuthResponse ApprovedGrantResponse => new() + { + AccessToken = new AccessToken() + { + Access = + [ + new AccessItem() + { + Type = AccessType.IncomingPayment, + Actions = [Actions.Create, Actions.Read, Actions.List, Actions.Complete], + } + ] + }, + Continue = new AuthContinue() + { + AccessToken = new Access_token2() + { + Value = "D8616A2FBC790B1CE132", + }, + Uri = new Uri(BaseUrl + "/continue/12345") + } + }; + + public RotateTokenResponse TokenResponse = new() + { + AccessToken = new AccessToken() + { + Access = + [ + new AccessItem() + { + Type = AccessType.IncomingPayment, + Actions = [Actions.Create, Actions.Read, Actions.List, Actions.Complete], + } + ] + } + }; + + public IncomingPaymentBody CreateIncomingPaymentBody = new() + { + WalletAddress = new Uri("https://example.com/wallet/1234"), + IncomingAmount = new Amount("1000", "EUR", 2) + }; + + public IncomingPaymentResponse CreateIncomingPaymentResponse = new() + { + Id = new Uri("https://example.com/incoming/1234"), + WalletAddress = new Uri("https://example.com/wallet/1234"), + IncomingAmount = new Amount("100", "EUR", 2), + ReceivedAmount = new Amount("0", "EUR", 2), + Completed = false, + CreatedAt = DateTime.UtcNow, + ExpiresAt = DateTime.UtcNow.AddDays(1), + Methods = [new IlpPaymentMethod() + { + Type = IlpPaymentMethodType.Ilp, + IlpAddress = "example.com/incoming/1234", + SharedSecret = "secret1234" + }], + Metadata = new JObject + { + ["description"] = "Free Money" + }, + }; + + public QuoteBody CreateQuoteBody = new() + { + WalletAddress = new Uri("https://example.com/wallet/1234"), + Receiver = new Uri("https://example.com/incoming/1234"), + Method = PaymentMethod.Ilp, + }; + + public QuoteResponse CreateQuoteResponse = new() + { + Id = new Uri("https://example.com/quote/1234"), + WalletAddress = new Uri("https://example.com/wallet/1234"), + ReceiveAmount = new Amount("100", "EUR"), + DebitAmount = new Amount("200", "EUR"), + Receiver = new Uri("https://example.com/incoming/1234"), + ExpiresAt = "", + CreatedAt = DateTime.UtcNow, + Method = PaymentMethod.Ilp, + }; + + public OutgoingPaymentBody CreateOutgoingPaymentBody = new() + { + WalletAddress = new Uri("https://example.com/wallet/1234"), + QuoteId = new Uri("https://example.com/quote/1234"), + Metadata = new JObject + { + ["description"] = "Free Money" + } + }; + + public OutgoingPaymentResponse CreateOutgoingPaymentResponse = new() + { + Id = new Uri("https://example.com/outgoing/1234"), + WalletAddress = new Uri("https://example.com/wallet/1234"), + QuoteId = new Uri("https://example.com/quote/1234"), + ReceiveAmount = new Amount("100", "EUR"), + DebitAmount = new Amount("200", "EUR"), + SentAmount = new Amount("0", "EUR"), + Receiver = new Uri("https://example.com/incoming/1234"), + Failed = false, + Metadata = new JObject() + { + ["description"] = "Free Money" + }, + CreatedAt = DateTime.UtcNow, + GrantSpentReceiveAmount = new Amount("0", "EUR"), + GrantSpentDebitAmount = new Amount("0", "EUR") + }; + + public HttpClient CreateHttpClientMock(object? responseObject = null, HttpStatusCode? code = null) + { + var statusCode = code ?? (responseObject == null ? HttpStatusCode.NoContent : HttpStatusCode.OK); + + var handler = new Mock(); + handler + .Protected() + .Setup>("SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny()) + .ReturnsAsync(new HttpResponseMessage + { + StatusCode = statusCode, + Content = responseObject == null + ? new StringContent("", Encoding.UTF8) + : new StringContent(JsonConvert.SerializeObject(responseObject), Encoding.UTF8, + "application/json") + }); + + return new HttpClient(handler.Object) + { + BaseAddress = new Uri(BaseUrl) + }; + } +} \ No newline at end of file diff --git a/OpenPayments.Sdk.Tests/Clients/AuthenticatedClient_Tests.cs b/OpenPayments.Sdk.Tests/Clients/AuthenticatedClient_Tests.cs new file mode 100644 index 0000000..f8733d2 --- /dev/null +++ b/OpenPayments.Sdk.Tests/Clients/AuthenticatedClient_Tests.cs @@ -0,0 +1,200 @@ +using System.Net; +using FluentAssertions; +using OpenPayments.Sdk.Clients; + +namespace OpenPayments.Sdk.Tests.Clients; + +public class AuthenticatedClient_Tests +{ + [Collection("AuthenticatedClient")] + public class AuthenticatedClient_RequestGrant_Tests + { + private readonly AuthenticatedClient _client; + private readonly AuthenticatedClientFixture _fixture; + + public AuthenticatedClient_RequestGrant_Tests(AuthenticatedClientFixture fixture) + { + _fixture = fixture; + var httpClient = _fixture.CreateHttpClientMock(_fixture.ApprovedGrantResponse); + _client = new AuthenticatedClient(httpClient, _fixture.PrivateKey, _fixture.KeyId, _fixture.ClientUrl); + } + + [Fact] + public async Task RequestGrantAsync_ReturnsModel() + { + var result = await _client.RequestGrantAsync( + _fixture.RequestGrantArgs, + _fixture.RequestGrantBody + ); + + result.Should().NotBeNull(); + result.Should().BeEquivalentTo(_fixture.ApprovedGrantResponse); + } + } + + [Collection("AuthenticatedClient")] + public class AuthenticatedClient_ContinueGrant_Tests + { + private readonly AuthenticatedClient _client; + private readonly AuthenticatedClientFixture _fixture; + + public AuthenticatedClient_ContinueGrant_Tests(AuthenticatedClientFixture fixture) + { + _fixture = fixture; + var httpClient = _fixture.CreateHttpClientMock(_fixture.ApprovedGrantResponse); + _client = new AuthenticatedClient(httpClient, _fixture.PrivateKey, _fixture.KeyId, _fixture.ClientUrl); + } + + [Fact] + public async Task ContinueGrantAsync_ReturnsModel() + { + var result = await _client.ContinueGrantAsync( + _fixture.GrantWithTokenArgs, + _fixture.ContinueGrantBody + ); + + result.Should().NotBeNull(); + result.Should().BeEquivalentTo(_fixture.ApprovedGrantResponse); + } + } + + [Collection("AuthenticatedClient")] + public class AuthenticatedClient_CancelGrant_Tests + { + private readonly AuthenticatedClient _client; + private readonly AuthenticatedClientFixture _fixture; + + public AuthenticatedClient_CancelGrant_Tests(AuthenticatedClientFixture fixture) + { + _fixture = fixture; + var httpClient = _fixture.CreateHttpClientMock(); + _client = new AuthenticatedClient(httpClient, _fixture.PrivateKey, _fixture.KeyId, _fixture.ClientUrl); + } + + [Fact] + public async Task CancelGrantAsync_ReturnsModel() + { + await _client.CancelGrantAsync(_fixture.GrantWithTokenArgs); + } + } + + [Collection("AuthenticatedClient")] + public class AuthenticatedClient_RotateToken_Tests + { + private readonly AuthenticatedClient _client; + private readonly AuthenticatedClientFixture _fixture; + + public AuthenticatedClient_RotateToken_Tests(AuthenticatedClientFixture fixture) + { + _fixture = fixture; + var httpClient = _fixture.CreateHttpClientMock(_fixture.TokenResponse); + _client = new AuthenticatedClient(httpClient, _fixture.PrivateKey, _fixture.KeyId, _fixture.ClientUrl); + } + + [Fact] + public async Task RotateTokenAsync_ReturnsModel() + { + var result = await _client.RotateTokenAsync(_fixture.GrantWithTokenArgs); + result.Should().NotBeNull(); + result.Should().BeEquivalentTo(_fixture.TokenResponse); + } + } + + [Collection("AuthenticatedClient")] + public class AuthenticatedClient_RevokeToken_Tests + { + private readonly AuthenticatedClient _client; + private readonly AuthenticatedClientFixture _fixture; + + public AuthenticatedClient_RevokeToken_Tests(AuthenticatedClientFixture fixture) + { + _fixture = fixture; + var httpClient = _fixture.CreateHttpClientMock(); + _client = new AuthenticatedClient(httpClient, _fixture.PrivateKey, _fixture.KeyId, _fixture.ClientUrl); + } + + [Fact] + public async Task RevokeTokenAsync_ReturnsModel() + { + await _client.RevokeTokenAsync(_fixture.GrantWithTokenArgs); + } + } + + [Collection("AuthenticatedClient")] + public class AuthenticatedClient_CreateIncomingPayment_Tests + { + private readonly AuthenticatedClient _client; + private readonly AuthenticatedClientFixture _fixture; + + public AuthenticatedClient_CreateIncomingPayment_Tests(AuthenticatedClientFixture fixture) + { + _fixture = fixture; + var httpClient = + _fixture.CreateHttpClientMock(_fixture.CreateIncomingPaymentResponse, HttpStatusCode.Created); + _client = new AuthenticatedClient(httpClient, _fixture.PrivateKey, _fixture.KeyId, _fixture.ClientUrl); + } + + [Fact] + public async Task CreateIncomingPaymentAsync_ReturnsModel() + { + var result = await _client.CreateIncomingPaymentAsync( + _fixture.GrantWithTokenArgs, + _fixture.CreateIncomingPaymentBody + ); + result.Should().NotBeNull(); + result.Should().BeEquivalentTo(_fixture.CreateIncomingPaymentResponse); + } + } + + [Collection("AuthenticatedClient")] + public class AuthenticatedClient_CreateQuote_Tests + { + private readonly AuthenticatedClient _client; + private readonly AuthenticatedClientFixture _fixture; + + public AuthenticatedClient_CreateQuote_Tests(AuthenticatedClientFixture fixture) + { + _fixture = fixture; + var httpClient = + _fixture.CreateHttpClientMock(_fixture.CreateQuoteResponse, HttpStatusCode.Created); + _client = new AuthenticatedClient(httpClient, _fixture.PrivateKey, _fixture.KeyId, _fixture.ClientUrl); + } + + [Fact] + public async Task CreateQuoteAsync_ReturnsModel() + { + var result = await _client.CreateQuoteAsync( + _fixture.GrantWithTokenArgs, + _fixture.CreateQuoteBody + ); + result.Should().NotBeNull(); + result.Should().BeEquivalentTo(_fixture.CreateQuoteResponse); + } + } + + [Collection("AuthenticatedClient")] + public class AuthenticatedClient_CreateOutgoingPayment_Tests + { + private readonly AuthenticatedClient _client; + private readonly AuthenticatedClientFixture _fixture; + + public AuthenticatedClient_CreateOutgoingPayment_Tests(AuthenticatedClientFixture fixture) + { + _fixture = fixture; + var httpClient = + _fixture.CreateHttpClientMock(_fixture.CreateOutgoingPaymentResponse, HttpStatusCode.Created); + _client = new AuthenticatedClient(httpClient, _fixture.PrivateKey, _fixture.KeyId, _fixture.ClientUrl); + } + + [Fact] + public async Task CreateOutgoingPaymentAsync_ReturnsModel() + { + var result = await _client.CreateOutgoingPaymentAsync( + _fixture.GrantWithTokenArgs, + _fixture.CreateOutgoingPaymentBody + ); + result.Should().NotBeNull(); + result.Should().BeEquivalentTo(_fixture.CreateOutgoingPaymentResponse); + } + } +} \ No newline at end of file diff --git a/OpenPayments.Sdk.Tests/Extensions/ServiceCollecitonExtenions_Tests.cs b/OpenPayments.Sdk.Tests/Extensions/ServiceCollecitonExtenions_Tests.cs index d5606f8..18f958f 100644 --- a/OpenPayments.Sdk.Tests/Extensions/ServiceCollecitonExtenions_Tests.cs +++ b/OpenPayments.Sdk.Tests/Extensions/ServiceCollecitonExtenions_Tests.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using NSec.Cryptography; using OpenPayments.Sdk.Clients; using OpenPayments.Sdk.Extensions; @@ -11,10 +12,7 @@ public void UseOpenPayments_WithUseUnauthenticatedClient_RegistersServices() { var services = new ServiceCollection(); - services.UseOpenPayments(options => - { - options.UseUnauthenticatedClient = true; - }); + services.UseOpenPayments(options => { options.UseUnauthenticatedClient = true; }); var provider = services.BuildServiceProvider(); var client = provider.GetService(); @@ -34,6 +32,51 @@ public void UseOpenPayments_WithoutUseUnauthenticatedClient_DoesNotRegisterClien Assert.Null(client); } + [Fact] + public void UseOpenPayments_WithUseAuthenticatedClient_RegistersServices() + { + var services = new ServiceCollection(); + + services.UseOpenPayments(options => + { + options.UseAuthenticatedClient = true; + options.ClientUrl = new Uri("https://example.com"); + options.KeyId = "1234"; + options.PrivateKey = Key.Create(SignatureAlgorithm.Ed25519); + }); + var provider = services.BuildServiceProvider(); + + var client = provider.GetService(); + Assert.NotNull(client); + Assert.IsType(client); + } + + [Fact] + public void UseOpenPayments_AuthenticatedClient_WithoutOptions_ThrowsException() + { + var services = new ServiceCollection(); + + services.UseOpenPayments(options => + { + options.UseAuthenticatedClient = true; + }); + var provider = services.BuildServiceProvider(); + + Assert.Throws(() => provider.GetService()); + } + + [Fact] + public void UseOpenPayments_WithoutUseAuthenticatedClient_DoesNotRegistersService() + { + var services = new ServiceCollection(); + + services.UseOpenPayments(options => { options.UseAuthenticatedClient = false; }); + var provider = services.BuildServiceProvider(); + + var client = provider.GetService(); + Assert.Null(client); + } + [Fact] public void UseOpenPayments_Always_RegistersHttpClient() { diff --git a/OpenPayments.Sdk/Clients/AuthClientBase.cs b/OpenPayments.Sdk/Clients/AuthClientBase.cs new file mode 100644 index 0000000..836f450 --- /dev/null +++ b/OpenPayments.Sdk/Clients/AuthClientBase.cs @@ -0,0 +1,89 @@ +using System.Globalization; +using NSec.Cryptography; +using OpenPayments.Sdk.Generated.Auth; + +namespace OpenPayments.Sdk.Clients; + +public class AuthClientBase : IAuthClientBase +{ + private readonly AuthServerClient _client; + private readonly HttpClient _httpClient; + + public AuthClientBase(HttpClient http, Key privateKey, string keyId, Uri clientUrl) + { + _httpClient = http; + _client = new AuthServerClient(http); + _client.AddSigningKey(privateKey, keyId); + _client.ClientUrl = clientUrl; + } + + public async Task RequestGrantAsync(RequestArgs requestArgs, + GrantCreateBody body, + CancellationToken cancellationToken = default) + { + _client.BaseUrl = requestArgs.Url.ToString(); + body.Client = _client.ClientUrl; + + return await _client + .CreateGrantAsync( + body + , cancellationToken) + .ConfigureAwait(false); + } + + public async Task ContinueGrantAsync(AuthRequestArgs requestArgs, + GrantContinueBody body, + CancellationToken cancellationToken = default) + { + return await _client + .ContinueGrantAsync( + requestArgs.Url, + requestArgs.AccessToken, + body, + cancellationToken) + .ConfigureAwait(false); + } + + public async Task CancelGrantAsync(AuthRequestArgs requestArgs, + CancellationToken cancellationToken) + { + await _client + .CancelGrantAsync( + requestArgs.Url, + requestArgs.AccessToken, + cancellationToken) + .ConfigureAwait(false); + } + + public async Task + RotateTokenAsync(AuthRequestArgs requestArgs, CancellationToken cancellationToken) + { + return await _client + .RotateTokenAsync(requestArgs.Url, requestArgs.AccessToken!, cancellationToken).ConfigureAwait(false); + } + + public async Task RevokeTokenAsync(AuthRequestArgs requestArgs, + CancellationToken cancellationToken = default) + { + await _client.RevokeTokenAsync(requestArgs.Url, requestArgs.AccessToken!, cancellationToken) + .ConfigureAwait(false); + } +} + +public interface IAuthClientBase +{ + public Task RequestGrantAsync(RequestArgs requestArgs, + GrantCreateBody body, + CancellationToken cancellationToken = default); + + public Task ContinueGrantAsync(AuthRequestArgs requestArgs, + GrantContinueBody body, + CancellationToken cancellationToken = default); + + public Task CancelGrantAsync(AuthRequestArgs requestArgs, CancellationToken cancellationToken); + + public Task RotateTokenAsync(AuthRequestArgs requestArgs, CancellationToken cancellationToken); + + public Task RevokeTokenAsync(AuthRequestArgs requestArgs, + CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/OpenPayments.Sdk/Clients/AuthenticatedClient.cs b/OpenPayments.Sdk/Clients/AuthenticatedClient.cs new file mode 100644 index 0000000..4d74fb8 --- /dev/null +++ b/OpenPayments.Sdk/Clients/AuthenticatedClient.cs @@ -0,0 +1,81 @@ +using NSec.Cryptography; +using OpenPayments.Sdk.Generated.Auth; +using OpenPayments.Sdk.Generated.Resource; + +namespace OpenPayments.Sdk.Clients; + +/// +/// Create a new AuthenticatedClient wrapping an existing instance. +/// +/// Pre-configured instance. It's is ignored; absolute request URIs are used instead. +/// Private key used to sign requests. +/// Key ID used to sign requests. +/// Client Wallet URL Address (e.g. https://wallet.example). +internal sealed class AuthenticatedClient(HttpClient http, Key privateKey, string keyId, Uri clientUrl) + : UnauthenticatedClient(http), IAuthenticatedClient +{ + private readonly IAuthClientBase _authClient = new AuthClientBase(http, privateKey, keyId, clientUrl); + private readonly IResourceClientBase _resClient = new ResourceClientBase(http, privateKey, keyId, clientUrl); + + public Task RequestGrantAsync(RequestArgs requestArgs, + GrantCreateBody body, + CancellationToken cancellationToken = default) + { + return _authClient.RequestGrantAsync(requestArgs, body, cancellationToken); + } + + public Task ContinueGrantAsync(AuthRequestArgs requestArgs, + GrantContinueBody? body, + CancellationToken cancellationToken = default) + { + body ??= new GrantContinueBody(); + return _authClient.ContinueGrantAsync(requestArgs, body, cancellationToken); + } + + public Task CancelGrantAsync(AuthRequestArgs requestArgs, + CancellationToken cancellationToken = default) + { + return _authClient.CancelGrantAsync(requestArgs, cancellationToken); + } + + public Task RotateTokenAsync(AuthRequestArgs requestArgs, + CancellationToken cancellationToken = default) + { + return _authClient.RotateTokenAsync(requestArgs, cancellationToken); + } + + public Task RevokeTokenAsync(AuthRequestArgs requestArgs, + CancellationToken cancellationToken = default) + { + return _authClient.RevokeTokenAsync(requestArgs, cancellationToken); + } + + public Task CreateIncomingPaymentAsync(AuthRequestArgs requestArgs, IncomingPaymentBody body, + CancellationToken cancellationToken = default) + { + return _resClient.CreateIncomingPaymentAsync(requestArgs, body, cancellationToken); + } + + public Task CreateQuoteAsync(AuthRequestArgs requestArgs, QuoteBody body, + CancellationToken cancellationToken = default) + { + return _resClient.CreateQuoteAsync(requestArgs, body, cancellationToken); + } + + public Task CreateOutgoingPaymentAsync(AuthRequestArgs requestArgs, OutgoingPaymentBody body, + CancellationToken cancellationToken = default) + { + return _resClient.CreateOutgoingPaymentAsync(requestArgs, body, cancellationToken); + } +} + +public class RequestArgs +{ + public required Uri Url { get; set; } +} + +public class AuthRequestArgs : RequestArgs +{ + public required string AccessToken { get; set; } +} + diff --git a/OpenPayments.Sdk/Clients/IAuthenticatedClient.cs b/OpenPayments.Sdk/Clients/IAuthenticatedClient.cs new file mode 100644 index 0000000..c71541e --- /dev/null +++ b/OpenPayments.Sdk/Clients/IAuthenticatedClient.cs @@ -0,0 +1,41 @@ +using OpenPayments.Sdk.Generated.Auth; +using OpenPayments.Sdk.Generated.Resource; + +namespace OpenPayments.Sdk.Clients; + +/// +/// Represents a client used to interact with Open Payments endpoints +/// that require authentication. +/// +public interface IAuthenticatedClient : IUnauthenticatedClient +{ + /// + /// Resolve a wallet-address URL (or payment pointer) and return its public metadata. + /// + /// Auth Server URL Address (e.g. https://auth.wallet.example) and access token. + /// Request body + /// Optional cancellation token. + public Task RequestGrantAsync(RequestArgs requestArgs, + GrantCreateBody body, + CancellationToken cancellationToken = default); + + public Task ContinueGrantAsync(AuthRequestArgs requestArgs, + GrantContinueBody? body = null, + CancellationToken cancellationToken = default); + + public Task CancelGrantAsync(AuthRequestArgs requestArgs, + CancellationToken cancellationToken = default); + + public Task RotateTokenAsync(AuthRequestArgs requestArgs, CancellationToken cancellationToken = default); + + public Task RevokeTokenAsync(AuthRequestArgs requestArgs, CancellationToken cancellationToken = default); + + public Task CreateIncomingPaymentAsync(AuthRequestArgs requestArgs, + IncomingPaymentBody body, CancellationToken cancellationToken = default); + + public Task CreateQuoteAsync(AuthRequestArgs requestArgs, QuoteBody body, + CancellationToken cancellationToken = default); + + public Task CreateOutgoingPaymentAsync(AuthRequestArgs requestArgs, OutgoingPaymentBody body, + CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/OpenPayments.Sdk/Clients/ResourceClientBase.cs b/OpenPayments.Sdk/Clients/ResourceClientBase.cs new file mode 100644 index 0000000..56e93bf --- /dev/null +++ b/OpenPayments.Sdk/Clients/ResourceClientBase.cs @@ -0,0 +1,54 @@ +using NSec.Cryptography; +using OpenPayments.Sdk.Generated.Resource; + +namespace OpenPayments.Sdk.Clients; + +public class ResourceClientBase : IResourceClientBase +{ + private readonly ResourceServerClient _client; + private readonly HttpClient _httpClient; + + public ResourceClientBase(HttpClient http, Key privateKey, string keyId, Uri clientUrl) + { + _httpClient = http; + _client = new ResourceServerClient(http); + _client.AddSigningKey(privateKey, keyId); + _client.ClientUrl = clientUrl; + } + + public async Task CreateIncomingPaymentAsync(AuthRequestArgs requestArgs, Body body, + CancellationToken cancellationToken = default) + { + _client.BaseUrl = requestArgs.Url.ToString(); + + return await _client.PostIncomingPaymentAsync(body, requestArgs.AccessToken!, cancellationToken); + } + + public async Task CreateQuoteAsync(AuthRequestArgs requestArgs, QuoteBody body, + CancellationToken cancellationToken = default) + { + _client.BaseUrl = requestArgs.Url.ToString(); + + return await _client.PostQuoteAsync(body, requestArgs.AccessToken!, cancellationToken); + } + + public async Task CreateOutgoingPaymentAsync(AuthRequestArgs requestArgs, + OutgoingPaymentBody body, CancellationToken cancellationToken = default) + { + _client.BaseUrl = requestArgs.Url.ToString(); + + return await _client.PostOutgoingPaymentAsync(body, requestArgs.AccessToken!, cancellationToken); + } +} + +public interface IResourceClientBase +{ + public Task CreateIncomingPaymentAsync(AuthRequestArgs requestArgs, Body body, + CancellationToken cancellationToken = default); + + public Task CreateQuoteAsync(AuthRequestArgs requestArgs, QuoteBody body, + CancellationToken cancellationToken = default); + + public Task CreateOutgoingPaymentAsync(AuthRequestArgs requestArgs, OutgoingPaymentBody body, + CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/OpenPayments.Sdk/Clients/UnauthenticatedClient.cs b/OpenPayments.Sdk/Clients/UnauthenticatedClient.cs index 73440ff..6a13818 100644 --- a/OpenPayments.Sdk/Clients/UnauthenticatedClient.cs +++ b/OpenPayments.Sdk/Clients/UnauthenticatedClient.cs @@ -12,7 +12,7 @@ namespace OpenPayments.Sdk.Clients; /// Create a new UnauthenticatedClient wrapping an existing . /// /// Pre-configured instance. Its is ignored; absolute request URIs are used instead. -internal sealed class UnauthenticatedClient(HttpClient http) : WalletAddressClientBase(http), IUnauthenticatedClient +internal class UnauthenticatedClient(HttpClient http) : WalletAddressClientBase(http), IUnauthenticatedClient { /// public async Task GetWalletAddressAsync(string walletAddressOrPaymentPointer, CancellationToken cancellationToken = default) diff --git a/OpenPayments.Sdk/Configuration/OpenPaymentsOptions.cs b/OpenPayments.Sdk/Configuration/OpenPaymentsOptions.cs index 616c401..ad8a6ff 100644 --- a/OpenPayments.Sdk/Configuration/OpenPaymentsOptions.cs +++ b/OpenPayments.Sdk/Configuration/OpenPaymentsOptions.cs @@ -1,3 +1,4 @@ +using NSec.Cryptography; using OpenPayments.Sdk.Clients; using OpenPayments.Sdk.Extensions; @@ -15,4 +16,12 @@ public class OpenPaymentsOptions /// Indicates whether the should be registered. /// public bool UseUnauthenticatedClient { get; set; } + + public bool UseAuthenticatedClient { get; set; } + + public string? KeyId { get; set; } + + public Key? PrivateKey { get; set; } + + public Uri? ClientUrl { get; set; } } \ No newline at end of file diff --git a/OpenPayments.Sdk/Extensions/ServiceCollectionExtensions.cs b/OpenPayments.Sdk/Extensions/ServiceCollectionExtensions.cs index c4ab931..25bba7e 100644 --- a/OpenPayments.Sdk/Extensions/ServiceCollectionExtensions.cs +++ b/OpenPayments.Sdk/Extensions/ServiceCollectionExtensions.cs @@ -18,12 +18,14 @@ public static class ServiceCollectionExtensions /// allowing selection between authenticated or unauthenticated client. /// /// The updated instance. - public static IServiceCollection UseOpenPayments(this IServiceCollection services, Action configure) + public static IServiceCollection UseOpenPayments(this IServiceCollection services, + Action configure) { var options = new OpenPaymentsOptions(); configure(options); services.AddHttpClient(); + if (options.UseUnauthenticatedClient) { services.AddSingleton(); @@ -31,7 +33,24 @@ public static IServiceCollection UseOpenPayments(this IServiceCollection service sp.GetRequiredService()); } + if (options.UseAuthenticatedClient) + { + services.AddSingleton(sp => + { + if (string.IsNullOrWhiteSpace(options.KeyId)) + throw new InvalidOperationException("OpenPaymentsOptions.KeyId must be provided."); + if (options.PrivateKey is null) + throw new InvalidOperationException("OpenPaymentsOptions.PrivateKey must be provided."); + if (options.ClientUrl is null) + throw new InvalidOperationException("OpenPaymentsOptions.ClientUrl must be provided."); + + var http = sp.GetRequiredService().CreateClient("authenticated"); + return new AuthenticatedClient(http, options.PrivateKey, options.KeyId, options.ClientUrl); + }); + services.AddSingleton(sp => + sp.GetRequiredService()); + } + return services; } -} - \ No newline at end of file +} \ No newline at end of file diff --git a/OpenPayments.Sdk/Generated/Auth/AuthContractResolver.cs b/OpenPayments.Sdk/Generated/Auth/AuthContractResolver.cs new file mode 100644 index 0000000..5679d28 --- /dev/null +++ b/OpenPayments.Sdk/Generated/Auth/AuthContractResolver.cs @@ -0,0 +1,41 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace OpenPayments.Sdk.Generated.Auth; + +public sealed class AuthContractResolver : DefaultContractResolver +{ + private readonly Dictionary> _exclusions = new Dictionary>(); + + public AuthContractResolver() + { + // this.SetProp(typeof(Response), "Interact"); + } + + private void SetProp(Type targetType, params string[] propertyNames) + { + if (!_exclusions.ContainsKey(targetType)) + { + _exclusions.Add(targetType, new HashSet(propertyNames, StringComparer.OrdinalIgnoreCase)); + } + + _exclusions[targetType].UnionWith(propertyNames); + } + + protected override JsonProperty CreateProperty(System.Reflection.MemberInfo member, + MemberSerialization memberSerialization) + { + var prop = base.CreateProperty(member, memberSerialization); + // + // if (prop.DeclaringType == null || !this._exclusions.ContainsKey(prop.DeclaringType) || + // !this._exclusions[prop.DeclaringType].Contains(prop.PropertyName!)) return prop; + + // Neutralize Required.Always + prop.Required = Required.Default; + // Also avoid throwing on missing/null + prop.NullValueHandling = NullValueHandling.Ignore; + // prop.DefaultValueHandling = DefaultValueHandling.Ignore; + + return prop; + } +} \ No newline at end of file diff --git a/OpenPayments.Sdk/Generated/Auth/AuthServerClient.Auth.cs b/OpenPayments.Sdk/Generated/Auth/AuthServerClient.Auth.cs new file mode 100644 index 0000000..1c51003 --- /dev/null +++ b/OpenPayments.Sdk/Generated/Auth/AuthServerClient.Auth.cs @@ -0,0 +1,32 @@ +using Newtonsoft.Json; +using NSec.Cryptography; + +namespace OpenPayments.Sdk.Generated.Auth; + +public partial class AuthServerClient +{ + private Key? _privateKey; + private string? _keyId; + public Uri ClientUrl { get; set; } + + public void AddSigningKey(Key privateKey, string keyId) + { + _privateKey = privateKey; + _keyId = keyId; + } + + static partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings) + { + settings.ContractResolver = new AuthContractResolver(); + } + + partial void PrepareRequest(HttpClient client, HttpRequestMessage request, + string url) + { + if (_privateKey == null || _keyId == null) return; + + var headers = HttpRequestSigner.SignHttpRequestAsync(request, _privateKey, _keyId).Result; + request.Headers.TryAddWithoutValidation("Signature", headers.Signature); + request.Headers.TryAddWithoutValidation("Signature-Input", headers.SignatureInput); + } +} \ No newline at end of file diff --git a/OpenPayments.Sdk/Generated/Auth/AuthServerClient.Methods.Grant.cs b/OpenPayments.Sdk/Generated/Auth/AuthServerClient.Methods.Grant.cs new file mode 100644 index 0000000..ba8060f --- /dev/null +++ b/OpenPayments.Sdk/Generated/Auth/AuthServerClient.Methods.Grant.cs @@ -0,0 +1,269 @@ +using System.Net.Http.Headers; +using Newtonsoft.Json; + +namespace OpenPayments.Sdk.Generated.Auth; + +public partial class AuthServerClient +{ + /// Body for grant request. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Grant Request + /// + /// + /// Make a new grant request + /// + /// OK + /// A server side error occurred. + public async Task CreateGrantAsync(GrantCreateBody body, + CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(body); + + var client = _httpClient; + using var request = new HttpRequestMessage(); + var json = JsonConvert.SerializeObject(body, JsonSerializerSettings); + var content = new StringContent(json); + content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); + request.Content = content; + request.Method = new HttpMethod("POST"); + request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); + var urlBuilder = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder.Append(_baseUrl); + + PrepareRequest(client, request, urlBuilder); + + var url = urlBuilder.ToString(); + request.RequestUri = new Uri(url, UriKind.RelativeOrAbsolute); + + PrepareRequest(client, request, url); + + var response = await client + .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken) + .ConfigureAwait(false); + + try + { + var headers = Helpers.ExtractHeaders(response); + + ProcessResponse(client, response); + + var status = (int)response.StatusCode; + + switch (status) + { + case 200: + { + var objectResponse = + await ReadObjectResponseAsync(response, headers, cancellationToken) + .ConfigureAwait(false); + if (objectResponse.Object == null) + { + throw new ApiException("Response was null which was not expected.", status, + objectResponse.Text, headers, null); + } + + return objectResponse.Object; + } + case 400 or 401 or 500: + { + var objectResponse = + await ReadObjectResponseAsync(response, headers, + cancellationToken).ConfigureAwait(false); + if (objectResponse.Object == null) + { + throw new ApiException("Response was null which was not expected.", status, + objectResponse.Text, headers, null); + } + + throw new ApiException(Helpers.StatusAsText(status), status, objectResponse.Text, + headers, objectResponse.Object, null); + } + default: + { + var responseData = + await ReadAsStringAsync(response.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException( + "The HTTP status code of the response was not expected (" + status + ").", status, + responseData, headers, null); + } + } + } + finally + { + response.Dispose(); + } + } + + /// Url for grant continuation. + /// Access Token for continuation. + /// Body for continuation. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Continuation Request + /// + /// + /// Continue a grant request during or after user interaction. + /// + /// Success + /// A server side error occurred. + public async Task ContinueGrantAsync(Uri continueUrl, + string accessToken, GrantContinueBody body, CancellationToken cancellationToken) + { + ArgumentException.ThrowIfNullOrWhiteSpace(continueUrl.ToString()); + ArgumentException.ThrowIfNullOrWhiteSpace(accessToken); + + var client = _httpClient; + using var request = new HttpRequestMessage(); + var json = JsonConvert.SerializeObject(body, JsonSerializerSettings); + var content = new StringContent(json); + content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); + request.Content = content; + request.Method = new HttpMethod("POST"); + request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); + request.Headers.Authorization = new AuthenticationHeaderValue("GNAP", $"{accessToken}"); + + var urlBuilder = new System.Text.StringBuilder(continueUrl.ToString()); + + PrepareRequest(client, request, urlBuilder); + + var url = urlBuilder.ToString(); + request.RequestUri = new Uri(url, UriKind.RelativeOrAbsolute); + + PrepareRequest(client, request, url); + + var response = await client + .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken) + .ConfigureAwait(false); + + try + { + var headers = Helpers.ExtractHeaders(response); + + ProcessResponse(client, response); + + var status = (int)response.StatusCode; + switch (status) + { + case 200: + { + + var objectResponse = + await ReadObjectResponseAsync(response, headers, cancellationToken) + .ConfigureAwait(false); + if (objectResponse.Object == null) + { + throw new ApiException("Response was null which was not expected.", status, + objectResponse.Text, headers, null); + } + + return objectResponse.Object; + } + case 400 or 401 or 404: + { + var objectResponse = + await ReadObjectResponseAsync(response, headers, + cancellationToken).ConfigureAwait(false); + if (objectResponse.Object == null) + { + throw new ApiException("Response was null which was not expected.", status, + objectResponse.Text, headers, null); + } + + throw new ApiException(Helpers.StatusAsText(status), status, objectResponse.Text, + headers, objectResponse.Object, null); + } + default: + { + var responseData = + await ReadAsStringAsync(response.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException( + "The HTTP status code of the response was not expected (" + status + ").", status, + responseData, headers, null); + } + } + } + finally + { + response.Dispose(); + } + } + + /// Continue URL. + /// Access Token. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Cancel Grant + /// + /// + /// Cancel a grant request or delete a grant client side. + /// + /// No Content + /// A server side error occurred. + public async Task CancelGrantAsync( + Uri continueUrl, string accessToken, + CancellationToken cancellationToken) + { + ArgumentException.ThrowIfNullOrWhiteSpace(continueUrl.ToString()); + ArgumentException.ThrowIfNullOrWhiteSpace(accessToken); + + var client = _httpClient; + + using var request = new HttpRequestMessage(); + request.Method = new HttpMethod("DELETE"); + request.Headers.Authorization = new AuthenticationHeaderValue("GNAP", $"{accessToken}"); + + var urlBuilder = new System.Text.StringBuilder(continueUrl.ToString()); + + PrepareRequest(client, request, urlBuilder); + + var url = urlBuilder.ToString(); + request.RequestUri = new Uri(url, UriKind.RelativeOrAbsolute); + + PrepareRequest(client, request, url); + + var response = await client.SendAsync(request, + HttpCompletionOption.ResponseHeadersRead, cancellationToken) + .ConfigureAwait(false); + + try + { + var headers = Helpers.ExtractHeaders(response); + + ProcessResponse(client, response); + + var status = (int)response.StatusCode; + switch (status) + { + case 204: + return; + case 401 or 404: + { + var objectResponse = + await ReadObjectResponseAsync(response, headers, + cancellationToken).ConfigureAwait(false); + if (objectResponse.Object == null) + { + throw new ApiException("Response was null which was not expected.", status, + objectResponse.Text, headers, null); + } + + throw new ApiException(Helpers.StatusAsText(status), status, objectResponse.Text, + headers, objectResponse.Object, null); + } + default: + { + var responseData = + await ReadAsStringAsync(response.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException( + "The HTTP status code of the response was not expected (" + status + ").", status, + responseData, headers, null); + } + } + } + finally + { + response.Dispose(); + } + } +} \ No newline at end of file diff --git a/OpenPayments.Sdk/Generated/Auth/AuthServerClient.Methods.Token.cs b/OpenPayments.Sdk/Generated/Auth/AuthServerClient.Methods.Token.cs new file mode 100644 index 0000000..626b12e --- /dev/null +++ b/OpenPayments.Sdk/Generated/Auth/AuthServerClient.Methods.Token.cs @@ -0,0 +1,171 @@ +using System.Net.Http.Headers; + +namespace OpenPayments.Sdk.Generated.Auth; + +public partial class AuthServerClient +{ + /// Token Url for rotation. + /// Access Token for rotation. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Rotate Access Token + /// + /// + /// Management endpoint to rotate access token. + /// + /// OK + /// A server side error occurred. + public async Task RotateTokenAsync(Uri tokenUrl, string accessToken, CancellationToken cancellationToken) + { + ArgumentException.ThrowIfNullOrWhiteSpace(tokenUrl.ToString()); + ArgumentException.ThrowIfNullOrWhiteSpace(accessToken); + + var client = _httpClient; + using var request = new HttpRequestMessage(); + request.Content = new StringContent(string.Empty, System.Text.Encoding.UTF8, "application/json"); + request.Method = new HttpMethod("POST"); + request.Headers.Authorization = new AuthenticationHeaderValue("GNAP", $"{accessToken}"); + request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); + var urlBuilder = new System.Text.StringBuilder(tokenUrl.ToString()); + + PrepareRequest(client, request, urlBuilder); + + var url = urlBuilder.ToString(); + request.RequestUri = new Uri(url, UriKind.RelativeOrAbsolute); + + PrepareRequest(client, request, url); + + var response = await client + .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken) + .ConfigureAwait(false); + + try + { + var headers = Helpers.ExtractHeaders(response); + + ProcessResponse(client, response); + + var status = (int)response.StatusCode; + switch (status) + { + case 200: + { + var objectResponse = + await ReadObjectResponseAsync(response, headers, cancellationToken) + .ConfigureAwait(false); + if (objectResponse.Object == null) + { + throw new ApiException("Response was null which was not expected.", status, + objectResponse.Text, headers, null); + } + + return objectResponse.Object; + } + case 400: + case 401: + case 404: + case 500: + { + var objectResponse = + await ReadObjectResponseAsync(response, headers, cancellationToken) + .ConfigureAwait(false); + if (objectResponse.Object == null) + { + throw new ApiException("Response was null which was not expected.", status, + objectResponse.Text, headers, null); + } + + throw new ApiException(Helpers.StatusAsText(status), status, + objectResponse.Text, + headers, objectResponse.Object, null); + } + default: + { + var responseData = + await ReadAsStringAsync(response.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException( + "The HTTP status code of the response was not expected (" + status + ").", status, + responseData, headers, null); + } + } + } + finally + { + response.Dispose(); + } + } + + /// Token Url for revocation. + /// Access Token for revocation. + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Revoke Access Token + /// + /// + /// Management endpoint to revoke access token. + /// + /// No Content + /// A server side error occurred. + public async Task RevokeTokenAsync(Uri tokenUrl, string accessToken, CancellationToken cancellationToken) + { + ArgumentException.ThrowIfNullOrWhiteSpace(tokenUrl.ToString(), nameof(tokenUrl)); + ArgumentException.ThrowIfNullOrWhiteSpace(accessToken); + + var client = _httpClient; + using var request = new HttpRequestMessage(); + request.Method = new HttpMethod("DELETE"); + request.Headers.Authorization = new AuthenticationHeaderValue("GNAP", $"{accessToken}"); + var urlBuilder = new System.Text.StringBuilder(tokenUrl.ToString()); + + PrepareRequest(client, request, urlBuilder); + + var url = urlBuilder.ToString(); + request.RequestUri = new Uri(url, UriKind.RelativeOrAbsolute); + + PrepareRequest(client, request, url); + + var response = await client + .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken) + .ConfigureAwait(false); + + try + { + var headers = Helpers.ExtractHeaders(response); + + ProcessResponse(client, response); + + var status = (int)response.StatusCode; + switch (status) + { + case 204: + return; + case 401: + case 500: + { + var objectResponse = + await ReadObjectResponseAsync(response, headers, cancellationToken) + .ConfigureAwait(false); + if (objectResponse.Object == null) + { + throw new ApiException("Response was null which was not expected.", status, + objectResponse.Text, headers, null); + } + + throw new ApiException(Helpers.StatusAsText(status), status, + objectResponse.Text, headers, objectResponse.Object, null); + } + default: + { + var responseData = await ReadAsStringAsync(response.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException( + "The HTTP status code of the response was not expected (" + status + ").", status, + responseData, headers, null); + } + } + } + finally + { + response.Dispose(); + } + } +} \ No newline at end of file diff --git a/OpenPayments.Sdk/Generated/Auth/AuthServerClient.g.cs b/OpenPayments.Sdk/Generated/Auth/AuthServerClient.g.cs new file mode 100644 index 0000000..f83b4eb --- /dev/null +++ b/OpenPayments.Sdk/Generated/Auth/AuthServerClient.g.cs @@ -0,0 +1,1735 @@ +//---------------------- +// +// Generated using the NSwag toolchain v14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org) +// +//---------------------- + +#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended." +#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword." +#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?' +#pragma warning disable 612 // Disable "CS0612 '...' is obsolete" +#pragma warning disable 649 // Disable "CS0649 Field is never assigned to, and will always have its default value null" +#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ... +#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..." +#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'" +#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant" +#pragma warning disable 8600 // Disable "CS8600 Converting null literal or possible null value to non-nullable type" +#pragma warning disable 8602 // Disable "CS8602 Dereference of a possibly null reference" +#pragma warning disable 8603 // Disable "CS8603 Possible null reference return" +#pragma warning disable 8604 // Disable "CS8604 Possible null reference argument for parameter" +#pragma warning disable 8625 // Disable "CS8625 Cannot convert null literal to non-nullable reference type" +#pragma warning disable 8765 // Disable "CS8765 Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes)." + +namespace OpenPayments.Sdk.Generated.Auth +{ + using System = global::System; + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class AuthServerClient + { + #pragma warning disable 8618 + private string _baseUrl; + #pragma warning restore 8618 + + private System.Net.Http.HttpClient _httpClient; + private static System.Lazy _settings = new System.Lazy(CreateSerializerSettings, true); + private Newtonsoft.Json.JsonSerializerSettings _instanceSettings; + + #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + public AuthServerClient(System.Net.Http.HttpClient httpClient) + #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + { + BaseUrl = "https://auth.interledger-test.dev"; + _httpClient = httpClient; + Initialize(); + } + + private static Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings() + { + var settings = new Newtonsoft.Json.JsonSerializerSettings(); + UpdateJsonSerializerSettings(settings); + return settings; + } + + public string BaseUrl + { + get { return _baseUrl; } + set + { + _baseUrl = value; + if (!string.IsNullOrEmpty(_baseUrl) && !_baseUrl.EndsWith("/")) + _baseUrl += '/'; + } + } + + protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _instanceSettings ?? _settings.Value; } } + + static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); + + partial void Initialize(); + + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); + partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); + + /// + /// Grant Request + /// + /// + /// Make a new grant request + /// + /// OK + /// A server side error occurred. + public virtual System.Threading.Tasks.Task PostRequestAsync(Body body) + { + return PostRequestAsync(body, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Grant Request + /// + /// + /// Make a new grant request + /// + /// OK + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task PostRequestAsync(Body body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(body, JsonSerializerSettings); + var content_ = new System.Net.Http.StringContent(json_); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "" + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 500) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Internal Server Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// + /// Continuation Request + /// + /// + /// Continue a grant request during or after user interaction. + /// + /// Success + /// A server side error occurred. + public virtual System.Threading.Tasks.Task PostContinueAsync(Body2 body, string id) + { + return PostContinueAsync(body, id, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Continuation Request + /// + /// + /// Continue a grant request during or after user interaction. + /// + /// Success + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task PostContinueAsync(Body2 body, string id, System.Threading.CancellationToken cancellationToken) + { + if (id == null) + throw new System.ArgumentNullException("id"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(body, JsonSerializerSettings); + var content_ = new System.Net.Http.StringContent(json_); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "continue/{id}" + urlBuilder_.Append("continue/"); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not Found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// + /// Cancel Grant + /// + /// + /// Cancel a grant request or delete a grant client side. + /// + /// No Content + /// A server side error occurred. + public virtual System.Threading.Tasks.Task DeleteContinueAsync(string id) + { + return DeleteContinueAsync(id, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Cancel Grant + /// + /// + /// Cancel a grant request or delete a grant client side. + /// + /// No Content + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task DeleteContinueAsync(string id, System.Threading.CancellationToken cancellationToken) + { + if (id == null) + throw new System.ArgumentNullException("id"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("DELETE"); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "continue/{id}" + urlBuilder_.Append("continue/"); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 204) + { + return; + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not Found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// + /// Rotate Access Token + /// + /// + /// Management endpoint to rotate access token. + /// + /// OK + /// A server side error occurred. + public virtual System.Threading.Tasks.Task PostTokenAsync(string id) + { + return PostTokenAsync(id, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Rotate Access Token + /// + /// + /// Management endpoint to rotate access token. + /// + /// OK + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task PostTokenAsync(string id, System.Threading.CancellationToken cancellationToken) + { + if (id == null) + throw new System.ArgumentNullException("id"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Content = new System.Net.Http.StringContent(string.Empty, System.Text.Encoding.UTF8, "application/json"); + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "token/{id}" + urlBuilder_.Append("token/"); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not Found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 500) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Internal Server Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// + /// Revoke Access Token + /// + /// + /// Management endpoint to revoke access token. + /// + /// No Content + /// A server side error occurred. + public virtual System.Threading.Tasks.Task DeleteTokenAsync(string id) + { + return DeleteTokenAsync(id, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Revoke Access Token + /// + /// + /// Management endpoint to revoke access token. + /// + /// No Content + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task DeleteTokenAsync(string id, System.Threading.CancellationToken cancellationToken) + { + if (id == null) + throw new System.ArgumentNullException("id"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("DELETE"); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "token/{id}" + urlBuilder_.Append("token/"); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 204) + { + return; + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unauthorized", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 500) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Internal Server Error", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + protected struct ObjectResponseResult + { + public ObjectResponseResult(T responseObject, string responseText) + { + this.Object = responseObject; + this.Text = responseText; + } + + public T Object { get; } + + public string Text { get; } + } + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private static System.Threading.Tasks.Task ReadAsStringAsync(System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) + { + #if NET5_0_OR_GREATER + return content.ReadAsStringAsync(cancellationToken); + #else + return content.ReadAsStringAsync(); + #endif + } + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private static System.Threading.Tasks.Task ReadAsStreamAsync(System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) + { + #if NET5_0_OR_GREATER + return content.ReadAsStreamAsync(cancellationToken); + #else + return content.ReadAsStreamAsync(); + #endif + } + + public bool ReadResponseAsString { get; set; } + + protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) + { + if (response == null || response.Content == null) + { + return new ObjectResponseResult(default(T), string.Empty); + } + + if (ReadResponseAsString) + { + var responseText = await ReadAsStringAsync(response.Content, cancellationToken).ConfigureAwait(false); + try + { + var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject(responseText, JsonSerializerSettings); + return new ObjectResponseResult(typedBody, responseText); + } + catch (Newtonsoft.Json.JsonException exception) + { + var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; + throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception); + } + } + else + { + try + { + using (var responseStream = await ReadAsStreamAsync(response.Content, cancellationToken).ConfigureAwait(false)) + using (var streamReader = new System.IO.StreamReader(responseStream)) + using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader)) + { + var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings); + var typedBody = serializer.Deserialize(jsonTextReader); + return new ObjectResponseResult(typedBody, string.Empty); + } + } + catch (Newtonsoft.Json.JsonException exception) + { + var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; + throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception); + } + } + } + + private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo) + { + if (value == null) + { + return ""; + } + + if (value is System.Enum) + { + var name = System.Enum.GetName(value.GetType(), value); + if (name != null) + { + var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); + if (field != null) + { + var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute)) + as System.Runtime.Serialization.EnumMemberAttribute; + if (attribute != null) + { + return attribute.Value != null ? attribute.Value : name; + } + } + + var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); + return converted == null ? string.Empty : converted; + } + } + else if (value is bool) + { + return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); + } + else if (value is byte[]) + { + return System.Convert.ToBase64String((byte[]) value); + } + else if (value is string[]) + { + return string.Join(",", (string[])value); + } + else if (value.GetType().IsArray) + { + var valueArray = (System.Array)value; + var valueTextArray = new string[valueArray.Length]; + for (var i = 0; i < valueArray.Length; i++) + { + valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo); + } + return string.Join(",", valueTextArray); + } + + var result = System.Convert.ToString(value, cultureInfo); + return result == null ? "" : result; + } + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Amount + { + + /// + /// The value is an unsigned 64-bit integer amount, represented as a string. + /// + [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Value { get; set; } + + /// + /// The assetCode is a code that indicates the underlying asset. An ISO4217 currency code should be used whenever possible. The ISO4217 representation of the US Dollar is USD. + /// + [Newtonsoft.Json.JsonProperty("assetCode", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string AssetCode { get; set; } + + /// + /// The number of decimal places that defines the scale of the smallest divisible unit for the given asset code. It determines how an integer amount is scaled to derive the actual monetary value. For example, USD has an asset scale of 2 with the smallest unit being 0.01. An integer amount of `1000` with an `assetCode` of `USD` and `assetScale` of `2` translates to $10.00. + /// + [Newtonsoft.Json.JsonProperty("assetScale", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Range(0, 255)] + public int AssetScale { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + /// + /// A description of the rights associated with this access token. + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Access : System.Collections.ObjectModel.Collection + { + + } + + /// + /// The access associated with the access token is described using objects that each contain multiple dimensions of access. + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class AccessItem + { + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class AccessOutgoing + { + + /// + /// The type of resource request as a string. This field defines which other fields are allowed in the request object. + /// + [Newtonsoft.Json.JsonProperty("type", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public AccessOutgoingType Type { get; set; } + + /// + /// The types of actions the client instance will take at the RS as an array of strings. + /// + [Newtonsoft.Json.JsonProperty("actions", Required = Newtonsoft.Json.Required.Always, ItemConverterType = typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Actions { get; set; } = new System.Collections.ObjectModel.Collection(); + + /// + /// A string identifier indicating a specific resource at the RS. + /// + [Newtonsoft.Json.JsonProperty("identifier", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri Identifier { get; set; } + + [Newtonsoft.Json.JsonProperty("limits", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public LimitsOutgoing Limits { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class AccessQuote + { + + /// + /// The type of resource request as a string. This field defines which other fields are allowed in the request object. + /// + [Newtonsoft.Json.JsonProperty("type", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public AccessQuoteType Type { get; set; } + + /// + /// The types of actions the client instance will take at the RS as an array of strings. + /// + [Newtonsoft.Json.JsonProperty("actions", Required = Newtonsoft.Json.Required.Always, ItemConverterType = typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Actions { get; set; } = new System.Collections.ObjectModel.Collection(); + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + /// + /// A single access token or set of access tokens that the client instance can use to call the RS on behalf of the RO. + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Access_token + { + + /// + /// The value of the access token as a string. The value is opaque to the client instance. The value SHOULD be limited to ASCII characters to facilitate transmission over HTTP headers within other protocols without requiring additional encoding. + /// + [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Value { get; set; } + + /// + /// The management URI for this access token. This URI MUST NOT include the access token value and SHOULD be different for each access token issued in a request. + /// + [Newtonsoft.Json.JsonProperty("manage", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri Manage { get; set; } + + /// + /// The number of seconds in which the access will expire. The client instance MUST NOT use the access token past this time. An RS MUST NOT accept an access token past this time. + /// + [Newtonsoft.Json.JsonProperty("expires_in", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Expires_in { get; set; } + + [Newtonsoft.Json.JsonProperty("access", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + [System.ComponentModel.DataAnnotations.MaxLength(3)] + public Access Access { get; set; } = new Access(); + + } + + /// + /// If the AS determines that the request can be continued with additional requests, it responds with the continue field. + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Continue + { + + /// + /// A unique access token for continuing the request, called the "continuation access token". + /// + [Newtonsoft.Json.JsonProperty("access_token", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public Access_token2 Access_token { get; set; } = new Access_token2(); + + /// + /// The URI at which the client instance can make continuation requests. + /// + [Newtonsoft.Json.JsonProperty("uri", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri Uri { get; set; } + + /// + /// The amount of time in integer seconds the client instance MUST wait after receiving this request continuation response and calling the continuation URI. + /// + [Newtonsoft.Json.JsonProperty("wait", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public int Wait { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + /// + /// The client instance declares the parameters for interaction methods that it can support using the interact field. + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class InteractRequest + { + + /// + /// Indicates how the client instance can start an interaction. + /// + [Newtonsoft.Json.JsonProperty("start", Required = Newtonsoft.Json.Required.Always, ItemConverterType = typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Start { get; set; } = new System.Collections.ObjectModel.Collection(); + + /// + /// Indicates how the client instance can receive an indication that interaction has finished at the AS. + /// + [Newtonsoft.Json.JsonProperty("finish", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Finish Finish { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class InteractResponse + { + + /// + /// The URI to direct the end user to. + /// + [Newtonsoft.Json.JsonProperty("redirect", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri Redirect { get; set; } + + /// + /// Unique key to secure the callback. + /// + [Newtonsoft.Json.JsonProperty("finish", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Finish { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + /// + /// Open Payments specific property that defines the limits under which outgoing payments can be created. + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class LimitsOutgoing + { + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class ErrorInvalidClient + { + + [Newtonsoft.Json.JsonProperty("error", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Error Error { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class ErrorInvalidRequest + { + + [Newtonsoft.Json.JsonProperty("error", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Error2 Error { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class ErrorRequestDenied + { + + [Newtonsoft.Json.JsonProperty("error", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Error3 Error { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class ErrorTooFast + { + + [Newtonsoft.Json.JsonProperty("error", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Error4 Error { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class ErrorInvalidContinuation + { + + [Newtonsoft.Json.JsonProperty("error", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Error5 Error { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class ErrorInvalidRotation + { + + [Newtonsoft.Json.JsonProperty("error", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Error6 Error { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Body + { + + [Newtonsoft.Json.JsonProperty("access_token", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public Access_token3 Access_token { get; set; } = new Access_token3(); + + [Newtonsoft.Json.JsonProperty("client", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Client { get; set; } + + [Newtonsoft.Json.JsonProperty("interact", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public InteractRequest Interact { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Body2 + { + + /// + /// The interaction reference generated for this + ///
interaction by the AS. + ///
+ [Newtonsoft.Json.JsonProperty("interact_ref", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Interact_ref { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Response + { + + [Newtonsoft.Json.JsonProperty("interact", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public InteractResponse Interact { get; set; } = new InteractResponse(); + + [Newtonsoft.Json.JsonProperty("continue", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public Continue Continue { get; set; } = new Continue(); + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Response2 + { + + [Newtonsoft.Json.JsonProperty("access_token", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Access_token Access_token { get; set; } + + [Newtonsoft.Json.JsonProperty("continue", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public Continue Continue { get; set; } = new Continue(); + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Response3 + { + + [Newtonsoft.Json.JsonProperty("access_token", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public Access_token Access_token { get; set; } = new Access_token(); + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum AccessItemType + { + + [System.Runtime.Serialization.EnumMember(Value = @"incoming-payment")] + IncomingPayment = 0, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum Actions + { + + [System.Runtime.Serialization.EnumMember(Value = @"create")] + Create = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"complete")] + Complete = 1, + + [System.Runtime.Serialization.EnumMember(Value = @"read")] + Read = 2, + + [System.Runtime.Serialization.EnumMember(Value = @"read-all")] + ReadAll = 3, + + [System.Runtime.Serialization.EnumMember(Value = @"list")] + List = 4, + + [System.Runtime.Serialization.EnumMember(Value = @"list-all")] + ListAll = 5, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum AccessOutgoingType + { + + [System.Runtime.Serialization.EnumMember(Value = @"outgoing-payment")] + OutgoingPayment = 0, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum Actions2 + { + + [System.Runtime.Serialization.EnumMember(Value = @"create")] + Create = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"read")] + Read = 1, + + [System.Runtime.Serialization.EnumMember(Value = @"read-all")] + ReadAll = 2, + + [System.Runtime.Serialization.EnumMember(Value = @"list")] + List = 3, + + [System.Runtime.Serialization.EnumMember(Value = @"list-all")] + ListAll = 4, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum AccessQuoteType + { + + [System.Runtime.Serialization.EnumMember(Value = @"quote")] + Quote = 0, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum Actions3 + { + + [System.Runtime.Serialization.EnumMember(Value = @"create")] + Create = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"read")] + Read = 1, + + [System.Runtime.Serialization.EnumMember(Value = @"read-all")] + ReadAll = 2, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Access_token2 + { + + [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Value { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum Start + { + + [System.Runtime.Serialization.EnumMember(Value = @"redirect")] + Redirect = 0, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Finish + { + + /// + /// The callback method that the AS will use to contact the client instance. + /// + [Newtonsoft.Json.JsonProperty("method", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public FinishMethod Method { get; set; } + + /// + /// Indicates the URI that the AS will either send the RO to after interaction or send an HTTP POST request. + /// + [Newtonsoft.Json.JsonProperty("uri", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri Uri { get; set; } + + /// + /// Unique value to be used in the calculation of the "hash" query parameter sent to the callback URI, must be sufficiently random to be unguessable by an attacker. MUST be generated by the client instance as a unique value for this request. + /// + [Newtonsoft.Json.JsonProperty("nonce", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Nonce { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Error + { + + [Newtonsoft.Json.JsonProperty("description", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Description { get; set; } + + [Newtonsoft.Json.JsonProperty("code", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public ErrorCode Code { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Error2 + { + + [Newtonsoft.Json.JsonProperty("description", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Description { get; set; } + + [Newtonsoft.Json.JsonProperty("code", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public Error2Code Code { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Error3 + { + + [Newtonsoft.Json.JsonProperty("description", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Description { get; set; } + + [Newtonsoft.Json.JsonProperty("code", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public Error3Code Code { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Error4 + { + + [Newtonsoft.Json.JsonProperty("description", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Description { get; set; } + + [Newtonsoft.Json.JsonProperty("code", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public Error4Code Code { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Error5 + { + + [Newtonsoft.Json.JsonProperty("description", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Description { get; set; } + + [Newtonsoft.Json.JsonProperty("code", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public Error5Code Code { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Error6 + { + + [Newtonsoft.Json.JsonProperty("description", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Description { get; set; } + + [Newtonsoft.Json.JsonProperty("code", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public Error6Code Code { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Access_token3 + { + + [Newtonsoft.Json.JsonProperty("access", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + [System.ComponentModel.DataAnnotations.MaxLength(3)] + public Access Access { get; set; } = new Access(); + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum FinishMethod + { + + [System.Runtime.Serialization.EnumMember(Value = @"redirect")] + Redirect = 0, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum ErrorCode + { + + [System.Runtime.Serialization.EnumMember(Value = @"invalid_client")] + Invalid_client = 0, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum Error2Code + { + + [System.Runtime.Serialization.EnumMember(Value = @"invalid_request")] + Invalid_request = 0, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum Error3Code + { + + [System.Runtime.Serialization.EnumMember(Value = @"request_denied")] + Request_denied = 0, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum Error4Code + { + + [System.Runtime.Serialization.EnumMember(Value = @"too_fast")] + Too_fast = 0, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum Error5Code + { + + [System.Runtime.Serialization.EnumMember(Value = @"invalid_continuation")] + Invalid_continuation = 0, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum Error6Code + { + + [System.Runtime.Serialization.EnumMember(Value = @"invalid_rotation")] + Invalid_rotation = 0, + + } + + + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class ApiException : System.Exception + { + public int StatusCode { get; private set; } + + public string Response { get; private set; } + + public System.Collections.Generic.IReadOnlyDictionary> Headers { get; private set; } + + public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Exception innerException) + : base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException) + { + StatusCode = statusCode; + Response = response; + Headers = headers; + } + + public override string ToString() + { + return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString()); + } + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class ApiException : ApiException + { + public TResult Result { get; private set; } + + public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, TResult result, System.Exception innerException) + : base(message, statusCode, response, headers, innerException) + { + Result = result; + } + } + +} + +#pragma warning restore 108 +#pragma warning restore 114 +#pragma warning restore 472 +#pragma warning restore 612 +#pragma warning restore 649 +#pragma warning restore 1573 +#pragma warning restore 1591 +#pragma warning restore 8073 +#pragma warning restore 3016 +#pragma warning restore 8600 +#pragma warning restore 8602 +#pragma warning restore 8603 +#pragma warning restore 8604 +#pragma warning restore 8625 +#pragma warning restore 8765 \ No newline at end of file diff --git a/OpenPayments.Sdk/Generated/Auth/Types.cs b/OpenPayments.Sdk/Generated/Auth/Types.cs new file mode 100644 index 0000000..e5e21b0 --- /dev/null +++ b/OpenPayments.Sdk/Generated/Auth/Types.cs @@ -0,0 +1,188 @@ +using System.Collections.ObjectModel; +using Newtonsoft.Json; + +namespace OpenPayments.Sdk.Generated.Auth +{ + public partial class GrantCreateBody + { + [JsonProperty("access_token")] public AccessToken? AccessToken { get; set; } + + [JsonProperty("client")] public Uri? Client { get; set; } + + [JsonProperty("interact")] public InteractRequest? Interact { get; set; } + + // TODO: Add subject + } + + public partial class GrantContinueBody + { + [JsonProperty("interact_ref")] public string? InteractRef { get; set; } + } + + public partial class AccessToken + { + [JsonProperty("access")] public required Collection Access { get; set; } + + [JsonProperty("value")] public string Value { get; set; } = null!; + + [JsonProperty("manage")] public string Manage { get; set; } = null!; + + [JsonProperty("expires_in")] public int? ExpiresIn { get; set; } + } + + /// + /// The access associated with the access token is described using objects that each contain multiple dimensions of access. + /// + public partial class AccessItem + { + [JsonProperty("type")] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public AccessType Type { get; set; } + + [JsonProperty("actions", ItemConverterType = typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + [System.ComponentModel.DataAnnotations.Required] + // [JsonProperty("actions")] public string[]? Actions { get; set; } + public ICollection Actions { get; set; } = new Collection(); + + [JsonProperty("identifier")] public string? Identifier { get; set; } + + [JsonProperty("limits")] public AccessLimits? Limits { get; set; } + } + + public enum AccessType + { + [System.Runtime.Serialization.EnumMember(Value = @"incoming-payment")] + IncomingPayment = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"outgoing-payment")] + OutgoingPayment = 1, + + [System.Runtime.Serialization.EnumMember(Value = @"quote")] + Quote = 2, + } + + public partial class AccessLimits + { + [JsonProperty("debitAmount")] public Amount? DebitAmount { get; set; } + + [JsonProperty("receiveAmount")] public Amount? ReceiveAmount { get; set; } + + [JsonProperty("interval")] public string? Interval { get; set; } + } + + public partial class AuthResponse + { + [JsonProperty("access_token")] public AccessToken? AccessToken { get; set; } + + [JsonProperty("interact")] public InteractResponse? Interact { get; set; } + + [JsonProperty("continue")] public AuthContinue Continue { get; set; } = null!; + } + + public partial class AuthContinue + { + /// + /// A unique access token for continuing the request, called the "continuation access token". + /// + [JsonProperty("access_token", Required = Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public Access_token2 AccessToken { get; set; } = new Access_token2(); + + /// + /// The URI at which the client instance can make continuation requests. + /// + [JsonProperty("uri", Required = Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public Uri Uri { get; set; } = null!; + + /// + /// The amount of time in integer seconds the client instance MUST wait after receiving this request continuation response and calling the continuation URI. + /// + [JsonProperty("wait", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)] + public int Wait { get; set; } + + private IDictionary? _additionalProperties; + + [JsonExtensionData] + public IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new Dictionary()); } + set { _additionalProperties = value; } + } + } + + public partial class RotateTokenResponse + { + [JsonProperty("access_token")] public required AccessToken AccessToken { get; set; } + } + + public partial class ErrorResponse + { + [JsonProperty("error", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)] + public ErrorItem? Error { get; set; } + + private IDictionary? _additionalProperties; + + [JsonExtensionData] + public IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new Dictionary()); } + set { _additionalProperties = value; } + } + } + + public partial class ErrorItem + { + [JsonProperty("description", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)] + public string? Description { get; set; } + + [JsonProperty("code", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public ErrorItemCode Code { get; set; } + + private IDictionary? _additionalProperties; + + [JsonExtensionData] + public IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new Dictionary()); } + set { _additionalProperties = value; } + } + } + + public partial class Amount + { + public Amount() + { + } + + public Amount(string value, string assetCode, int? assetScale = 2) + { + Value = value; + AssetCode = assetCode; + AssetScale = assetScale ?? 2; + } + } + + public enum ErrorItemCode + { + [System.Runtime.Serialization.EnumMember(Value = @"invalid_client")] + InvalidClient = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"invalid_request")] + InvalidRequest = 1, + + [System.Runtime.Serialization.EnumMember(Value = @"request_denied")] + RequestDenied = 2, + + [System.Runtime.Serialization.EnumMember(Value = @"too_fast")] + TooFast = 3, + + [System.Runtime.Serialization.EnumMember(Value = @"invalid_continuation")] + InvalidContinuation = 4, + + [System.Runtime.Serialization.EnumMember(Value = @"invalid_rotation")] + InvalidRotation = 5, + } +} \ No newline at end of file diff --git a/OpenPayments.Sdk/Generated/Resource/ResourceContractResolver.cs b/OpenPayments.Sdk/Generated/Resource/ResourceContractResolver.cs new file mode 100644 index 0000000..3ced69c --- /dev/null +++ b/OpenPayments.Sdk/Generated/Resource/ResourceContractResolver.cs @@ -0,0 +1,24 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace OpenPayments.Sdk.Generated.Resource; + +public sealed class ResourceContractResolver : DefaultContractResolver +{ + protected override JsonProperty CreateProperty(System.Reflection.MemberInfo member, + MemberSerialization memberSerialization) + { + var prop = base.CreateProperty(member, memberSerialization); + prop.Required = Required.AllowNull; + prop.NullValueHandling = NullValueHandling.Ignore; + + // Force 'metadata' property to be optional, regardless of what the attributes say + if (prop.PropertyName != null && prop.PropertyName.Equals("metadata", StringComparison.OrdinalIgnoreCase)) + { + prop.Required = Required.Default; + prop.NullValueHandling = NullValueHandling.Ignore; + } + + return prop; + } +} \ No newline at end of file diff --git a/OpenPayments.Sdk/Generated/Resource/ResourceServerClient.Auth.cs b/OpenPayments.Sdk/Generated/Resource/ResourceServerClient.Auth.cs new file mode 100644 index 0000000..d2aeefb --- /dev/null +++ b/OpenPayments.Sdk/Generated/Resource/ResourceServerClient.Auth.cs @@ -0,0 +1,32 @@ +using Newtonsoft.Json; +using NSec.Cryptography; + +namespace OpenPayments.Sdk.Generated.Resource; + +public partial class ResourceServerClient +{ + private Key? _privateKey; + private string? _keyId; + public Uri ClientUrl { get; set; } + + public void AddSigningKey(Key privateKey, string keyId) + { + _privateKey = privateKey; + _keyId = keyId; + } + + static partial void UpdateJsonSerializerSettings(JsonSerializerSettings settings) + { + settings.ContractResolver = new ResourceContractResolver(); + } + + partial void PrepareRequest(HttpClient client, HttpRequestMessage request, + string url) + { + if (_privateKey == null || _keyId == null) return; + + var headers = HttpRequestSigner.SignHttpRequestAsync(request, _privateKey, _keyId).Result; + request.Headers.TryAddWithoutValidation("Signature", headers.Signature); + request.Headers.TryAddWithoutValidation("Signature-Input", headers.SignatureInput); + } +} \ No newline at end of file diff --git a/OpenPayments.Sdk/Generated/Resource/ResourceServerClient.Methods.IncomingPayment.cs b/OpenPayments.Sdk/Generated/Resource/ResourceServerClient.Methods.IncomingPayment.cs new file mode 100644 index 0000000..f5a61a8 --- /dev/null +++ b/OpenPayments.Sdk/Generated/Resource/ResourceServerClient.Methods.IncomingPayment.cs @@ -0,0 +1,119 @@ +using System.Net.Http.Headers; +using System.Text; +using Newtonsoft.Json; + +namespace OpenPayments.Sdk.Generated.Resource; + +public partial class ResourceServerClient +{ + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Create an Incoming Payment + /// + /// + /// A client MUST create an **incoming payment** resource before it is possible to send any payments to the wallet address. + ///
+ ///
When a client creates an **incoming payment** the receiving Account Servicing Entity generates unique payment details that can be used to address payments to the account and returns these details to the client as properties of the new **incoming payment**. Any payments received using those details are then associated with the **incoming payment**. + ///
+ ///
All of the input parameters are _optional_. + ///
+ ///
For example, the client could use the `metadata` property to store an external reference on the **incoming payment** and this can be shared with the account holder to assist with reconciliation. + ///
+ ///
If `incomingAmount` is specified and the total received using the payment details equals or exceeds the specified `incomingAmount`, then the receiving Account Servicing Entity MUST reject any further payments and set `completed` to `true`. + ///
+ ///
If an `expiresAt` value is defined, and the current date and time on the receiving Account Servicing Entity's systems exceeds that value, the receiving Account Servicing Entity MUST reject any further payments. + ///
+ /// A subset of the incoming payments schema is accepted as input to create a new incoming payment. + ///
+ ///
The `incomingAmount` must use the same `assetCode` and `assetScale` as the wallet address. + /// Access Token. + /// Incoming Payment Created + /// A server side error occurred. + public async Task PostIncomingPaymentAsync(Body body, + string accessToken, + CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(body); + ArgumentException.ThrowIfNullOrWhiteSpace(accessToken); + + var client = _httpClient; + + using var request = new HttpRequestMessage(); + var json = JsonConvert.SerializeObject(body, JsonSerializerSettings); + var content = new StringContent(json); + content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); + request.Content = content; + request.Method = new HttpMethod("POST"); + request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); + request.Headers.Authorization = new AuthenticationHeaderValue("GNAP", $"{accessToken}"); + + var urlBuilder = new StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder.Append(_baseUrl); + // Operation Path: "incoming-payments" + urlBuilder.Append("incoming-payments"); + + PrepareRequest(client, request, urlBuilder); + + var url = urlBuilder.ToString(); + request.RequestUri = new Uri(url, UriKind.RelativeOrAbsolute); + + PrepareRequest(client, request, url); + + var response = await client + .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken) + .ConfigureAwait(false); + + try + { + var headers = Helpers.ExtractHeaders(response); + + ProcessResponse(client, response); + + var status = (int)response.StatusCode; + switch (status) + { + case 201: + { + var objectResponse = + await ReadObjectResponseAsync(response, headers, + cancellationToken).ConfigureAwait(false); + if (objectResponse.Object == null) + { + throw new ApiException("Response was null which was not expected.", status, + objectResponse.Text, headers, null); + } + + return objectResponse.Object; + } + case 401: + case 403: + { + var objectResponse = + await ReadObjectResponseAsync(response, headers, cancellationToken) + .ConfigureAwait(false); + if (objectResponse.Object == null) + { + throw new ApiException("Response was null which was not expected.", status, + objectResponse.Text, headers, null); + } + + throw new ApiException(Helpers.StatusAsText(status), status, objectResponse.Text, + headers, + objectResponse.Object, null); + } + default: + { + var responseData = await ReadAsStringAsync(response.Content, cancellationToken) + .ConfigureAwait(false); + throw new ApiException( + "The HTTP status code of the response was not expected (" + status + ").", status, + responseData, headers, null); + } + } + } + finally + { + response.Dispose(); + } + } +} \ No newline at end of file diff --git a/OpenPayments.Sdk/Generated/Resource/ResourceServerClient.Methods.OutgoingPayment.cs b/OpenPayments.Sdk/Generated/Resource/ResourceServerClient.Methods.OutgoingPayment.cs new file mode 100644 index 0000000..a0229a0 --- /dev/null +++ b/OpenPayments.Sdk/Generated/Resource/ResourceServerClient.Methods.OutgoingPayment.cs @@ -0,0 +1,107 @@ +using System.Net.Http.Headers; +using System.Text; +using Newtonsoft.Json; + +namespace OpenPayments.Sdk.Generated.Resource; + +public partial class ResourceServerClient +{ + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Create an Outgoing Payment + /// + /// + /// An **outgoing payment** is a sub-resource of a wallet address. It represents a payment from the wallet address. + ///
+ ///
Once created, it is already authorized and SHOULD be processed immediately. If payment fails, the Account Servicing Entity must mark the **outgoing payment** as `failed`. + ///
+ /// A subset of the outgoing payments schema is accepted as input to create a new outgoing payment. + ///
+ ///
The `debitAmount` must use the same `assetCode` and `assetScale` as the wallet address. + ///
+ ///
Either provide a `quoteId` to create an outgoing payment based on a quote or provide `incomingPayment` and `debitAmount` to create an outgoing payment directly from an incoming payment. + /// Access Token. + /// Outgoing Payment Created + /// A server side error occurred. + public async Task PostOutgoingPaymentAsync(OutgoingPaymentBody body, + string accessToken, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(body); + + var client = _httpClient; + using var request = new HttpRequestMessage(); + var json = JsonConvert.SerializeObject(body, JsonSerializerSettings); + var content = new StringContent(json); + content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); + request.Content = content; + request.Method = new HttpMethod("POST"); + request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); + request.Headers.Authorization = new AuthenticationHeaderValue("GNAP", $"{accessToken}"); + var urlBuilder = new StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder.Append(_baseUrl); + urlBuilder.Append("outgoing-payments"); + + PrepareRequest(client, request, urlBuilder); + + var url = urlBuilder.ToString(); + request.RequestUri = new Uri(url, UriKind.RelativeOrAbsolute); + + PrepareRequest(client, request, url); + + var response = await client.SendAsync(request, + HttpCompletionOption.ResponseHeadersRead, cancellationToken) + .ConfigureAwait(false); + + try + { + var headers = Helpers.ExtractHeaders(response); + + ProcessResponse(client, response); + + var status = (int)response.StatusCode; + switch (status) + { + case 201: + { + var objectResponse = + await ReadObjectResponseAsync(response, headers, + cancellationToken).ConfigureAwait(false); + if (objectResponse.Object == null) + { + throw new ApiException("Response was null which was not expected.", status, + objectResponse.Text, headers, null); + } + + return objectResponse.Object; + } + case 401: + case 403: + { + var objectResponse = + await ReadObjectResponseAsync(response, headers, cancellationToken) + .ConfigureAwait(false); + if (objectResponse.Object == null) + { + throw new ApiException("Response was null which was not expected.", status, + objectResponse.Text, headers, null); + } + + throw new ApiException(Helpers.StatusAsText(status), status, objectResponse.Text, + headers, objectResponse.Object, null); + } + default: + { + var responseData = await ReadAsStringAsync(response.Content, cancellationToken) + .ConfigureAwait(false); + throw new ApiException( + "The HTTP status code of the response was not expected (" + status + ").", status, + responseData, headers, null); + } + } + } + finally + { + response.Dispose(); + } + } +} \ No newline at end of file diff --git a/OpenPayments.Sdk/Generated/Resource/ResourceServerClient.Methods.Quote.cs b/OpenPayments.Sdk/Generated/Resource/ResourceServerClient.Methods.Quote.cs new file mode 100644 index 0000000..c8e4394 --- /dev/null +++ b/OpenPayments.Sdk/Generated/Resource/ResourceServerClient.Methods.Quote.cs @@ -0,0 +1,106 @@ +using System.Net.Http.Headers; +using System.Text; +using Newtonsoft.Json; + +namespace OpenPayments.Sdk.Generated.Resource; + +public partial class ResourceServerClient +{ + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Create a Quote + /// + /// + /// A **quote** is a sub-resource of a wallet address. It represents a quote for a payment from the wallet address. + /// + /// A subset of the quotes schema is accepted as input to create a new quote. + ///
+ ///
The quote must be created with a (`debitAmount` xor `receiveAmount`) unless the `receiver` is an Incoming Payment which has an `incomingAmount`. + /// Access Token. + /// Quote Created + /// A server side error occurred. + public async Task PostQuoteAsync(QuoteBody body, string accessToken, + CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(body); + ArgumentException.ThrowIfNullOrWhiteSpace(accessToken); + + var client = _httpClient; + using var request = new HttpRequestMessage(); + var json = JsonConvert.SerializeObject(body, JsonSerializerSettings); + var content = new StringContent(json); + content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); + request.Content = content; + request.Method = new HttpMethod("POST"); + request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse("application/json")); + request.Headers.Authorization = new AuthenticationHeaderValue("GNAP", $"{accessToken}"); + + var urlBuilder = new StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder.Append(_baseUrl); + urlBuilder.Append("quotes"); + + PrepareRequest(client, request, urlBuilder); + + var url = urlBuilder.ToString(); + request.RequestUri = new Uri(url, UriKind.RelativeOrAbsolute); + + PrepareRequest(client, request, url); + + var response = await client.SendAsync(request, + HttpCompletionOption.ResponseHeadersRead, cancellationToken) + .ConfigureAwait(false); + + try + { + var headers = Helpers.ExtractHeaders(response); + + ProcessResponse(client, response); + + var status = (int)response.StatusCode; + switch (status) + { + case 201: + { + var objectResponse = + await ReadObjectResponseAsync(response, headers, cancellationToken) + .ConfigureAwait(false); + if (objectResponse.Object == null) + { + throw new ApiException("Response was null which was not expected.", status, + objectResponse.Text, headers, null); + } + + return objectResponse.Object; + } + case 400: + case 401: + case 403: + { + var objectResponse = + await ReadObjectResponseAsync(response, headers, cancellationToken) + .ConfigureAwait(false); + if (objectResponse.Object == null) + { + throw new ApiException("Response was null which was not expected.", status, + objectResponse.Text, headers, null); + } + + throw new ApiException(Helpers.StatusAsText(status), status, objectResponse.Text, + headers, objectResponse.Object, null); + } + default: + { + var responseData = await ReadAsStringAsync(response.Content, cancellationToken) + .ConfigureAwait(false); + throw new ApiException( + "The HTTP status code of the response was not expected (" + status + ").", status, + responseData, headers, null); + } + } + } + finally + { + response.Dispose(); + } + } +} \ No newline at end of file diff --git a/OpenPayments.Sdk/Generated/Resource/ResourceServerClient.g.cs b/OpenPayments.Sdk/Generated/Resource/ResourceServerClient.g.cs new file mode 100644 index 0000000..b000d59 --- /dev/null +++ b/OpenPayments.Sdk/Generated/Resource/ResourceServerClient.g.cs @@ -0,0 +1,2200 @@ +//---------------------- +// +// Generated using the NSwag toolchain v14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org) +// +//---------------------- + +#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended." +#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword." +#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?' +#pragma warning disable 612 // Disable "CS0612 '...' is obsolete" +#pragma warning disable 649 // Disable "CS0649 Field is never assigned to, and will always have its default value null" +#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ... +#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..." +#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'" +#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant" +#pragma warning disable 8600 // Disable "CS8600 Converting null literal or possible null value to non-nullable type" +#pragma warning disable 8602 // Disable "CS8602 Dereference of a possibly null reference" +#pragma warning disable 8603 // Disable "CS8603 Possible null reference return" +#pragma warning disable 8604 // Disable "CS8604 Possible null reference argument for parameter" +#pragma warning disable 8625 // Disable "CS8625 Cannot convert null literal to non-nullable reference type" +#pragma warning disable 8765 // Disable "CS8765 Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes)." + +namespace OpenPayments.Sdk.Generated.Resource +{ + using System = global::System; + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class ResourceServerClient + { + #pragma warning disable 8618 + private string _baseUrl; + #pragma warning restore 8618 + + private System.Net.Http.HttpClient _httpClient; + private static System.Lazy _settings = new System.Lazy(CreateSerializerSettings, true); + private Newtonsoft.Json.JsonSerializerSettings _instanceSettings; + + #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + public ResourceServerClient(System.Net.Http.HttpClient httpClient) + #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + { + BaseUrl = "https://ilp.interledger-test.dev"; + _httpClient = httpClient; + Initialize(); + } + + private static Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings() + { + var settings = new Newtonsoft.Json.JsonSerializerSettings(); + UpdateJsonSerializerSettings(settings); + return settings; + } + + public string BaseUrl + { + get { return _baseUrl; } + set + { + _baseUrl = value; + if (!string.IsNullOrEmpty(_baseUrl) && !_baseUrl.EndsWith("/")) + _baseUrl += '/'; + } + } + + protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _instanceSettings ?? _settings.Value; } } + + static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); + + partial void Initialize(); + + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); + partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); + + /// + /// Create an Incoming Payment + /// + /// + /// A client MUST create an **incoming payment** resource before it is possible to send any payments to the wallet address. + ///
+ ///
When a client creates an **incoming payment** the receiving Account Servicing Entity generates unique payment details that can be used to address payments to the account and returns these details to the client as properties of the new **incoming payment**. Any payments received using those details are then associated with the **incoming payment**. + ///
+ ///
All of the input parameters are _optional_. + ///
+ ///
For example, the client could use the `metadata` property to store an external reference on the **incoming payment** and this can be shared with the account holder to assist with reconciliation. + ///
+ ///
If `incomingAmount` is specified and the total received using the payment details equals or exceeds the specified `incomingAmount`, then the receiving Account Servicing Entity MUST reject any further payments and set `completed` to `true`. + ///
+ ///
If an `expiresAt` value is defined, and the current date and time on the receiving Account Servicing Entity's systems exceeds that value, the receiving Account Servicing Entity MUST reject any further payments. + ///
+ /// A subset of the incoming payments schema is accepted as input to create a new incoming payment. + ///
+ ///
The `incomingAmount` must use the same `assetCode` and `assetScale` as the wallet address. + /// The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. + /// The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. + /// Incoming Payment Created + /// A server side error occurred. + public virtual System.Threading.Tasks.Task CreateIncomingPaymentAsync(Body body, string signature_Input, string signature) + { + return CreateIncomingPaymentAsync(body, signature_Input, signature, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Create an Incoming Payment + /// + /// + /// A client MUST create an **incoming payment** resource before it is possible to send any payments to the wallet address. + ///
+ ///
When a client creates an **incoming payment** the receiving Account Servicing Entity generates unique payment details that can be used to address payments to the account and returns these details to the client as properties of the new **incoming payment**. Any payments received using those details are then associated with the **incoming payment**. + ///
+ ///
All of the input parameters are _optional_. + ///
+ ///
For example, the client could use the `metadata` property to store an external reference on the **incoming payment** and this can be shared with the account holder to assist with reconciliation. + ///
+ ///
If `incomingAmount` is specified and the total received using the payment details equals or exceeds the specified `incomingAmount`, then the receiving Account Servicing Entity MUST reject any further payments and set `completed` to `true`. + ///
+ ///
If an `expiresAt` value is defined, and the current date and time on the receiving Account Servicing Entity's systems exceeds that value, the receiving Account Servicing Entity MUST reject any further payments. + ///
+ /// A subset of the incoming payments schema is accepted as input to create a new incoming payment. + ///
+ ///
The `incomingAmount` must use the same `assetCode` and `assetScale` as the wallet address. + /// The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. + /// The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. + /// Incoming Payment Created + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task CreateIncomingPaymentAsync(Body body, string signature_Input, string signature, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + + if (signature_Input == null) + throw new System.ArgumentNullException("signature_Input"); + request_.Headers.TryAddWithoutValidation("Signature-Input", ConvertToString(signature_Input, System.Globalization.CultureInfo.InvariantCulture)); + + if (signature == null) + throw new System.ArgumentNullException("signature"); + request_.Headers.TryAddWithoutValidation("Signature", ConvertToString(signature, System.Globalization.CultureInfo.InvariantCulture)); + var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(body, JsonSerializerSettings); + var content_ = new System.Net.Http.StringContent(json_); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "incoming-payments" + urlBuilder_.Append("incoming-payments"); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 201) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Authorization required", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 403) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Forbidden", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// + /// List Incoming Payments + /// + /// + /// List all incoming payments on the wallet address + /// + /// URL of a wallet address hosted by a Rafiki instance. + /// The cursor key to list from. + /// The number of items to return after the cursor. + /// The number of items to return before the cursor. + /// The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. + /// The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. + /// OK + /// A server side error occurred. + public virtual System.Threading.Tasks.Task ListIncomingPaymentsAsync(string wallet_address, string cursor, int? first, int? last, string signature_Input, string signature) + { + return ListIncomingPaymentsAsync(wallet_address, cursor, first, last, signature_Input, signature, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// List Incoming Payments + /// + /// + /// List all incoming payments on the wallet address + /// + /// URL of a wallet address hosted by a Rafiki instance. + /// The cursor key to list from. + /// The number of items to return after the cursor. + /// The number of items to return before the cursor. + /// The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. + /// The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. + /// OK + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task ListIncomingPaymentsAsync(string wallet_address, string cursor, int? first, int? last, string signature_Input, string signature, System.Threading.CancellationToken cancellationToken) + { + if (wallet_address == null) + throw new System.ArgumentNullException("wallet_address"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + + if (signature_Input == null) + throw new System.ArgumentNullException("signature_Input"); + request_.Headers.TryAddWithoutValidation("Signature-Input", ConvertToString(signature_Input, System.Globalization.CultureInfo.InvariantCulture)); + + if (signature == null) + throw new System.ArgumentNullException("signature"); + request_.Headers.TryAddWithoutValidation("Signature", ConvertToString(signature, System.Globalization.CultureInfo.InvariantCulture)); + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "incoming-payments" + urlBuilder_.Append("incoming-payments"); + urlBuilder_.Append('?'); + urlBuilder_.Append(System.Uri.EscapeDataString("wallet-address")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(wallet_address, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + if (cursor != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("cursor")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(cursor, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + } + if (first != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("first")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(first, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + } + if (last != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("last")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(last, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + } + urlBuilder_.Length--; + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Authorization required", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 403) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Forbidden", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// + /// Create an Outgoing Payment + /// + /// + /// An **outgoing payment** is a sub-resource of a wallet address. It represents a payment from the wallet address. + ///
+ ///
Once created, it is already authorized and SHOULD be processed immediately. If payment fails, the Account Servicing Entity must mark the **outgoing payment** as `failed`. + ///
+ /// A subset of the outgoing payments schema is accepted as input to create a new outgoing payment. + ///
+ ///
The `debitAmount` must use the same `assetCode` and `assetScale` as the wallet address. + ///
+ ///
Either provide a `quoteId` to create an outgoing payment based on a quote or provide `incomingPayment` and `debitAmount` to create an outgoing payment directly from an incoming payment. + /// The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. + /// The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. + /// Outgoing Payment Created + /// A server side error occurred. + public virtual System.Threading.Tasks.Task CreateOutgoingPaymentAsync(Body2 body, string signature_Input, string signature) + { + return CreateOutgoingPaymentAsync(body, signature_Input, signature, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Create an Outgoing Payment + /// + /// + /// An **outgoing payment** is a sub-resource of a wallet address. It represents a payment from the wallet address. + ///
+ ///
Once created, it is already authorized and SHOULD be processed immediately. If payment fails, the Account Servicing Entity must mark the **outgoing payment** as `failed`. + ///
+ /// A subset of the outgoing payments schema is accepted as input to create a new outgoing payment. + ///
+ ///
The `debitAmount` must use the same `assetCode` and `assetScale` as the wallet address. + ///
+ ///
Either provide a `quoteId` to create an outgoing payment based on a quote or provide `incomingPayment` and `debitAmount` to create an outgoing payment directly from an incoming payment. + /// The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. + /// The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. + /// Outgoing Payment Created + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task CreateOutgoingPaymentAsync(Body2 body, string signature_Input, string signature, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + + if (signature_Input == null) + throw new System.ArgumentNullException("signature_Input"); + request_.Headers.TryAddWithoutValidation("Signature-Input", ConvertToString(signature_Input, System.Globalization.CultureInfo.InvariantCulture)); + + if (signature == null) + throw new System.ArgumentNullException("signature"); + request_.Headers.TryAddWithoutValidation("Signature", ConvertToString(signature, System.Globalization.CultureInfo.InvariantCulture)); + var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(body, JsonSerializerSettings); + var content_ = new System.Net.Http.StringContent(json_); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "outgoing-payments" + urlBuilder_.Append("outgoing-payments"); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 201) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Authorization required", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 403) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Forbidden", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// + /// List Outgoing Payments + /// + /// + /// List all outgoing payments on the wallet address + /// + /// URL of a wallet address hosted by a Rafiki instance. + /// The cursor key to list from. + /// The number of items to return after the cursor. + /// The number of items to return before the cursor. + /// The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. + /// The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. + /// OK + /// A server side error occurred. + public virtual System.Threading.Tasks.Task ListOutgoingPaymentsAsync(string wallet_address, string cursor, int? first, int? last, string signature_Input, string signature) + { + return ListOutgoingPaymentsAsync(wallet_address, cursor, first, last, signature_Input, signature, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// List Outgoing Payments + /// + /// + /// List all outgoing payments on the wallet address + /// + /// URL of a wallet address hosted by a Rafiki instance. + /// The cursor key to list from. + /// The number of items to return after the cursor. + /// The number of items to return before the cursor. + /// The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. + /// The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. + /// OK + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task ListOutgoingPaymentsAsync(string wallet_address, string cursor, int? first, int? last, string signature_Input, string signature, System.Threading.CancellationToken cancellationToken) + { + if (wallet_address == null) + throw new System.ArgumentNullException("wallet_address"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + + if (signature_Input == null) + throw new System.ArgumentNullException("signature_Input"); + request_.Headers.TryAddWithoutValidation("Signature-Input", ConvertToString(signature_Input, System.Globalization.CultureInfo.InvariantCulture)); + + if (signature == null) + throw new System.ArgumentNullException("signature"); + request_.Headers.TryAddWithoutValidation("Signature", ConvertToString(signature, System.Globalization.CultureInfo.InvariantCulture)); + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "outgoing-payments" + urlBuilder_.Append("outgoing-payments"); + urlBuilder_.Append('?'); + urlBuilder_.Append(System.Uri.EscapeDataString("wallet-address")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(wallet_address, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + if (cursor != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("cursor")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(cursor, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + } + if (first != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("first")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(first, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + } + if (last != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("last")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(last, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + } + urlBuilder_.Length--; + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Authorization required", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 403) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Forbidden", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// + /// Create a Quote + /// + /// + /// A **quote** is a sub-resource of a wallet address. It represents a quote for a payment from the wallet address. + /// + /// A subset of the quotes schema is accepted as input to create a new quote. + ///
+ ///
The quote must be created with a (`debitAmount` xor `receiveAmount`) unless the `receiver` is an Incoming Payment which has an `incomingAmount`. + /// The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. + /// The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. + /// Quote Created + /// A server side error occurred. + public virtual System.Threading.Tasks.Task CreateQuoteAsync(Body3 body, string signature_Input, string signature) + { + return CreateQuoteAsync(body, signature_Input, signature, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Create a Quote + /// + /// + /// A **quote** is a sub-resource of a wallet address. It represents a quote for a payment from the wallet address. + /// + /// A subset of the quotes schema is accepted as input to create a new quote. + ///
+ ///
The quote must be created with a (`debitAmount` xor `receiveAmount`) unless the `receiver` is an Incoming Payment which has an `incomingAmount`. + /// The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. + /// The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. + /// Quote Created + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task CreateQuoteAsync(Body3 body, string signature_Input, string signature, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + + if (signature_Input == null) + throw new System.ArgumentNullException("signature_Input"); + request_.Headers.TryAddWithoutValidation("Signature-Input", ConvertToString(signature_Input, System.Globalization.CultureInfo.InvariantCulture)); + + if (signature == null) + throw new System.ArgumentNullException("signature"); + request_.Headers.TryAddWithoutValidation("Signature", ConvertToString(signature, System.Globalization.CultureInfo.InvariantCulture)); + var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(body, JsonSerializerSettings); + var content_ = new System.Net.Http.StringContent(json_); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "quotes" + urlBuilder_.Append("quotes"); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 201) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad Request", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Authorization required", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 403) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Forbidden", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// + /// Get an Incoming Payment + /// + /// + /// A client can fetch the latest state of an incoming payment to determine the amount received into the wallet address. + /// + /// The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. + /// The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. + /// Sub-resource identifier + /// Incoming Payment Found + /// A server side error occurred. + public virtual System.Threading.Tasks.Task GetIncomingPaymentAsync(string signature_Input, string signature, string id) + { + return GetIncomingPaymentAsync(signature_Input, signature, id, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Get an Incoming Payment + /// + /// + /// A client can fetch the latest state of an incoming payment to determine the amount received into the wallet address. + /// + /// The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. + /// The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. + /// Sub-resource identifier + /// Incoming Payment Found + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task GetIncomingPaymentAsync(string signature_Input, string signature, string id, System.Threading.CancellationToken cancellationToken) + { + if (id == null) + throw new System.ArgumentNullException("id"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + + if (signature_Input != null) + request_.Headers.TryAddWithoutValidation("Signature-Input", ConvertToString(signature_Input, System.Globalization.CultureInfo.InvariantCulture)); + + if (signature != null) + request_.Headers.TryAddWithoutValidation("Signature", ConvertToString(signature, System.Globalization.CultureInfo.InvariantCulture)); + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "incoming-payments/{id}" + urlBuilder_.Append("incoming-payments/"); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Authorization required", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 403) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Forbidden", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not Found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// + /// Complete an Incoming Payment + /// + /// + /// A client with the appropriate permissions MAY mark a non-expired **incoming payment** as `completed` indicating that the client is not going to make any further payments toward this **incoming payment**, even though the full `incomingAmount` may not have been received. + ///
+ ///
This indicates to the receiving Account Servicing Entity that it can begin any post processing of the payment such as generating account statements or notifying the account holder of the completed payment. + ///
+ /// The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. + /// The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. + /// Sub-resource identifier + /// OK + /// A server side error occurred. + public virtual System.Threading.Tasks.Task CompleteIncomingPaymentAsync(string signature_Input, string signature, string id) + { + return CompleteIncomingPaymentAsync(signature_Input, signature, id, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Complete an Incoming Payment + /// + /// + /// A client with the appropriate permissions MAY mark a non-expired **incoming payment** as `completed` indicating that the client is not going to make any further payments toward this **incoming payment**, even though the full `incomingAmount` may not have been received. + ///
+ ///
This indicates to the receiving Account Servicing Entity that it can begin any post processing of the payment such as generating account statements or notifying the account holder of the completed payment. + ///
+ /// The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. + /// The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. + /// Sub-resource identifier + /// OK + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task CompleteIncomingPaymentAsync(string signature_Input, string signature, string id, System.Threading.CancellationToken cancellationToken) + { + if (id == null) + throw new System.ArgumentNullException("id"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + + if (signature_Input == null) + throw new System.ArgumentNullException("signature_Input"); + request_.Headers.TryAddWithoutValidation("Signature-Input", ConvertToString(signature_Input, System.Globalization.CultureInfo.InvariantCulture)); + + if (signature == null) + throw new System.ArgumentNullException("signature"); + request_.Headers.TryAddWithoutValidation("Signature", ConvertToString(signature, System.Globalization.CultureInfo.InvariantCulture)); + request_.Content = new System.Net.Http.StringContent(string.Empty, System.Text.Encoding.UTF8, "application/json"); + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "incoming-payments/{id}/complete" + urlBuilder_.Append("incoming-payments/"); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); + urlBuilder_.Append("/complete"); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Authorization required", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 403) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Forbidden", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not Found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// + /// Get an Outgoing Payment + /// + /// + /// A client can fetch the latest state of an outgoing payment. + /// + /// The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. + /// The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. + /// Sub-resource identifier + /// Outgoing Payment Found + /// A server side error occurred. + public virtual System.Threading.Tasks.Task GetOutgoingPaymentAsync(string signature_Input, string signature, string id) + { + return GetOutgoingPaymentAsync(signature_Input, signature, id, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Get an Outgoing Payment + /// + /// + /// A client can fetch the latest state of an outgoing payment. + /// + /// The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. + /// The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. + /// Sub-resource identifier + /// Outgoing Payment Found + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task GetOutgoingPaymentAsync(string signature_Input, string signature, string id, System.Threading.CancellationToken cancellationToken) + { + if (id == null) + throw new System.ArgumentNullException("id"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + + if (signature_Input == null) + throw new System.ArgumentNullException("signature_Input"); + request_.Headers.TryAddWithoutValidation("Signature-Input", ConvertToString(signature_Input, System.Globalization.CultureInfo.InvariantCulture)); + + if (signature == null) + throw new System.ArgumentNullException("signature"); + request_.Headers.TryAddWithoutValidation("Signature", ConvertToString(signature, System.Globalization.CultureInfo.InvariantCulture)); + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "outgoing-payments/{id}" + urlBuilder_.Append("outgoing-payments/"); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Authorization required", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 403) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Forbidden", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not Found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// + /// Get a Quote + /// + /// + /// A client can fetch the latest state of a quote. + /// + /// The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. + /// The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. + /// Sub-resource identifier + /// Quote Found + /// A server side error occurred. + public virtual System.Threading.Tasks.Task GetQuoteAsync(string signature_Input, string signature, string id) + { + return GetQuoteAsync(signature_Input, signature, id, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Get a Quote + /// + /// + /// A client can fetch the latest state of a quote. + /// + /// The Signature-Input field is a Dictionary structured field containing the metadata for one or more message signatures generated from components within the HTTP message. Each member describes a single message signature. The member's key is the label that uniquely identifies the message signature within the context of the HTTP message. The member's value is the serialization of the covered components Inner List plus all signature metadata parameters identified by the label. The following components MUST be included: - "@method" - "@target-uri" - "authorization". When the message contains a request body, the covered components MUST also include the following: - "content-digest" The keyid parameter of the signature MUST be set to the kid value of the JWK. See [ietf-httpbis-message-signatures](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-message-signatures#section-4.1) for more details. + /// The signature generated based on the Signature-Input, using the signing algorithm specified in the "alg" field of the JWK. + /// Sub-resource identifier + /// Quote Found + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task GetQuoteAsync(string signature_Input, string signature, string id, System.Threading.CancellationToken cancellationToken) + { + if (id == null) + throw new System.ArgumentNullException("id"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + + if (signature_Input == null) + throw new System.ArgumentNullException("signature_Input"); + request_.Headers.TryAddWithoutValidation("Signature-Input", ConvertToString(signature_Input, System.Globalization.CultureInfo.InvariantCulture)); + + if (signature == null) + throw new System.ArgumentNullException("signature"); + request_.Headers.TryAddWithoutValidation("Signature", ConvertToString(signature, System.Globalization.CultureInfo.InvariantCulture)); + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "quotes/{id}" + urlBuilder_.Append("quotes/"); + urlBuilder_.Append(System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 401) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Authorization required", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 403) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Forbidden", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 404) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not Found", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + { + var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + protected struct ObjectResponseResult + { + public ObjectResponseResult(T responseObject, string responseText) + { + this.Object = responseObject; + this.Text = responseText; + } + + public T Object { get; } + + public string Text { get; } + } + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private static System.Threading.Tasks.Task ReadAsStringAsync(System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) + { + #if NET5_0_OR_GREATER + return content.ReadAsStringAsync(cancellationToken); + #else + return content.ReadAsStringAsync(); + #endif + } + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private static System.Threading.Tasks.Task ReadAsStreamAsync(System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) + { + #if NET5_0_OR_GREATER + return content.ReadAsStreamAsync(cancellationToken); + #else + return content.ReadAsStreamAsync(); + #endif + } + + public bool ReadResponseAsString { get; set; } + + protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) + { + if (response == null || response.Content == null) + { + return new ObjectResponseResult(default(T), string.Empty); + } + + if (ReadResponseAsString) + { + var responseText = await ReadAsStringAsync(response.Content, cancellationToken).ConfigureAwait(false); + try + { + var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject(responseText, JsonSerializerSettings); + return new ObjectResponseResult(typedBody, responseText); + } + catch (Newtonsoft.Json.JsonException exception) + { + var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; + throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception); + } + } + else + { + try + { + using (var responseStream = await ReadAsStreamAsync(response.Content, cancellationToken).ConfigureAwait(false)) + using (var streamReader = new System.IO.StreamReader(responseStream)) + using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader)) + { + var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings); + var typedBody = serializer.Deserialize(jsonTextReader); + return new ObjectResponseResult(typedBody, string.Empty); + } + } + catch (Newtonsoft.Json.JsonException exception) + { + var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; + throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception); + } + } + } + + private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo) + { + if (value == null) + { + return ""; + } + + if (value is System.Enum) + { + var name = System.Enum.GetName(value.GetType(), value); + if (name != null) + { + var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); + if (field != null) + { + var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute)) + as System.Runtime.Serialization.EnumMemberAttribute; + if (attribute != null) + { + return attribute.Value != null ? attribute.Value : name; + } + } + + var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); + return converted == null ? string.Empty : converted; + } + } + else if (value is bool) + { + return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); + } + else if (value is byte[]) + { + return System.Convert.ToBase64String((byte[]) value); + } + else if (value is string[]) + { + return string.Join(",", (string[])value); + } + else if (value.GetType().IsArray) + { + var valueArray = (System.Array)value; + var valueTextArray = new string[valueArray.Length]; + for (var i = 0; i < valueArray.Length; i++) + { + valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo); + } + return string.Join(",", valueTextArray); + } + + var result = System.Convert.ToString(value, cultureInfo); + return result == null ? "" : result; + } + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Amount + { + + /// + /// The value is an unsigned 64-bit integer amount, represented as a string. + /// + [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Value { get; set; } + + /// + /// The assetCode is a code that indicates the underlying asset. An ISO4217 currency code should be used whenever possible. The ISO4217 representation of the US Dollar is USD. + /// + [Newtonsoft.Json.JsonProperty("assetCode", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string AssetCode { get; set; } + + /// + /// The number of decimal places that defines the scale of the smallest divisible unit for the given asset code. It determines how an integer amount is scaled to derive the actual monetary value. For example, USD has an asset scale of 2 with the smallest unit being 0.01. An integer amount of `1000` with an `assetCode` of `USD` and `assetScale` of `2` translates to $10.00. + /// + [Newtonsoft.Json.JsonProperty("assetScale", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Range(0, 255)] + public int AssetScale { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + /// + /// An **incoming payment** resource represents a payment that will be, is currently being, or has been received by the account. + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class IncomingPayment + { + + /// + /// The URL identifying the incoming payment. + /// + [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri Id { get; set; } + + /// + /// The URL of the wallet address this payment is being made into. + /// + [Newtonsoft.Json.JsonProperty("walletAddress", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri WalletAddress { get; set; } + + /// + /// Describes whether the incoming payment has completed receiving fund. + /// + [Newtonsoft.Json.JsonProperty("completed", Required = Newtonsoft.Json.Required.Always)] + public bool Completed { get; set; } = false; + + /// + /// The maximum amount that should be paid into the wallet address under this incoming payment. + /// + [Newtonsoft.Json.JsonProperty("incomingAmount", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Amount IncomingAmount { get; set; } + + /// + /// The total amount that has been paid into the wallet address under this incoming payment. + /// + [Newtonsoft.Json.JsonProperty("receivedAmount", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public Amount ReceivedAmount { get; set; } = new Amount(); + + /// + /// The date and time when payments under this incoming payment will no longer be accepted. + /// + [Newtonsoft.Json.JsonProperty("expiresAt", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.DateTimeOffset ExpiresAt { get; set; } + + /// + /// Additional metadata associated with the incoming payment. (Optional) + /// + [Newtonsoft.Json.JsonProperty("metadata", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public object Metadata { get; set; } + + /// + /// The date and time when the incoming payment was created. + /// + [Newtonsoft.Json.JsonProperty("createdAt", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.DateTimeOffset CreatedAt { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + /// + /// An **incoming payment** resource with public details. + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class IncomingPaymentWithMethods : IncomingPayment + { + + /// + /// The list of payment methods supported by this incoming payment. + /// + [Newtonsoft.Json.JsonProperty("methods", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Methods { get; set; } = new System.Collections.ObjectModel.Collection(); + + } + + /// + /// An **incoming payment** resource with public details. + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class PublicIncomingPayment + { + + [Newtonsoft.Json.JsonProperty("receivedAmount", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Amount ReceivedAmount { get; set; } + + /// + /// The URL of the authorization server endpoint for getting grants and access tokens for this wallet address. + /// + [Newtonsoft.Json.JsonProperty("authServer", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri AuthServer { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + /// + /// An **outgoing payment** resource represents a payment that will be, is currently being, or has previously been, sent from the wallet address. + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class OutgoingPayment + { + + /// + /// The URL identifying the outgoing payment. + /// + [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri Id { get; set; } + + /// + /// The URL of the wallet address from which this payment is sent. + /// + [Newtonsoft.Json.JsonProperty("walletAddress", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri WalletAddress { get; set; } + + /// + /// The URL of the quote defining this payment's amounts. + /// + [Newtonsoft.Json.JsonProperty("quoteId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Uri QuoteId { get; set; } + + /// + /// Describes whether the payment failed to send its full amount. + /// + [Newtonsoft.Json.JsonProperty("failed", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public bool Failed { get; set; } = false; + + /// + /// The URL of the incoming payment that is being paid. + /// + [Newtonsoft.Json.JsonProperty("receiver", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [System.ComponentModel.DataAnnotations.RegularExpression(@"^(https|http)://(.+)/incoming-payments/(.+)$")] + public System.Uri Receiver { get; set; } + + /// + /// The total amount that should be received by the receiver when this outgoing payment has been paid. + /// + [Newtonsoft.Json.JsonProperty("receiveAmount", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public Amount ReceiveAmount { get; set; } = new Amount(); + + /// + /// The total amount that should be deducted from the sender's account when this outgoing payment has been paid. + /// + [Newtonsoft.Json.JsonProperty("debitAmount", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public Amount DebitAmount { get; set; } = new Amount(); + + /// + /// The total amount that has been sent under this outgoing payment. + /// + [Newtonsoft.Json.JsonProperty("sentAmount", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public Amount SentAmount { get; set; } = new Amount(); + + /// + /// Additional metadata associated with the outgoing payment. (Optional) + /// + [Newtonsoft.Json.JsonProperty("metadata", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public object Metadata { get; set; } + + /// + /// The date and time when the outgoing payment was created. + /// + [Newtonsoft.Json.JsonProperty("createdAt", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.DateTimeOffset CreatedAt { get; set; } + + } + + /// + /// An **outgoing payment** resource represents a payment that will be, is currently being, or has previously been, sent from the wallet address. + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class OutgoingPaymentWithSpentAmounts + { + + /// + /// The URL identifying the outgoing payment. + /// + [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri Id { get; set; } + + /// + /// The URL of the wallet address from which this payment is sent. + /// + [Newtonsoft.Json.JsonProperty("walletAddress", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri WalletAddress { get; set; } + + /// + /// The URL of the quote defining this payment's amounts. + /// + [Newtonsoft.Json.JsonProperty("quoteId", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Uri QuoteId { get; set; } + + /// + /// Describes whether the payment failed to send its full amount. + /// + [Newtonsoft.Json.JsonProperty("failed", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public bool Failed { get; set; } = false; + + /// + /// The URL of the incoming payment that is being paid. + /// + [Newtonsoft.Json.JsonProperty("receiver", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [System.ComponentModel.DataAnnotations.RegularExpression(@"^(https|http)://(.+)/incoming-payments/(.+)$")] + public System.Uri Receiver { get; set; } + + /// + /// The total amount that should be received by the receiver when this outgoing payment has been paid. + /// + [Newtonsoft.Json.JsonProperty("receiveAmount", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public Amount ReceiveAmount { get; set; } = new Amount(); + + /// + /// The total amount that should be deducted from the sender's account when this outgoing payment has been paid. + /// + [Newtonsoft.Json.JsonProperty("debitAmount", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public Amount DebitAmount { get; set; } = new Amount(); + + /// + /// The total amount that has been sent under this outgoing payment. + /// + [Newtonsoft.Json.JsonProperty("sentAmount", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public Amount SentAmount { get; set; } = new Amount(); + + /// + /// The total amount successfully deducted from the sender's account using the current outgoing payment grant. + /// + [Newtonsoft.Json.JsonProperty("grantSpentDebitAmount", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Amount GrantSpentDebitAmount { get; set; } + + /// + /// The total amount successfully received (by all receivers) using the current outgoing payment grant. + /// + [Newtonsoft.Json.JsonProperty("grantSpentReceiveAmount", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Amount GrantSpentReceiveAmount { get; set; } + + /// + /// Additional metadata associated with the outgoing payment. (Optional) + /// + [Newtonsoft.Json.JsonProperty("metadata", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public object Metadata { get; set; } + + /// + /// The date and time when the outgoing payment was created. + /// + [Newtonsoft.Json.JsonProperty("createdAt", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.DateTimeOffset CreatedAt { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + /// + /// A **quote** resource represents the quoted amount details with which an Outgoing Payment may be created. + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Quote + { + + /// + /// The URL identifying the quote. + /// + [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri Id { get; set; } + + /// + /// The URL of the wallet address from which this quote's payment would be sent. + /// + [Newtonsoft.Json.JsonProperty("walletAddress", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri WalletAddress { get; set; } + + /// + /// The URL of the incoming payment that the quote is created for. + /// + [Newtonsoft.Json.JsonProperty("receiver", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [System.ComponentModel.DataAnnotations.RegularExpression(@"^(https|http)://(.+)/incoming-payments/(.+)$")] + public System.Uri Receiver { get; set; } + + /// + /// The total amount that should be received by the receiver when the corresponding outgoing payment has been paid. + /// + [Newtonsoft.Json.JsonProperty("receiveAmount", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public Amount ReceiveAmount { get; set; } = new Amount(); + + /// + /// The total amount that should be deducted from the sender's account when the corresponding outgoing payment has been paid. + /// + [Newtonsoft.Json.JsonProperty("debitAmount", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public Amount DebitAmount { get; set; } = new Amount(); + + [Newtonsoft.Json.JsonProperty("method", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public PaymentMethod Method { get; set; } + + /// + /// The date and time when the calculated `debitAmount` is no longer valid. + /// + [Newtonsoft.Json.JsonProperty("expiresAt", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string ExpiresAt { get; set; } + + /// + /// The date and time when the quote was created. + /// + [Newtonsoft.Json.JsonProperty("createdAt", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.DateTimeOffset CreatedAt { get; set; } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class PageInfo + { + + /// + /// Cursor corresponding to the first element in the result array. + /// + [Newtonsoft.Json.JsonProperty("startCursor", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.StringLength(int.MaxValue, MinimumLength = 1)] + public string StartCursor { get; set; } + + /// + /// Cursor corresponding to the last element in the result array. + /// + [Newtonsoft.Json.JsonProperty("endCursor", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [System.ComponentModel.DataAnnotations.StringLength(int.MaxValue, MinimumLength = 1)] + public string EndCursor { get; set; } + + /// + /// Describes whether the data set has further entries. + /// + [Newtonsoft.Json.JsonProperty("hasNextPage", Required = Newtonsoft.Json.Required.Always)] + public bool HasNextPage { get; set; } + + /// + /// Describes whether the data set has previous entries. + /// + [Newtonsoft.Json.JsonProperty("hasPreviousPage", Required = Newtonsoft.Json.Required.Always)] + public bool HasPreviousPage { get; set; } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum PaymentMethod + { + + [System.Runtime.Serialization.EnumMember(Value = @"ilp")] + Ilp = 0, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class IlpPaymentMethod + { + + [Newtonsoft.Json.JsonProperty("type", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public IlpPaymentMethodType Type { get; set; } + + /// + /// The ILP address to use when establishing a STREAM connection. + /// + [Newtonsoft.Json.JsonProperty("ilpAddress", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [System.ComponentModel.DataAnnotations.StringLength(1023)] + [System.ComponentModel.DataAnnotations.RegularExpression(@"^(g|private|example|peer|self|test[1-3]?|local)([.][a-zA-Z0-9_~-]+)+$")] + public string IlpAddress { get; set; } + + /// + /// The base64 url-encoded shared secret to use when establishing a STREAM connection. + /// + [Newtonsoft.Json.JsonProperty("sharedSecret", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [System.ComponentModel.DataAnnotations.RegularExpression(@"^[a-zA-Z0-9-_]+$")] + public string SharedSecret { get; set; } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class ErrorResponse + { + + [Newtonsoft.Json.JsonProperty("error", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public Error Error { get; set; } = new Error(); + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Body + { + + [Newtonsoft.Json.JsonProperty("walletAddress", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri WalletAddress { get; set; } + + /// + /// The maximum amount that should be paid into the wallet address under this incoming payment. + /// + [Newtonsoft.Json.JsonProperty("incomingAmount", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public Amount IncomingAmount { get; set; } + + /// + /// The date and time when payments into the incoming payment must no longer be accepted. + /// + [Newtonsoft.Json.JsonProperty("expiresAt", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.DateTimeOffset ExpiresAt { get; set; } + + /// + /// Additional metadata associated with the incoming payment. (Optional) + /// + [Newtonsoft.Json.JsonProperty("metadata", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public object Metadata { get; set; } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Body2 + { + + [Newtonsoft.Json.JsonProperty("walletAddress", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri WalletAddress { get; set; } + + /// + /// The URL of the quote defining this payment's amounts. + /// + [Newtonsoft.Json.JsonProperty("quoteId", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri QuoteId { get; set; } + + /// + /// Additional metadata associated with the outgoing payment. (Optional) + /// + [Newtonsoft.Json.JsonProperty("metadata", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public object Metadata { get; set; } + + } + + /// + /// Create quote for an `receiver` that is an Incoming Payment with an `incomingAmount` + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Body3 + { + + [Newtonsoft.Json.JsonProperty("walletAddress", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri WalletAddress { get; set; } + + [Newtonsoft.Json.JsonProperty("receiver", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [System.ComponentModel.DataAnnotations.RegularExpression(@"^(https|http)://(.+)/incoming-payments/(.+)$")] + public System.Uri Receiver { get; set; } + + [Newtonsoft.Json.JsonProperty("method", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public PaymentMethod Method { get; set; } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Response + { + + [Newtonsoft.Json.JsonProperty("pagination", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public PageInfo Pagination { get; set; } + + [Newtonsoft.Json.JsonProperty("result", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Result { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Response2 + { + + [Newtonsoft.Json.JsonProperty("pagination", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public PageInfo Pagination { get; set; } + + [Newtonsoft.Json.JsonProperty("result", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public System.Collections.Generic.ICollection Result { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Response3 + { + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum IlpPaymentMethodType + { + + [System.Runtime.Serialization.EnumMember(Value = @"ilp")] + Ilp = 0, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Error + { + + [Newtonsoft.Json.JsonProperty("code", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Code { get; set; } + + [Newtonsoft.Json.JsonProperty("description", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Description { get; set; } + + /// + /// Additional details about the error. + /// + [Newtonsoft.Json.JsonProperty("details", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public object Details { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class ApiException : System.Exception + { + public int StatusCode { get; private set; } + + public string Response { get; private set; } + + public System.Collections.Generic.IReadOnlyDictionary> Headers { get; private set; } + + public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Exception innerException) + : base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException) + { + StatusCode = statusCode; + Response = response; + Headers = headers; + } + + public override string ToString() + { + return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString()); + } + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class ApiException : ApiException + { + public TResult Result { get; private set; } + + public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, TResult result, System.Exception innerException) + : base(message, statusCode, response, headers, innerException) + { + Result = result; + } + } + +} + +#pragma warning restore 108 +#pragma warning restore 114 +#pragma warning restore 472 +#pragma warning restore 612 +#pragma warning restore 649 +#pragma warning restore 1573 +#pragma warning restore 1591 +#pragma warning restore 8073 +#pragma warning restore 3016 +#pragma warning restore 8600 +#pragma warning restore 8602 +#pragma warning restore 8603 +#pragma warning restore 8604 +#pragma warning restore 8625 +#pragma warning restore 8765 \ No newline at end of file diff --git a/OpenPayments.Sdk/Generated/Resource/Types.cs b/OpenPayments.Sdk/Generated/Resource/Types.cs new file mode 100644 index 0000000..be6f9c1 --- /dev/null +++ b/OpenPayments.Sdk/Generated/Resource/Types.cs @@ -0,0 +1,85 @@ +using Newtonsoft.Json; + +namespace OpenPayments.Sdk.Generated.Resource +{ + public partial class IncomingPaymentBody : Body + { + /// + /// The date and time when payments into the incoming payment must no longer be accepted. + /// + [JsonProperty("expiresAt", Required = Required.AllowNull, NullValueHandling = NullValueHandling.Ignore)] + public new DateTimeOffset? ExpiresAt { get; set; } + + /// + /// Additional metadata associated with the incoming payment. (Optional) + /// + [JsonProperty("metadata", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)] + public new object? Metadata { get; set; } + } + + public partial class IncomingPaymentResponse : IncomingPaymentWithMethods + { + /// + [JsonProperty("metadata", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)] + public new object? Metadata { get; set; } + } + + public partial class QuoteBody : Body3 + { + /// + /// The fixed amount that would be paid into the receiving wallet address given a successful outgoing payment. + /// + [JsonProperty("receiveAmount")] + public Amount? ReceiveAmount { get; set; } + + /// + /// The fixed amount that would be sent from the sending wallet address given a successful outgoing payment. + /// + [JsonProperty("debitAmount")] + public Amount? DebitAmount { get; set; } + } + + public partial class QuoteResponse : Quote + { + } + + public partial class OutgoingPaymentBody : Body2 + { + /// + /// The URL of the quote defining this payment's amounts. + /// + [JsonProperty("quoteId", Required = Required.AllowNull)] + public new Uri? QuoteId { get; set; } + + /// + /// The URL of the incoming payment this outgoing payment will fulfill. + /// + [JsonProperty("incomingPayment")] + public Uri? IncomingPayment { get; set; } + + /// + /// The fixed amount that would be sent from the sending wallet address given a successful outgoing payment. + /// + [JsonProperty("debitAmount")] + public Amount? DebitAmount { get; set; } + + /// + [JsonProperty("metadata", Required = Required.Default, NullValueHandling = NullValueHandling.Ignore)] + public new object? Metadata { get; set; } + } + + public partial class OutgoingPaymentResponse : OutgoingPaymentWithSpentAmounts + { + } + + public partial class Amount + { + public Amount() {} + public Amount(string value, string assetCode, int? assetScale = 2) + { + this.Value = value; + this.AssetCode = assetCode; + this.AssetScale = assetScale ?? 2; + } + } +} \ No newline at end of file diff --git a/OpenPayments.Sdk/Generated/Types.cs b/OpenPayments.Sdk/Generated/Types.cs new file mode 100644 index 0000000..3fb89ec --- /dev/null +++ b/OpenPayments.Sdk/Generated/Types.cs @@ -0,0 +1,43 @@ +namespace OpenPayments.Sdk.Generated +{ + public abstract partial class Anonymous + { + private IDictionary? _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public IDictionary AdditionalProperties + { + get { return _additionalProperties ??= new Dictionary(); } + set => _additionalProperties = value; + } + } + + public static class Helpers + { + public static string StatusAsText(int status) + { + return status switch + { + 400 => "Bad Request", + 401 => "Unauthorized", + 403 => "Forbidden", + 404 => "Not Found", + 500 => "Internal Server Error", + _ => "Unknown Status Code" + }; + } + + public static Dictionary> ExtractHeaders(HttpResponseMessage response) + { + var headers = new Dictionary>(); + foreach (var item in response.Headers) + headers[item.Key] = item.Value; + foreach (var item in response.Content.Headers) + headers[item.Key] = item.Value; + + return headers; + } + } + +} + diff --git a/OpenPayments.Sdk/Generated/PartialWalletAddressClient.cs b/OpenPayments.Sdk/Generated/Wallet/WalletAddressClient.Methods.cs similarity index 100% rename from OpenPayments.Sdk/Generated/PartialWalletAddressClient.cs rename to OpenPayments.Sdk/Generated/Wallet/WalletAddressClient.Methods.cs diff --git a/OpenPayments.Sdk/Generated/Wallet/WalletAddressClient.g.cs b/OpenPayments.Sdk/Generated/Wallet/WalletAddressClient.g.cs new file mode 100644 index 0000000..5a83410 --- /dev/null +++ b/OpenPayments.Sdk/Generated/Wallet/WalletAddressClient.g.cs @@ -0,0 +1,739 @@ +//---------------------- +// +// Generated using the NSwag toolchain v14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org) +// +//---------------------- + +#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended." +#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword." +#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?' +#pragma warning disable 612 // Disable "CS0612 '...' is obsolete" +#pragma warning disable 649 // Disable "CS0649 Field is never assigned to, and will always have its default value null" +#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ... +#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..." +#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'" +#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant" +#pragma warning disable 8600 // Disable "CS8600 Converting null literal or possible null value to non-nullable type" +#pragma warning disable 8602 // Disable "CS8602 Dereference of a possibly null reference" +#pragma warning disable 8603 // Disable "CS8603 Possible null reference return" +#pragma warning disable 8604 // Disable "CS8604 Possible null reference argument for parameter" +#pragma warning disable 8625 // Disable "CS8625 Cannot convert null literal to non-nullable reference type" +#pragma warning disable 8765 // Disable "CS8765 Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes)." + +namespace OpenPayments.Sdk.Generated.Wallet +{ + using System = global::System; + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class WalletAddressClient + { + #pragma warning disable 8618 + private string _baseUrl; + #pragma warning restore 8618 + + private System.Net.Http.HttpClient _httpClient; + private static System.Lazy _settings = new System.Lazy(CreateSerializerSettings, true); + private Newtonsoft.Json.JsonSerializerSettings _instanceSettings; + + #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + public WalletAddressClient(System.Net.Http.HttpClient httpClient) + #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + { + BaseUrl = "https://ilp.interledger-test.dev/alice"; + _httpClient = httpClient; + Initialize(); + } + + private static Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings() + { + var settings = new Newtonsoft.Json.JsonSerializerSettings(); + UpdateJsonSerializerSettings(settings); + return settings; + } + + public string BaseUrl + { + get { return _baseUrl; } + set + { + _baseUrl = value; + if (!string.IsNullOrEmpty(_baseUrl) && !_baseUrl.EndsWith("/")) + _baseUrl += '/'; + } + } + + protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _instanceSettings ?? _settings.Value; } } + + static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); + + partial void Initialize(); + + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); + partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); + + /// + /// Get a Wallet Address + /// + /// + /// Retrieve the public information of the Wallet Address. + ///
+ ///
This end-point should be open to anonymous requests as it allows clients to verify a Wallet Address URL and get the basic information required to construct new transactions and discover the grant request URL. + ///
+ ///
The content should be slow changing and cacheable for long periods. Servers SHOULD use cache control headers. + ///
+ /// Wallet Address Found + /// A server side error occurred. + public virtual System.Threading.Tasks.Task GetWalletAddressAsync() + { + return GetWalletAddressAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Get a Wallet Address + /// + /// + /// Retrieve the public information of the Wallet Address. + ///
+ ///
This end-point should be open to anonymous requests as it allows clients to verify a Wallet Address URL and get the basic information required to construct new transactions and discover the grant request URL. + ///
+ ///
The content should be slow changing and cacheable for long periods. Servers SHOULD use cache control headers. + ///
+ /// Wallet Address Found + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task GetWalletAddressAsync(System.Threading.CancellationToken cancellationToken) + { + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "" + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 302) + { + string responseText_ = ( response_.Content == null ) ? string.Empty : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("If the `Accept` header is `text/html` in the request, the server may choose to redirect to an HTML page for the given wallet address.", status_, responseText_, headers_, null); + } + else + if (status_ == 404) + { + string responseText_ = ( response_.Content == null ) ? string.Empty : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("Wallet Address Not Found", status_, responseText_, headers_, null); + } + else + { + var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// + /// Get the keys bound to a Wallet Address + /// + /// + /// Retrieve the public keys of the Wallet Address. + /// + /// JWKS Document Found + /// A server side error occurred. + public virtual System.Threading.Tasks.Task GetWalletAddressKeysAsync() + { + return GetWalletAddressKeysAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Get the keys bound to a Wallet Address + /// + /// + /// Retrieve the public keys of the Wallet Address. + /// + /// JWKS Document Found + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task GetWalletAddressKeysAsync(System.Threading.CancellationToken cancellationToken) + { + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "jwks.json" + urlBuilder_.Append("jwks.json"); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 404) + { + string responseText_ = ( response_.Content == null ) ? string.Empty : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("JWKS Document Not Found", status_, responseText_, headers_, null); + } + else + { + var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + /// + /// Get the DID Document for this wallet + /// + /// + /// Retrieve the DID Document of the Wallet Address. + /// + /// DID Document Found + /// A server side error occurred. + public virtual System.Threading.Tasks.Task GetWalletAddressDidDocumentAsync() + { + return GetWalletAddressDidDocumentAsync(System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Get the DID Document for this wallet + /// + /// + /// Retrieve the DID Document of the Wallet Address. + /// + /// DID Document Found + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task GetWalletAddressDidDocumentAsync(System.Threading.CancellationToken cancellationToken) + { + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "did.json" + urlBuilder_.Append("did.json"); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 500) + { + string responseText_ = ( response_.Content == null ) ? string.Empty : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("DID Document not yet implemented", status_, responseText_, headers_, null); + } + else + { + var responseData_ = response_.Content == null ? null : await ReadAsStringAsync(response_.Content, cancellationToken).ConfigureAwait(false); + throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + protected struct ObjectResponseResult + { + public ObjectResponseResult(T responseObject, string responseText) + { + this.Object = responseObject; + this.Text = responseText; + } + + public T Object { get; } + + public string Text { get; } + } + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private static System.Threading.Tasks.Task ReadAsStringAsync(System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) + { + #if NET5_0_OR_GREATER + return content.ReadAsStringAsync(cancellationToken); + #else + return content.ReadAsStringAsync(); + #endif + } + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private static System.Threading.Tasks.Task ReadAsStreamAsync(System.Net.Http.HttpContent content, System.Threading.CancellationToken cancellationToken) + { + #if NET5_0_OR_GREATER + return content.ReadAsStreamAsync(cancellationToken); + #else + return content.ReadAsStreamAsync(); + #endif + } + + public bool ReadResponseAsString { get; set; } + + protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) + { + if (response == null || response.Content == null) + { + return new ObjectResponseResult(default(T), string.Empty); + } + + if (ReadResponseAsString) + { + var responseText = await ReadAsStringAsync(response.Content, cancellationToken).ConfigureAwait(false); + try + { + var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject(responseText, JsonSerializerSettings); + return new ObjectResponseResult(typedBody, responseText); + } + catch (Newtonsoft.Json.JsonException exception) + { + var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; + throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception); + } + } + else + { + try + { + using (var responseStream = await ReadAsStreamAsync(response.Content, cancellationToken).ConfigureAwait(false)) + using (var streamReader = new System.IO.StreamReader(responseStream)) + using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader)) + { + var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings); + var typedBody = serializer.Deserialize(jsonTextReader); + return new ObjectResponseResult(typedBody, string.Empty); + } + } + catch (Newtonsoft.Json.JsonException exception) + { + var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; + throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception); + } + } + } + + private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo) + { + if (value == null) + { + return ""; + } + + if (value is System.Enum) + { + var name = System.Enum.GetName(value.GetType(), value); + if (name != null) + { + var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); + if (field != null) + { + var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute)) + as System.Runtime.Serialization.EnumMemberAttribute; + if (attribute != null) + { + return attribute.Value != null ? attribute.Value : name; + } + } + + var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); + return converted == null ? string.Empty : converted; + } + } + else if (value is bool) + { + return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); + } + else if (value is byte[]) + { + return System.Convert.ToBase64String((byte[]) value); + } + else if (value is string[]) + { + return string.Join(",", (string[])value); + } + else if (value.GetType().IsArray) + { + var valueArray = (System.Array)value; + var valueTextArray = new string[valueArray.Length]; + for (var i = 0; i < valueArray.Length; i++) + { + valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo); + } + return string.Join(",", valueTextArray); + } + + var result = System.Convert.ToString(value, cultureInfo); + return result == null ? "" : result; + } + } + + /// + /// A **wallet address** resource is the root of the API and contains the public details of the financial account represented by the Wallet Address that is also the service endpoint URL. + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class WalletAddress + { + + /// + /// The URL identifying the wallet address. + /// + [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri Id { get; set; } + + /// + /// A public name for the account. This should be set by the account holder with their provider to provide a hint to counterparties as to the identity of the account holder. + /// + [Newtonsoft.Json.JsonProperty("publicName", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string PublicName { get; set; } + + /// + /// The assetCode is a code that indicates the underlying asset. An ISO4217 currency code should be used whenever possible. The ISO4217 representation of the US Dollar is USD. + /// + [Newtonsoft.Json.JsonProperty("assetCode", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string AssetCode { get; set; } + + /// + /// The number of decimal places that defines the scale of the smallest divisible unit for the given asset code. It determines how an integer amount is scaled to derive the actual monetary value. For example, USD has an asset scale of 2 with the smallest unit being 0.01. An integer amount of `1000` with an `assetCode` of `USD` and `assetScale` of `2` translates to $10.00. + /// + [Newtonsoft.Json.JsonProperty("assetScale", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Range(0, 255)] + public int AssetScale { get; set; } + + /// + /// The URL of the authorization server endpoint for getting grants and access tokens for this wallet address. + /// + [Newtonsoft.Json.JsonProperty("authServer", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri AuthServer { get; set; } + + /// + /// The URL of the resource server endpoint for performing Open Payments with this wallet address. + /// + [Newtonsoft.Json.JsonProperty("resourceServer", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public System.Uri ResourceServer { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + /// + /// A JSON Web Key Set document according to [rfc7517](https://datatracker.ietf.org/doc/html/rfc7517) listing the keys associated with this wallet address. These keys are used to sign requests made by this wallet address. + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class JsonWebKeySet + { + + [Newtonsoft.Json.JsonProperty("keys", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required] + public System.Collections.Generic.ICollection Keys { get; set; } = new System.Collections.ObjectModel.Collection(); + + } + + /// + /// A JWK representation of an Ed25519 Public Key + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class JsonWebKey + { + + [Newtonsoft.Json.JsonProperty("kid", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + public string Kid { get; set; } + + /// + /// The cryptographic algorithm family used with the key. The only allowed value is `EdDSA`. + /// + [Newtonsoft.Json.JsonProperty("alg", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public JsonWebKeyAlg Alg { get; set; } + + [Newtonsoft.Json.JsonProperty("use", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public JsonWebKeyUse Use { get; set; } + + [Newtonsoft.Json.JsonProperty("kty", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public JsonWebKeyKty Kty { get; set; } + + [Newtonsoft.Json.JsonProperty("crv", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public JsonWebKeyCrv Crv { get; set; } + + /// + /// The base64 url-encoded public key. + /// + [Newtonsoft.Json.JsonProperty("x", Required = Newtonsoft.Json.Required.Always)] + [System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)] + [System.ComponentModel.DataAnnotations.RegularExpression(@"^[a-zA-Z0-9-_]+$")] + public string X { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + /// + /// A DID Document using JSON encoding + /// + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class DidDocument + { + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum JsonWebKeyAlg + { + + [System.Runtime.Serialization.EnumMember(Value = @"EdDSA")] + EdDSA = 0, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum JsonWebKeyUse + { + + [System.Runtime.Serialization.EnumMember(Value = @"sig")] + Sig = 0, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum JsonWebKeyKty + { + + [System.Runtime.Serialization.EnumMember(Value = @"OKP")] + OKP = 0, + + } + + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum JsonWebKeyCrv + { + + [System.Runtime.Serialization.EnumMember(Value = @"Ed25519")] + Ed25519 = 0, + + } + + + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class ApiException : System.Exception + { + public int StatusCode { get; private set; } + + public string Response { get; private set; } + + public System.Collections.Generic.IReadOnlyDictionary> Headers { get; private set; } + + public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Exception innerException) + : base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException) + { + StatusCode = statusCode; + Response = response; + Headers = headers; + } + + public override string ToString() + { + return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString()); + } + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.6.2.0 (NJsonSchema v11.5.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class ApiException : ApiException + { + public TResult Result { get; private set; } + + public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary> headers, TResult result, System.Exception innerException) + : base(message, statusCode, response, headers, innerException) + { + Result = result; + } + } + +} + +#pragma warning restore 108 +#pragma warning restore 114 +#pragma warning restore 472 +#pragma warning restore 612 +#pragma warning restore 649 +#pragma warning restore 1573 +#pragma warning restore 1591 +#pragma warning restore 8073 +#pragma warning restore 3016 +#pragma warning restore 8600 +#pragma warning restore 8602 +#pragma warning restore 8603 +#pragma warning restore 8604 +#pragma warning restore 8625 +#pragma warning restore 8765 \ No newline at end of file diff --git a/OpenPayments.Sdk/OpenPayments.Sdk.csproj b/OpenPayments.Sdk/OpenPayments.Sdk.csproj index 918c2d9..95cf61b 100644 --- a/OpenPayments.Sdk/OpenPayments.Sdk.csproj +++ b/OpenPayments.Sdk/OpenPayments.Sdk.csproj @@ -13,11 +13,9 @@ + - + - \ No newline at end of file diff --git a/OpenPayments.Snippets/OpenPayments.Snippets.csproj b/OpenPayments.Snippets/OpenPayments.Snippets.csproj index 222ff35..b24a109 100644 --- a/OpenPayments.Snippets/OpenPayments.Snippets.csproj +++ b/OpenPayments.Snippets/OpenPayments.Snippets.csproj @@ -1,6 +1,7 @@  + diff --git a/OpenPayments.Snippets/Program.cs b/OpenPayments.Snippets/Program.cs index 8434d4e..6d746ea 100644 --- a/OpenPayments.Snippets/Program.cs +++ b/OpenPayments.Snippets/Program.cs @@ -1,16 +1,28 @@ using System.CommandLine; using Microsoft.Extensions.DependencyInjection; using OpenPayments.Sdk.Extensions; +using OpenPayments.Sdk.HttpSignatureUtils; +using OpenPayments.Snippets.Services.Authenticated; using OpenPayments.Snippets.Services.Unauthenticated; +using IncomingPaymentService = OpenPayments.Snippets.Services.Authenticated.IncomingPaymentService; +using PublicIncomingPaymentService = OpenPayments.Snippets.Services.Unauthenticated.IncomingPaymentService; var services = new ServiceCollection(); // let's register OP SDK here -services.UseOpenPayments(opts => { - opts.UseUnauthenticatedClient = true; +services.UseOpenPayments(opts => +{ + opts.UseAuthenticatedClient = true; + opts.KeyId = Environment.GetEnvironmentVariable("CLIENT_ID"); + opts.PrivateKey = KeyUtils.LoadPem(Environment.GetEnvironmentVariable("CLIENT_SECRET")!.Replace("\\n", "\n")); + opts.ClientUrl = new Uri(Environment.GetEnvironmentVariable("CLIENT_URL")!); }); services.AddTransient(); +services.AddTransient(); services.AddTransient(); +services.AddTransient(); +services.AddTransient(); +services.AddTransient(); var provider = services.BuildServiceProvider(); @@ -25,6 +37,43 @@ Description = "If specified, returns only the wallet address keys (JWKS)." }; +Option senderWalletAddressOption = new("--sender", "-s") +{ + Description = "The wallet address of the sender." +}; + +Option receiverWalletAddressOption = new("--receiver", "-r") +{ + Description = "The wallet address of the receiver.", + Required = true +}; + +Option amountOption = new("--amount", "-a") +{ + Description = "The amount to send. Eg: 1000 (10 Euro)", + Required = true +}; + +Option incomingPaymentIdOption = new("--incomingPaymentId", "-i") +{ + Description = "The incoming payment ID.", + Required = true +}; + +Option quoteUrlOption = new("--quoteUrl", "-q") +{ + Description = "The URL of the quote defining this payment's amounts.", + Required = true +}; +Option accessTokenValue = new("--accessToken", "-t") +{ + Description = "The access token to use for authentication.", +}; +Option tokenAction = new("--action", "-a") +{ + Description = "The action to perform on the token: 'rotate' or 'revoke'.", +}; + var rootCommand = new RootCommand("OpenPayments CLI"); var walletAddressCommand = new Command("WalletAddress") @@ -33,14 +82,39 @@ walletAddressKeysOption }; -var incomingPaymentCommand = new Command("IncomingPayment") { +var manageTokenCommand = new Command("ManageToken") +{ + resourceUrlOption, + accessTokenValue, + tokenAction +}; + +var getIncomingPaymentCommand = new Command("GetIncomingPayment") +{ resourceUrlOption }; -incomingPaymentCommand.SetAction(async result => +var createIncomingPaymentCommand = new Command("CreateIncomingPayment") +{ + receiverWalletAddressOption, + amountOption +}; +var createQuoteCommand = new Command("CreateQuote") +{ + senderWalletAddressOption, + incomingPaymentIdOption +}; +var createOutgoingPaymentCommand = new Command("CreateOutgoingPayment") +{ + senderWalletAddressOption, + quoteUrlOption, + amountOption +}; + +getIncomingPaymentCommand.SetAction(async result => { var incomingPaymentUrl = result.GetRequiredValue(resourceUrlOption); - var incomingPaymentService = provider.GetRequiredService(); + var incomingPaymentService = provider.GetRequiredService(); await incomingPaymentService.DisplayIncomingPaymentInfoAsync(incomingPaymentUrl); }); @@ -61,8 +135,66 @@ } }); +createIncomingPaymentCommand.SetAction(async result => +{ + var receiver = result.GetValue(receiverWalletAddressOption)!; + var amount = result.GetValue(amountOption)!; + + var service = provider.GetRequiredService(); + await service.CreateIncomingPaymentAsync(receiver, amount); +}); + +createQuoteCommand.SetAction(async result => +{ + var incomingPaymentUrl = result.GetValue(incomingPaymentIdOption)!; + var sender = result.GetValue(senderWalletAddressOption)!; + + var service = provider.GetRequiredService(); + await service.CreateQuoteAsync(sender, incomingPaymentUrl); +}); + +createOutgoingPaymentCommand.SetAction(async result => +{ + var sender = result.GetValue(senderWalletAddressOption)!; + var quoteUrl = result.GetValue(quoteUrlOption)!; + var debitAmount = result.GetValue(amountOption)!; + + var service = provider.GetRequiredService(); + await service.CreateOutgoingPaymentAsync(sender, quoteUrl, debitAmount); +}); + +manageTokenCommand.SetAction(async result => +{ + var tokenUrl = result.GetValue(resourceUrlOption)!; + var accessToken = result.GetValue(accessTokenValue)!; + var manageAction = result.GetValue(tokenAction); + var service = provider.GetRequiredService(); + + switch (manageAction) + { + case "revoke": + await service.RevokeTokenAsync(tokenUrl, accessToken); + return; + case "rotate": + await service.RotateTokenAsync(tokenUrl, accessToken); + break; + } +}); + +rootCommand.SetAction(_ => +{ + +}); + +// Unauthenticated rootCommand.Add(walletAddressCommand); -rootCommand.Add(incomingPaymentCommand); +rootCommand.Add(getIncomingPaymentCommand); + +// Authenticated +rootCommand.Add(createIncomingPaymentCommand); +rootCommand.Add(createQuoteCommand); +rootCommand.Add(createOutgoingPaymentCommand); + var config = new CommandLineConfiguration(rootCommand); return await config.InvokeAsync(args); \ No newline at end of file diff --git a/OpenPayments.Snippets/Services/Authenticated/IncomingPaymentService.cs b/OpenPayments.Snippets/Services/Authenticated/IncomingPaymentService.cs new file mode 100644 index 0000000..6f4a12c --- /dev/null +++ b/OpenPayments.Snippets/Services/Authenticated/IncomingPaymentService.cs @@ -0,0 +1,60 @@ +using OpenPayments.Sdk.Clients; +using OpenPayments.Sdk.Generated.Auth; +using OpenPayments.Sdk.Generated.Resource; +using Amount = OpenPayments.Sdk.Generated.Resource.Amount; + +namespace OpenPayments.Snippets.Services.Authenticated; + +public class IncomingPaymentService(IAuthenticatedClient client) +{ + public async Task CreateIncomingPaymentAsync(string receiver, string amount) + { + var waDetails = await client.GetWalletAddressAsync(receiver); + + var grant = await client.RequestGrantAsync( + new RequestArgs() + { + Url = waDetails.AuthServer + }, + new GrantCreateBody() + { + AccessToken = new AccessToken() + { + Access = + [ + new AccessItem() + { + Type = AccessType.IncomingPayment, + Actions = [Actions.Create, Actions.Read, Actions.List, Actions.Complete] + } + ] + } + } + ); + + var iPaymentResponse = await client.CreateIncomingPaymentAsync( + new AuthRequestArgs() + { + Url = waDetails.ResourceServer, + AccessToken = grant.AccessToken!.Value + }, + new IncomingPaymentBody() + { + WalletAddress = waDetails.Id, + IncomingAmount = new Amount() + { + AssetCode = waDetails.AssetCode, + AssetScale = waDetails.AssetScale, + Value = amount + } + } + ); + + Console.WriteLine("===Incoming Payment==="); + Console.WriteLine("Id: {0}", iPaymentResponse.Id); + Console.WriteLine("Amount: {0}", iPaymentResponse.IncomingAmount.Value); + Console.WriteLine("ExpiresAt: {0}", iPaymentResponse.ExpiresAt); + + return iPaymentResponse; + } +} \ No newline at end of file diff --git a/OpenPayments.Snippets/Services/Authenticated/OutgoingPaymentService.cs b/OpenPayments.Snippets/Services/Authenticated/OutgoingPaymentService.cs new file mode 100644 index 0000000..cfcaeb0 --- /dev/null +++ b/OpenPayments.Snippets/Services/Authenticated/OutgoingPaymentService.cs @@ -0,0 +1,135 @@ +using Newtonsoft.Json; +using OpenPayments.Sdk.Clients; +using OpenPayments.Sdk.Generated.Auth; +using OpenPayments.Sdk.Generated.Resource; +using Amount = OpenPayments.Sdk.Generated.Auth.Amount; + +namespace OpenPayments.Snippets.Services.Authenticated; + +public class OutgoingPaymentService(IAuthenticatedClient client) +{ + public async Task CreateOutgoingPaymentAsync(string senderWalletAddress, string quoteUrl, + string debitAmount) + { + var waDetails = await client.GetWalletAddressAsync(senderWalletAddress); + + var grantResponse = await client.RequestGrantAsync( + new RequestArgs() + { + Url = waDetails.AuthServer + }, + new GrantCreateBody() + { + AccessToken = new AccessToken() + { + Access = + [ + new AccessItem() + { + Type = AccessType.OutgoingPayment, + Actions = [Actions.Create, Actions.Read, Actions.List], + Identifier = waDetails.Id.ToString(), + Limits = new AccessLimits() + { + DebitAmount = new Amount(debitAmount, "EUR", 2) + } + } + ] + }, + Interact = new InteractRequest() + { + Start = [Start.Redirect], + // Finish = new Finish() + // { + // Method = FinishMethod.Redirect, + // Uri = new Uri("https://localhost"), + // Nonce = Guid.NewGuid().ToString(), + // } + } + } + ); + + Console.WriteLine("Visit the link below, then press enter to continue:"); + Console.WriteLine($"{grantResponse.Interact!.Redirect}"); + Console.ReadLine(); + + var tokenResponse = await client.ContinueGrantAsync( + new AuthRequestArgs() + { + Url = grantResponse.Continue.Uri, + AccessToken = grantResponse.Continue.AccessToken.Value, + } + ); + + // Create Outgoing Payment + var outgoing = await client.CreateOutgoingPaymentAsync( + new AuthRequestArgs() + { + Url = waDetails.ResourceServer, + AccessToken = tokenResponse.AccessToken!.Value + }, + new OutgoingPaymentBody() + { + WalletAddress = waDetails.Id, + QuoteId = new Uri(quoteUrl) + } + ); + + Console.WriteLine("===Outgoing Payment==="); + Console.WriteLine("Id: {0}", outgoing.Id); + Console.WriteLine("Quote: {0}", outgoing.QuoteId); + Console.WriteLine("IncomingPaymentUrl: {0}", outgoing.Receiver); + Console.WriteLine("Receive Amount: {0}", outgoing.ReceiveAmount.Value); + Console.WriteLine("Debit Amount: {0}", outgoing.DebitAmount.Value); + + return outgoing; + } + + + public async Task CreateOutgoingPaymentGrantAndCancelAsync(string senderWalletAddress) + { + var waDetails = await client.GetWalletAddressAsync(senderWalletAddress); + + var response = await client.RequestGrantAsync( + new RequestArgs() + { + Url = waDetails.AuthServer + }, + new GrantCreateBody() + { + AccessToken = new AccessToken() + { + Access = + [ + new AccessItem() + { + Type = AccessType.OutgoingPayment, + Actions = [Actions.Create, Actions.Read, Actions.List], + Identifier = waDetails.Id.ToString(), + Limits = new AccessLimits() + { + DebitAmount = new Amount("100", "EUR", 2) + } + } + ] + }, + Interact = new InteractRequest() + { + Start = [Start.Redirect] + } + } + ); + + // Console.WriteLine(JsonSerializer.Serialize(response, new JsonSerializerOptions { WriteIndented = true })); + Console.WriteLine(JsonConvert.SerializeObject(response, Formatting.Indented)); + Console.ReadLine(); + + await client.CancelGrantAsync( + new AuthRequestArgs() + { + Url = response.Continue.Uri, + AccessToken = response.Continue.AccessToken.Value, + } + ); + } +} \ No newline at end of file diff --git a/OpenPayments.Snippets/Services/Authenticated/QuoteService.cs b/OpenPayments.Snippets/Services/Authenticated/QuoteService.cs new file mode 100644 index 0000000..d66ee82 --- /dev/null +++ b/OpenPayments.Snippets/Services/Authenticated/QuoteService.cs @@ -0,0 +1,56 @@ +using Newtonsoft.Json; +using OpenPayments.Sdk.Clients; +using OpenPayments.Sdk.Generated.Auth; +using OpenPayments.Sdk.Generated.Resource; + +namespace OpenPayments.Snippets.Services.Authenticated; + +public class QuoteService(IAuthenticatedClient client) +{ + public async Task CreateQuoteAsync(string senderWalletAddress, string incomingPaymentUrl) + { + var waDetails = await client.GetWalletAddressAsync(senderWalletAddress); + + var authResponse = await client.RequestGrantAsync( + new RequestArgs() + { + Url = waDetails.AuthServer + }, + new GrantCreateBody() + { + AccessToken = new AccessToken() + { + Access = + [ + new AccessItem() + { + Type = AccessType.Quote, + Actions = [Actions.Create, Actions.Read] + } + ] + } + } + ); + + var quote = await client.CreateQuoteAsync( + new AuthRequestArgs() + { + Url = waDetails.ResourceServer, + AccessToken = authResponse.AccessToken!.Value, + }, new QuoteBody() + { + WalletAddress = waDetails.Id, + Receiver = new Uri(incomingPaymentUrl), + Method = PaymentMethod.Ilp, + } + ); + + Console.WriteLine("===Quote==="); + Console.WriteLine("Id: {0}", quote.Id); + Console.WriteLine("IncomingPaymentUrl: {0}", quote.Receiver); + Console.WriteLine("Receive Amount: {0}", quote.ReceiveAmount.Value); + Console.WriteLine("Debit Amount: {0}", quote.DebitAmount.Value); + + return quote; + } +} \ No newline at end of file diff --git a/OpenPayments.Snippets/Services/Authenticated/TokenService.cs b/OpenPayments.Snippets/Services/Authenticated/TokenService.cs new file mode 100644 index 0000000..ca9660a --- /dev/null +++ b/OpenPayments.Snippets/Services/Authenticated/TokenService.cs @@ -0,0 +1,86 @@ +using Newtonsoft.Json; +using OpenPayments.Sdk.Clients; +using OpenPayments.Sdk.Generated.Auth; + +namespace OpenPayments.Snippets.Services.Authenticated; + +public class TokenService(IAuthenticatedClient client) +{ + public async Task CreateAndRotateTokenAsync(string senderWalletAddress) + { + var waDetails = await client.GetWalletAddressAsync(senderWalletAddress); + + var grantResponse = await client.RequestGrantAsync( + new RequestArgs() + { + Url = waDetails.AuthServer + }, + new GrantCreateBody() + { + AccessToken = new AccessToken() + { + Access = + [ + new AccessItem() + { + Type = AccessType.OutgoingPayment, + Actions = [Actions.Create, Actions.Read, Actions.List], + Identifier = waDetails.Id.ToString(), + Limits = new AccessLimits() + { + DebitAmount = new Amount("10000", "EUR"), + } + } + ], + ExpiresIn = 600 + }, + Interact = new InteractRequest() + { + Start = [Start.Redirect] + } + } + ); + + // Console.WriteLine(JsonSerializer.Serialize(response, new JsonSerializerOptions { WriteIndented = true })); + Console.WriteLine(JsonConvert.SerializeObject(grantResponse, Formatting.Indented)); + Console.ReadLine(); + + var tokenResponse = await client.ContinueGrantAsync( + new AuthRequestArgs() + { + Url = grantResponse.Continue.Uri, + AccessToken = grantResponse.Continue.AccessToken.Value, + } + ); + + Console.WriteLine(JsonConvert.SerializeObject(tokenResponse, Formatting.Indented)); + Console.ReadLine(); + + var rotatedToken = await RotateTokenAsync(tokenResponse.AccessToken!.Manage, tokenResponse.AccessToken.Value); + Console.ReadLine(); + + await RevokeTokenAsync(rotatedToken.AccessToken.Manage, rotatedToken.AccessToken.Value); + } + + public async Task RotateTokenAsync(string tokenUrl, string accessTokenValue) + { + var rotatedToken = await client.RotateTokenAsync(new AuthRequestArgs() + { + Url = new Uri(tokenUrl), + AccessToken = accessTokenValue, + }); + + Console.WriteLine(JsonConvert.SerializeObject(rotatedToken, Formatting.Indented)); + + return rotatedToken; + } + + public async Task RevokeTokenAsync(string tokenUrl, string accessTokenValue) + { + await client.RevokeTokenAsync(new AuthRequestArgs() + { + Url = new Uri(tokenUrl), + AccessToken = accessTokenValue, + }); + } +} \ No newline at end of file diff --git a/OpenPayments.Snippets/Services/Unauthenticated/IncomingPaymentService.cs b/OpenPayments.Snippets/Services/Unauthenticated/IncomingPaymentService.cs index b2efe27..2d0b2d1 100644 --- a/OpenPayments.Snippets/Services/Unauthenticated/IncomingPaymentService.cs +++ b/OpenPayments.Snippets/Services/Unauthenticated/IncomingPaymentService.cs @@ -8,7 +8,7 @@ public class IncomingPaymentService(IUnauthenticatedClient client) public async Task DisplayIncomingPaymentInfoAsync(string incomingPaymentUrl) { - var incomingPayment = await client.GetIncomingPaymentAsync(incomingPaymentUrl); + var incomingPayment = await _client.GetIncomingPaymentAsync(incomingPaymentUrl); Console.WriteLine("===Incoming Payment Info==="); Console.WriteLine("AssetCode: {0}", incomingPayment.ReceivedAmount.AssetCode); Console.WriteLine("AssetScale: {0}", incomingPayment.ReceivedAmount.AssetScale); diff --git a/OpenPayments.Snippets/Services/Unauthenticated/WalletAddressService.cs b/OpenPayments.Snippets/Services/Unauthenticated/WalletAddressService.cs index 55d4c78..7bb3b63 100644 --- a/OpenPayments.Snippets/Services/Unauthenticated/WalletAddressService.cs +++ b/OpenPayments.Snippets/Services/Unauthenticated/WalletAddressService.cs @@ -24,7 +24,7 @@ public async Task DisplayWalletAddressKeysAsync(string address) public async Task DisplayWalletInfoAsync(string address) { - var walletAddressData = await client.GetWalletAddressAsync(address); + var walletAddressData = await _client.GetWalletAddressAsync(address); Console.WriteLine("===Wallet Info==="); Console.WriteLine("PublicName: {0}", walletAddressData.PublicName); Console.WriteLine("AssetCode: {0}", walletAddressData.AssetCode); diff --git a/OpenPayments.Snippets/example.env b/OpenPayments.Snippets/example.env new file mode 100644 index 0000000..921470d --- /dev/null +++ b/OpenPayments.Snippets/example.env @@ -0,0 +1,3 @@ +CLIENT_SECRET="-----BEGIN PRIVATE KEY-----\n\n-----END PRIVATE KEY-----" +CLIENT_ID="12345678-1234-1234-4321-87654321" +CLIENT_URL="https://ilp.interledger-test.dev/test" diff --git a/OpenPayments.sln b/OpenPayments.sln index 5f12d56..a173f1b 100644 --- a/OpenPayments.sln +++ b/OpenPayments.sln @@ -54,11 +54,11 @@ Global {08C04CD3-3069-4D89-879B-31758F659173}.Debug|x86.ActiveCfg = Debug|Any CPU {08C04CD3-3069-4D89-879B-31758F659173}.Debug|x86.Build.0 = Debug|Any CPU {08C04CD3-3069-4D89-879B-31758F659173}.Release|Any CPU.ActiveCfg = Release|Any CPU - {08C04CD3-3069-4D89-879B-31758F659173}.Release|Any CPU.Build.0 = Release|Any CPU {08C04CD3-3069-4D89-879B-31758F659173}.Release|x64.ActiveCfg = Release|Any CPU {08C04CD3-3069-4D89-879B-31758F659173}.Release|x64.Build.0 = Release|Any CPU {08C04CD3-3069-4D89-879B-31758F659173}.Release|x86.ActiveCfg = Release|Any CPU {08C04CD3-3069-4D89-879B-31758F659173}.Release|x86.Build.0 = Release|Any CPU + {08C04CD3-3069-4D89-879B-31758F659173}.Release|Any CPU.Build.0 = Release|Any CPU {2B5ADC76-5DDC-48A6-B664-DD864279E347}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2B5ADC76-5DDC-48A6-B664-DD864279E347}.Debug|Any CPU.Build.0 = Debug|Any CPU {2B5ADC76-5DDC-48A6-B664-DD864279E347}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -78,11 +78,11 @@ Global {8A37590B-B283-49A4-AD86-23BA55B99CAE}.Debug|x86.ActiveCfg = Debug|Any CPU {8A37590B-B283-49A4-AD86-23BA55B99CAE}.Debug|x86.Build.0 = Debug|Any CPU {8A37590B-B283-49A4-AD86-23BA55B99CAE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8A37590B-B283-49A4-AD86-23BA55B99CAE}.Release|Any CPU.Build.0 = Release|Any CPU {8A37590B-B283-49A4-AD86-23BA55B99CAE}.Release|x64.ActiveCfg = Release|Any CPU {8A37590B-B283-49A4-AD86-23BA55B99CAE}.Release|x64.Build.0 = Release|Any CPU {8A37590B-B283-49A4-AD86-23BA55B99CAE}.Release|x86.ActiveCfg = Release|Any CPU {8A37590B-B283-49A4-AD86-23BA55B99CAE}.Release|x86.Build.0 = Release|Any CPU + {8A37590B-B283-49A4-AD86-23BA55B99CAE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/OpenPayments.sln.DotSettings.user b/OpenPayments.sln.DotSettings.user new file mode 100644 index 0000000..05029ac --- /dev/null +++ b/OpenPayments.sln.DotSettings.user @@ -0,0 +1,18 @@ + + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Solution /> +</SessionState> \ No newline at end of file