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.
+ ///