first
This commit is contained in:
@@ -0,0 +1,42 @@
|
|||||||
|
using Csob.Configuration;
|
||||||
|
using Csob.Credentials;
|
||||||
|
|
||||||
|
namespace Csob.Client;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scoped accessor that resolves the credentials for the current request and lazily builds a
|
||||||
|
/// single <see cref="CsobApiClient"/> (bound to the request's mutual-TLS client) shared by all
|
||||||
|
/// services handling that request.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CsobApiAccessor
|
||||||
|
{
|
||||||
|
private readonly RequestCredentialsProvider _credentialsProvider;
|
||||||
|
private readonly CsobHttpClientProvider _httpClientProvider;
|
||||||
|
private readonly CsobSettings _settings;
|
||||||
|
private CsobApiClient? _client;
|
||||||
|
|
||||||
|
public CsobApiAccessor(
|
||||||
|
RequestCredentialsProvider credentialsProvider,
|
||||||
|
CsobHttpClientProvider httpClientProvider,
|
||||||
|
CsobSettings settings)
|
||||||
|
{
|
||||||
|
_credentialsProvider = credentialsProvider;
|
||||||
|
_httpClientProvider = httpClientProvider;
|
||||||
|
_settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CsobApiClient Client
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_client is null)
|
||||||
|
{
|
||||||
|
var credentials = _credentialsProvider.Resolve();
|
||||||
|
var http = _httpClientProvider.GetClient(credentials.Certificate!);
|
||||||
|
_client = new CsobApiClient(http, credentials, _settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _client;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using Csob.Configuration;
|
||||||
|
using Csob.Credentials;
|
||||||
|
|
||||||
|
namespace Csob.Client;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Thin HTTP wrapper around the ČSOB PSD2 resource API for a single request. It attaches the
|
||||||
|
/// mandatory COBS headers (<c>Authorization</c>, <c>APIKEY</c>, <c>TPP-Name</c>, <c>X-Request-ID</c>,
|
||||||
|
/// <c>Date</c>, <c>User-Involved</c>), sends the call over the per-request mutual-TLS client and
|
||||||
|
/// returns the response JSON verbatim (<see cref="JsonNode"/>) so no field is lost in translation.
|
||||||
|
/// Non-success responses become a <see cref="CsobApiException"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CsobApiClient
|
||||||
|
{
|
||||||
|
/// <summary>Web defaults (camelCase) match the COBS JSON contract; null properties are omitted on write.</summary>
|
||||||
|
public static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
|
||||||
|
{
|
||||||
|
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull,
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly HttpClient _http;
|
||||||
|
private readonly CsobCredentials _credentials;
|
||||||
|
private readonly string _baseUrl;
|
||||||
|
|
||||||
|
public CsobApiClient(HttpClient http, CsobCredentials credentials, CsobSettings settings)
|
||||||
|
{
|
||||||
|
_http = http;
|
||||||
|
_credentials = credentials;
|
||||||
|
_baseUrl = settings.ApiBaseUrl.TrimEnd('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<JsonNode?> GetAsync(string path, IReadOnlyDictionary<string, string?>? query, CancellationToken ct)
|
||||||
|
=> SendAsync(HttpMethod.Get, path, query, body: null, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> PostAsync(string path, object? body, CancellationToken ct)
|
||||||
|
=> SendAsync(HttpMethod.Post, path, query: null, body, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> PutAsync(string path, object? body, CancellationToken ct)
|
||||||
|
=> SendAsync(HttpMethod.Put, path, query: null, body, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> DeleteAsync(string path, CancellationToken ct)
|
||||||
|
=> SendAsync(HttpMethod.Delete, path, query: null, body: null, ct);
|
||||||
|
|
||||||
|
private async Task<JsonNode?> SendAsync(
|
||||||
|
HttpMethod method, string path, IReadOnlyDictionary<string, string?>? query, object? body, CancellationToken ct)
|
||||||
|
{
|
||||||
|
using var request = new HttpRequestMessage(method, _baseUrl + path + BuildQuery(query));
|
||||||
|
ApplyHeaders(request);
|
||||||
|
|
||||||
|
if (body is not null)
|
||||||
|
{
|
||||||
|
var json = body is JsonNode node ? node.ToJsonString(JsonOptions) : JsonSerializer.Serialize(body, JsonOptions);
|
||||||
|
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||||
|
}
|
||||||
|
|
||||||
|
using var response = await _http.SendAsync(request, ct);
|
||||||
|
var payload = await response.Content.ReadAsStringAsync(ct);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
throw new CsobApiException(response.StatusCode, ParseErrorCodes(payload), payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.IsNullOrWhiteSpace(payload) ? null : JsonNode.Parse(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyHeaders(HttpRequestMessage request)
|
||||||
|
{
|
||||||
|
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _credentials.AccessToken);
|
||||||
|
request.Headers.Date = DateTimeOffset.UtcNow;
|
||||||
|
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
|
|
||||||
|
request.Headers.TryAddWithoutValidation("APIKEY", _credentials.ApiKey);
|
||||||
|
request.Headers.TryAddWithoutValidation("TPP-Name", _credentials.TppName);
|
||||||
|
request.Headers.TryAddWithoutValidation("X-Request-ID", Guid.NewGuid().ToString());
|
||||||
|
request.Headers.TryAddWithoutValidation("User-Involved", _credentials.UserInvolved ? "true" : "false");
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(_credentials.UserIpAddress))
|
||||||
|
{
|
||||||
|
request.Headers.TryAddWithoutValidation("User-IP-Address", _credentials.UserIpAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildQuery(IReadOnlyDictionary<string, string?>? query)
|
||||||
|
{
|
||||||
|
if (query is null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parts = query
|
||||||
|
.Where(kvp => !string.IsNullOrWhiteSpace(kvp.Value))
|
||||||
|
.Select(kvp => $"{Uri.EscapeDataString(kvp.Key)}={Uri.EscapeDataString(kvp.Value!)}")
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
return parts.Length == 0 ? string.Empty : "?" + string.Join("&", parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Best-effort parse of the COBS error shape <c>{ "errors": [ { "error": "CODE" } ] }</c>.</summary>
|
||||||
|
private static IReadOnlyList<string> ParseErrorCodes(string payload)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(payload))
|
||||||
|
{
|
||||||
|
return Array.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var node = JsonNode.Parse(payload);
|
||||||
|
if (node?["errors"] is JsonArray errors)
|
||||||
|
{
|
||||||
|
return errors
|
||||||
|
.Select(e => e?["error"]?.GetValue<string>())
|
||||||
|
.Where(code => !string.IsNullOrWhiteSpace(code))
|
||||||
|
.Select(code => code!)
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (JsonException)
|
||||||
|
{
|
||||||
|
// Upstream returned a non-JSON error body; the raw payload is still carried on the exception.
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.Empty<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace Csob.Client;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when the ČSOB PSD2 API returns a non-success HTTP status. Carries the upstream status
|
||||||
|
/// and the raw error body so the middleware can surface it without leaking credentials.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CsobApiException : Exception
|
||||||
|
{
|
||||||
|
public HttpStatusCode StatusCode { get; }
|
||||||
|
|
||||||
|
/// <summary>Machine-readable error codes parsed from the ČSOB <c>errors[].error</c> array (best effort).</summary>
|
||||||
|
public IReadOnlyList<string> ErrorCodes { get; }
|
||||||
|
|
||||||
|
/// <summary>Raw response body (already credential-free — it is the upstream's own error payload).</summary>
|
||||||
|
public string? RawBody { get; }
|
||||||
|
|
||||||
|
public CsobApiException(HttpStatusCode statusCode, IReadOnlyList<string> errorCodes, string? rawBody)
|
||||||
|
: base($"ČSOB PSD2 API returned {(int)statusCode} ({statusCode}).")
|
||||||
|
{
|
||||||
|
StatusCode = statusCode;
|
||||||
|
ErrorCodes = errorCodes;
|
||||||
|
RawBody = rawBody;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
namespace Csob.Client;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Central registry of ČSOB PSD2 resource path templates (relative to <c>CSOB_API_BASE_URL</c>).
|
||||||
|
///
|
||||||
|
/// Paths follow the Czech Open Banking Standard (COBS) as implemented by ČSOB. The account-scoped
|
||||||
|
/// AISP paths and the PISP <c>/my/payments/.../sign/{signId}</c> authorization flow are confirmed
|
||||||
|
/// against the ČSOB developer portal; the remaining COBS resources use the same <c>/my/</c> prefix.
|
||||||
|
/// Keep every path here so a portal-specific correction is a single-file change.
|
||||||
|
/// </summary>
|
||||||
|
public static class CsobApiPaths
|
||||||
|
{
|
||||||
|
// ----- AISP (Account Information) -----
|
||||||
|
public const string Accounts = "/my/accounts";
|
||||||
|
public static string Account(string id) => $"/my/accounts/{Esc(id)}";
|
||||||
|
public static string Balance(string accountId) => $"/my/accounts/{Esc(accountId)}/balance";
|
||||||
|
public static string Transactions(string accountId) => $"/my/accounts/{Esc(accountId)}/transactions";
|
||||||
|
public static string AwaitingTransactions(string accountId) => $"/my/accounts/{Esc(accountId)}/transactions/awaiting";
|
||||||
|
public static string AccountStandingOrders(string accountId) => $"/my/accounts/{Esc(accountId)}/standingorders";
|
||||||
|
public static string AccountStandingOrder(string accountId, string standingOrderId) => $"/my/accounts/{Esc(accountId)}/standingorders/{Esc(standingOrderId)}";
|
||||||
|
public static string AccountDirectDebits(string accountId) => $"/my/accounts/{Esc(accountId)}/directdebits";
|
||||||
|
|
||||||
|
// ----- PISP (Payment Initiation) -----
|
||||||
|
public const string Payments = "/my/payments";
|
||||||
|
public static string Payment(string id) => $"/my/payments/{Esc(id)}";
|
||||||
|
public static string PaymentStatus(string id) => $"/my/payments/{Esc(id)}/status";
|
||||||
|
public static string PaymentSign(string id, string signId) => $"/my/payments/{Esc(id)}/sign/{Esc(signId)}";
|
||||||
|
|
||||||
|
public const string StandingOrders = "/my/standingorders";
|
||||||
|
public static string StandingOrder(string id) => $"/my/standingorders/{Esc(id)}";
|
||||||
|
public static string StandingOrderStatus(string id) => $"/my/standingorders/{Esc(id)}/status";
|
||||||
|
public static string StandingOrderSign(string id, string signId) => $"/my/standingorders/{Esc(id)}/sign/{Esc(signId)}";
|
||||||
|
|
||||||
|
public const string DirectDebits = "/my/directdebits";
|
||||||
|
public static string DirectDebit(string id) => $"/my/directdebits/{Esc(id)}";
|
||||||
|
public static string DirectDebitStatus(string id) => $"/my/directdebits/{Esc(id)}/status";
|
||||||
|
public static string DirectDebitSign(string id, string signId) => $"/my/directdebits/{Esc(id)}/sign/{Esc(signId)}";
|
||||||
|
|
||||||
|
// ----- Common (Consents) -----
|
||||||
|
public const string Consents = "/consents";
|
||||||
|
public static string Consent(string id) => $"/consents/{Esc(id)}";
|
||||||
|
|
||||||
|
private static string Esc(string segment) => Uri.EscapeDataString(segment);
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using Csob.Configuration;
|
||||||
|
|
||||||
|
namespace Csob.Client;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides <see cref="HttpClient"/> instances configured for mutual TLS with a per-request eIDAS
|
||||||
|
/// client certificate. ČSOB requires the client certificate at the transport layer, so a single
|
||||||
|
/// shared client cannot be used across tenants.
|
||||||
|
///
|
||||||
|
/// Clients are cached by certificate thumbprint and reused for connection pooling. In practice the
|
||||||
|
/// certificate identifies the TPP application (not the PSU), so the number of distinct certificates
|
||||||
|
/// is small and bounded by the set of calling tenants. Certificates and clients live in memory only
|
||||||
|
/// and never touch disk.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CsobHttpClientProvider : IDisposable
|
||||||
|
{
|
||||||
|
private readonly CsobSettings _settings;
|
||||||
|
private readonly ConcurrentDictionary<string, HttpClient> _clients = new();
|
||||||
|
private readonly object _buildLock = new();
|
||||||
|
|
||||||
|
public CsobHttpClientProvider(CsobSettings settings) => _settings = settings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a (cached) mutual-TLS <see cref="HttpClient"/> presenting <paramref name="certificate"/>.
|
||||||
|
/// A fresh certificate instance is built per request; if an equivalent one (same thumbprint) is
|
||||||
|
/// already cached, the redundant instance is disposed so it does not leak.
|
||||||
|
/// </summary>
|
||||||
|
public HttpClient GetClient(X509Certificate2 certificate)
|
||||||
|
{
|
||||||
|
var thumbprint = certificate.Thumbprint;
|
||||||
|
|
||||||
|
if (_clients.TryGetValue(thumbprint, out var existing))
|
||||||
|
{
|
||||||
|
certificate.Dispose();
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_buildLock)
|
||||||
|
{
|
||||||
|
if (_clients.TryGetValue(thumbprint, out existing))
|
||||||
|
{
|
||||||
|
certificate.Dispose();
|
||||||
|
return existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
var client = BuildClient(certificate);
|
||||||
|
_clients[thumbprint] = client;
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpClient BuildClient(X509Certificate2 certificate)
|
||||||
|
{
|
||||||
|
var handler = new SocketsHttpHandler
|
||||||
|
{
|
||||||
|
PooledConnectionLifetime = TimeSpan.FromMinutes(10),
|
||||||
|
};
|
||||||
|
handler.SslOptions.ClientCertificates = new X509CertificateCollection { certificate };
|
||||||
|
|
||||||
|
return new HttpClient(handler, disposeHandler: true)
|
||||||
|
{
|
||||||
|
Timeout = TimeSpan.FromSeconds(_settings.RequestTimeoutSeconds),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (var client in _clients.Values)
|
||||||
|
{
|
||||||
|
client.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_clients.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
namespace Csob.Configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Service configuration resolved from environment variables.
|
||||||
|
///
|
||||||
|
/// This service is a stateless, multi-tenant proxy in front of the ČSOB PSD2 (Open Banking)
|
||||||
|
/// API. Unlike the sibling iDoklad service, it holds <b>no per-client secrets</b>: the eIDAS
|
||||||
|
/// client certificate, OAuth client id/secret, access token, API key and TPP name are all
|
||||||
|
/// supplied per request as HTTP headers by the calling client service
|
||||||
|
/// (see <see cref="Credentials.CredentialConstants"/>). Only non-secret infrastructure
|
||||||
|
/// configuration (API/OAuth base URLs, app metadata, reverse-proxy prefix, timeout) lives here.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CsobSettings
|
||||||
|
{
|
||||||
|
public string AppName { get; init; } = GetEnv("APP_NAME", "ČSOB PSD2 Service");
|
||||||
|
public string AppVersion { get; init; } = GetEnv("APP_VERSION", "1.0.0");
|
||||||
|
|
||||||
|
/// <summary>Public reverse-proxy prefix (e.g. <c>/apps/csob</c>) injected by AppFactory.</summary>
|
||||||
|
public string RootPath { get; init; } = GetEnv("ROOT_PATH", string.Empty);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base URL of the ČSOB PSD2 resource API. Production default; override for any other
|
||||||
|
/// environment. All AISP/PISP/consent path templates are appended to this base.
|
||||||
|
/// </summary>
|
||||||
|
public string ApiBaseUrl { get; init; } =
|
||||||
|
GetEnv("CSOB_API_BASE_URL", "https://api.csob.cz/api/csob/psd2/v1");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OAuth2 authorization endpoint (Authorization Code flow, PSU redirect). Production default;
|
||||||
|
/// verify against the current ČSOB developer portal as the host may change.
|
||||||
|
/// </summary>
|
||||||
|
public string OAuthAuthorizeUrl { get; init; } =
|
||||||
|
GetEnv("CSOB_OAUTH_AUTHORIZE_URL", "https://identita.csob.cz/mep/fs/fl/oauth2/auth");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OAuth2 token endpoint (code->token and refresh). Production default; verify against the
|
||||||
|
/// current ČSOB developer portal.
|
||||||
|
/// </summary>
|
||||||
|
public string OAuthTokenUrl { get; init; } =
|
||||||
|
GetEnv("CSOB_OAUTH_TOKEN_URL", "https://api.csob.cz/api/csob/oauth2/v1/token");
|
||||||
|
|
||||||
|
/// <summary>Upstream HTTP request timeout in seconds.</summary>
|
||||||
|
public int RequestTimeoutSeconds { get; init; } =
|
||||||
|
ParseInt(GetEnv("CSOB_REQUEST_TIMEOUT_SECONDS", "100"), 100);
|
||||||
|
|
||||||
|
private static string GetEnv(string name, string fallback)
|
||||||
|
{
|
||||||
|
var value = Environment.GetEnvironmentVariable(name);
|
||||||
|
return string.IsNullOrWhiteSpace(value) ? fallback : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ParseInt(string value, int fallback) =>
|
||||||
|
int.TryParse(value, out var parsed) && parsed > 0 ? parsed : fallback;
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Csob.Services;
|
||||||
|
|
||||||
|
namespace Csob.Controllers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AISP – account information. All endpoints require the ČSOB credential headers
|
||||||
|
/// (eIDAS certificate, access token, API key, TPP name). See the Swagger description for details.
|
||||||
|
/// </summary>
|
||||||
|
[ApiController]
|
||||||
|
[Route("accounts")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
[Tags("AISP – Accounts")]
|
||||||
|
public sealed class AccountsController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly AccountsService _service;
|
||||||
|
|
||||||
|
public AccountsController(AccountsService service) => _service = service;
|
||||||
|
|
||||||
|
/// <summary>List the PSU's payment accounts (paged).</summary>
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> List(
|
||||||
|
[FromQuery] int? page, [FromQuery] int? size, [FromQuery] string? sort, [FromQuery] string? order, CancellationToken ct)
|
||||||
|
=> Ok(await _service.ListAsync(page, size, sort, order, ct));
|
||||||
|
|
||||||
|
/// <summary>Get the balance(s) of an account.</summary>
|
||||||
|
[HttpGet("{id}/balance")]
|
||||||
|
public async Task<IActionResult> Balance(string id, [FromQuery] string? currency, CancellationToken ct)
|
||||||
|
=> Ok(await _service.BalanceAsync(id, currency, ct));
|
||||||
|
|
||||||
|
/// <summary>List booked transactions of an account (paged, optional date range).</summary>
|
||||||
|
[HttpGet("{id}/transactions")]
|
||||||
|
public async Task<IActionResult> Transactions(
|
||||||
|
string id,
|
||||||
|
[FromQuery] int? page, [FromQuery] int? size, [FromQuery] string? sort, [FromQuery] string? order,
|
||||||
|
[FromQuery] string? dateFrom, [FromQuery] string? dateTo, CancellationToken ct)
|
||||||
|
=> Ok(await _service.TransactionsAsync(id, new TransactionQuery
|
||||||
|
{
|
||||||
|
Page = page, Size = size, Sort = sort, Order = order, DateFrom = dateFrom, DateTo = dateTo,
|
||||||
|
}, ct));
|
||||||
|
|
||||||
|
/// <summary>List awaiting (pending) transactions of an account.</summary>
|
||||||
|
[HttpGet("{id}/transactions/awaiting")]
|
||||||
|
public async Task<IActionResult> Awaiting(string id, [FromQuery] int? page, [FromQuery] int? size, CancellationToken ct)
|
||||||
|
=> Ok(await _service.AwaitingTransactionsAsync(id, page, size, ct));
|
||||||
|
|
||||||
|
/// <summary>List the account's existing standing orders.</summary>
|
||||||
|
[HttpGet("{id}/standing-orders")]
|
||||||
|
public async Task<IActionResult> StandingOrders(string id, [FromQuery] int? page, [FromQuery] int? size, CancellationToken ct)
|
||||||
|
=> Ok(await _service.StandingOrdersAsync(id, page, size, ct));
|
||||||
|
|
||||||
|
/// <summary>Get a standing-order detail for the account.</summary>
|
||||||
|
[HttpGet("{id}/standing-orders/{standingOrderId}")]
|
||||||
|
public async Task<IActionResult> StandingOrderDetail(string id, string standingOrderId, CancellationToken ct)
|
||||||
|
=> Ok(await _service.StandingOrderDetailAsync(id, standingOrderId, ct));
|
||||||
|
|
||||||
|
/// <summary>List the account's direct-debit mandates.</summary>
|
||||||
|
[HttpGet("{id}/direct-debits")]
|
||||||
|
public async Task<IActionResult> DirectDebits(string id, [FromQuery] int? page, [FromQuery] int? size, CancellationToken ct)
|
||||||
|
=> Ok(await _service.DirectDebitsAsync(id, page, size, ct));
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Csob.Services;
|
||||||
|
|
||||||
|
namespace Csob.Controllers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Common – PSU consent lifecycle. Requires the ČSOB credential headers. The consent body is
|
||||||
|
/// forwarded to ČSOB as raw JSON (COBS consent shape).
|
||||||
|
/// </summary>
|
||||||
|
[ApiController]
|
||||||
|
[Route("consents")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
[Tags("Consents")]
|
||||||
|
public sealed class ConsentsController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ConsentsService _service;
|
||||||
|
|
||||||
|
public ConsentsController(ConsentsService service) => _service = service;
|
||||||
|
|
||||||
|
/// <summary>Create a consent.</summary>
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Create([FromBody] JsonNode request, CancellationToken ct)
|
||||||
|
=> Ok(await _service.CreateAsync(request, ct));
|
||||||
|
|
||||||
|
/// <summary>Get a consent detail.</summary>
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<IActionResult> Detail(string id, CancellationToken ct)
|
||||||
|
=> Ok(await _service.DetailAsync(id, ct));
|
||||||
|
|
||||||
|
/// <summary>Revoke a consent.</summary>
|
||||||
|
[HttpDelete("{id}")]
|
||||||
|
public async Task<IActionResult> Delete(string id, CancellationToken ct)
|
||||||
|
=> Ok(await _service.DeleteAsync(id, ct));
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using Csob.Services;
|
||||||
|
|
||||||
|
namespace Csob.Controllers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PISP – direct-debit mandate initiation and its authorization (sign) flow. Requires the ČSOB
|
||||||
|
/// credential headers. The mandate body is forwarded to ČSOB as raw JSON (COBS direct-debit shape).
|
||||||
|
/// </summary>
|
||||||
|
[ApiController]
|
||||||
|
[Route("direct-debits")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
[Tags("PISP – Direct debits")]
|
||||||
|
public sealed class DirectDebitsController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly DirectDebitsService _service;
|
||||||
|
|
||||||
|
public DirectDebitsController(DirectDebitsService service) => _service = service;
|
||||||
|
|
||||||
|
/// <summary>Initiate a direct-debit mandate.</summary>
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Initiate([FromBody] JsonNode request, CancellationToken ct)
|
||||||
|
=> Ok(await _service.InitiateAsync(request, ct));
|
||||||
|
|
||||||
|
/// <summary>Get the direct-debit mandate detail.</summary>
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<IActionResult> Detail(string id, CancellationToken ct)
|
||||||
|
=> Ok(await _service.DetailAsync(id, ct));
|
||||||
|
|
||||||
|
/// <summary>Get the direct-debit instruction status.</summary>
|
||||||
|
[HttpGet("{id}/status")]
|
||||||
|
public async Task<IActionResult> Status(string id, CancellationToken ct)
|
||||||
|
=> Ok(await _service.StatusAsync(id, ct));
|
||||||
|
|
||||||
|
/// <summary>Revoke a direct-debit mandate.</summary>
|
||||||
|
[HttpDelete("{id}")]
|
||||||
|
public async Task<IActionResult> Cancel(string id, CancellationToken ct)
|
||||||
|
=> Ok(await _service.CancelAsync(id, ct));
|
||||||
|
|
||||||
|
/// <summary>Start the authorization (SCA) of a direct-debit mandate. Returns the PSU redirect details.</summary>
|
||||||
|
[HttpPost("{id}/sign/{signId}")]
|
||||||
|
public async Task<IActionResult> StartSign(string id, string signId, [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] JsonNode? body, CancellationToken ct)
|
||||||
|
=> Ok(await _service.StartSignAsync(id, signId, body, ct));
|
||||||
|
|
||||||
|
/// <summary>Get the current state of an authorization (sign) transaction.</summary>
|
||||||
|
[HttpGet("{id}/sign/{signId}")]
|
||||||
|
public async Task<IActionResult> SignStatus(string id, string signId, CancellationToken ct)
|
||||||
|
=> Ok(await _service.SignStatusAsync(id, signId, ct));
|
||||||
|
|
||||||
|
/// <summary>Finalize an authorization (sign) transaction.</summary>
|
||||||
|
[HttpPut("{id}/sign/{signId}")]
|
||||||
|
public async Task<IActionResult> FinalizeSign(string id, string signId, [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] JsonNode? body, CancellationToken ct)
|
||||||
|
=> Ok(await _service.FinalizeSignAsync(id, signId, body, ct));
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Csob.Configuration;
|
||||||
|
|
||||||
|
namespace Csob.Controllers;
|
||||||
|
|
||||||
|
/// <summary>Service metadata endpoints. These do not require ČSOB credentials.</summary>
|
||||||
|
[ApiController]
|
||||||
|
[Produces("application/json")]
|
||||||
|
[Tags("Meta")]
|
||||||
|
public sealed class MetaController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly CsobSettings _settings;
|
||||||
|
|
||||||
|
public MetaController(CsobSettings settings) => _settings = settings;
|
||||||
|
|
||||||
|
/// <summary>Liveness probe.</summary>
|
||||||
|
[HttpGet("/health")]
|
||||||
|
public IActionResult Health() => Ok(new { status = "ok" });
|
||||||
|
|
||||||
|
/// <summary>Service name, version and the configured upstream endpoints (no secrets).</summary>
|
||||||
|
[HttpGet("/version")]
|
||||||
|
public IActionResult Version() => Ok(new
|
||||||
|
{
|
||||||
|
app = _settings.AppName,
|
||||||
|
version = _settings.AppVersion,
|
||||||
|
language = "dotnet",
|
||||||
|
api = "ČSOB PSD2 (Czech Open Banking Standard / COBS)",
|
||||||
|
root_path = _settings.RootPath,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reports the non-secret configuration. This service is stateless and multi-tenant: it holds
|
||||||
|
/// no credentials, so there is nothing per-client to report — every secret is supplied per
|
||||||
|
/// request via headers.
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("/status")]
|
||||||
|
public IActionResult Status() => Ok(new
|
||||||
|
{
|
||||||
|
stateless = true,
|
||||||
|
api_base_url = _settings.ApiBaseUrl,
|
||||||
|
oauth_authorize_url = _settings.OAuthAuthorizeUrl,
|
||||||
|
oauth_token_url = _settings.OAuthTokenUrl,
|
||||||
|
request_timeout_seconds = _settings.RequestTimeoutSeconds,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Csob.Models;
|
||||||
|
using Csob.Services;
|
||||||
|
|
||||||
|
namespace Csob.Controllers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OAuth2 Authorization Code helper for the ČSOB PSD2 PSU consent flow. Build the authorization URL,
|
||||||
|
/// redirect the PSU to it, then exchange the returned code for tokens. The token/refresh calls run
|
||||||
|
/// over mutual TLS, so they require the <c>X-CSOB-Certificate</c> header; client id/secret are also
|
||||||
|
/// supplied per request (this service is multi-tenant).
|
||||||
|
/// </summary>
|
||||||
|
[ApiController]
|
||||||
|
[Route("oauth")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
[Tags("OAuth2")]
|
||||||
|
public sealed class OAuthController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly OAuthService _service;
|
||||||
|
|
||||||
|
public OAuthController(OAuthService service) => _service = service;
|
||||||
|
|
||||||
|
/// <summary>Build the ČSOB authorization URL to which the PSU must be redirected.</summary>
|
||||||
|
[HttpGet("authorization-url")]
|
||||||
|
public IActionResult AuthorizationUrl(
|
||||||
|
[FromQuery] string redirectUri, [FromQuery] string? scope, [FromQuery] string? state)
|
||||||
|
=> Ok(_service.BuildAuthorizationUrl(redirectUri, scope, state));
|
||||||
|
|
||||||
|
/// <summary>Exchange an authorization code for an access/refresh token (mutual TLS).</summary>
|
||||||
|
[HttpPost("token")]
|
||||||
|
public async Task<ActionResult<JsonNode>> Token([FromBody] TokenExchangeRequest request, CancellationToken ct)
|
||||||
|
=> Ok(await _service.ExchangeCodeAsync(request, ct));
|
||||||
|
|
||||||
|
/// <summary>Refresh an access token using a refresh token (mutual TLS).</summary>
|
||||||
|
[HttpPost("refresh")]
|
||||||
|
public async Task<ActionResult<JsonNode>> Refresh([FromBody] TokenRefreshRequest request, CancellationToken ct)
|
||||||
|
=> Ok(await _service.RefreshAsync(request, ct));
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using Csob.Models;
|
||||||
|
using Csob.Services;
|
||||||
|
|
||||||
|
namespace Csob.Controllers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PISP – single payment initiation and its authorization (sign) flow. Requires the ČSOB credential
|
||||||
|
/// headers. After <c>POST /payments</c> the response carries <c>signInfo.signId</c>; use it with the
|
||||||
|
/// sign endpoints to drive Strong Customer Authentication (SCA).
|
||||||
|
/// </summary>
|
||||||
|
[ApiController]
|
||||||
|
[Route("payments")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
[Tags("PISP – Payments")]
|
||||||
|
public sealed class PaymentsController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly PaymentsService _service;
|
||||||
|
|
||||||
|
public PaymentsController(PaymentsService service) => _service = service;
|
||||||
|
|
||||||
|
/// <summary>Initiate a domestic (DMCT) or SEPA (ESCT) payment.</summary>
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Initiate([FromBody] PaymentInitiationRequest request, CancellationToken ct)
|
||||||
|
=> Ok(await _service.InitiateAsync(request, ct));
|
||||||
|
|
||||||
|
/// <summary>Get the full payment detail.</summary>
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<IActionResult> Detail(string id, CancellationToken ct)
|
||||||
|
=> Ok(await _service.DetailAsync(id, ct));
|
||||||
|
|
||||||
|
/// <summary>Get the payment instruction status.</summary>
|
||||||
|
[HttpGet("{id}/status")]
|
||||||
|
public async Task<IActionResult> Status(string id, CancellationToken ct)
|
||||||
|
=> Ok(await _service.StatusAsync(id, ct));
|
||||||
|
|
||||||
|
/// <summary>Cancel a not-yet-authorized payment.</summary>
|
||||||
|
[HttpDelete("{id}")]
|
||||||
|
public async Task<IActionResult> Cancel(string id, CancellationToken ct)
|
||||||
|
=> Ok(await _service.CancelAsync(id, ct));
|
||||||
|
|
||||||
|
/// <summary>Start the authorization (SCA) of a payment. Returns the PSU redirect details.</summary>
|
||||||
|
[HttpPost("{id}/sign/{signId}")]
|
||||||
|
public async Task<IActionResult> StartSign(string id, string signId, [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] JsonNode? body, CancellationToken ct)
|
||||||
|
=> Ok(await _service.StartSignAsync(id, signId, body, ct));
|
||||||
|
|
||||||
|
/// <summary>Get the current state of an authorization (sign) transaction.</summary>
|
||||||
|
[HttpGet("{id}/sign/{signId}")]
|
||||||
|
public async Task<IActionResult> SignStatus(string id, string signId, CancellationToken ct)
|
||||||
|
=> Ok(await _service.SignStatusAsync(id, signId, ct));
|
||||||
|
|
||||||
|
/// <summary>Finalize an authorization (sign) transaction.</summary>
|
||||||
|
[HttpPut("{id}/sign/{signId}")]
|
||||||
|
public async Task<IActionResult> FinalizeSign(string id, string signId, [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] JsonNode? body, CancellationToken ct)
|
||||||
|
=> Ok(await _service.FinalizeSignAsync(id, signId, body, ct));
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using Csob.Models;
|
||||||
|
using Csob.Services;
|
||||||
|
|
||||||
|
namespace Csob.Controllers;
|
||||||
|
|
||||||
|
/// <summary>PISP – standing-order initiation and its authorization (sign) flow. Requires the ČSOB credential headers.</summary>
|
||||||
|
[ApiController]
|
||||||
|
[Route("standing-orders")]
|
||||||
|
[Produces("application/json")]
|
||||||
|
[Tags("PISP – Standing orders")]
|
||||||
|
public sealed class StandingOrdersController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly StandingOrdersService _service;
|
||||||
|
|
||||||
|
public StandingOrdersController(StandingOrdersService service) => _service = service;
|
||||||
|
|
||||||
|
/// <summary>Initiate a standing order.</summary>
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Initiate([FromBody] StandingOrderInitiationRequest request, CancellationToken ct)
|
||||||
|
=> Ok(await _service.InitiateAsync(request, ct));
|
||||||
|
|
||||||
|
/// <summary>Get the standing-order detail.</summary>
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
public async Task<IActionResult> Detail(string id, CancellationToken ct)
|
||||||
|
=> Ok(await _service.DetailAsync(id, ct));
|
||||||
|
|
||||||
|
/// <summary>Get the standing-order instruction status.</summary>
|
||||||
|
[HttpGet("{id}/status")]
|
||||||
|
public async Task<IActionResult> Status(string id, CancellationToken ct)
|
||||||
|
=> Ok(await _service.StatusAsync(id, ct));
|
||||||
|
|
||||||
|
/// <summary>Cancel a standing order.</summary>
|
||||||
|
[HttpDelete("{id}")]
|
||||||
|
public async Task<IActionResult> Cancel(string id, CancellationToken ct)
|
||||||
|
=> Ok(await _service.CancelAsync(id, ct));
|
||||||
|
|
||||||
|
/// <summary>Start the authorization (SCA) of a standing order. Returns the PSU redirect details.</summary>
|
||||||
|
[HttpPost("{id}/sign/{signId}")]
|
||||||
|
public async Task<IActionResult> StartSign(string id, string signId, [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] JsonNode? body, CancellationToken ct)
|
||||||
|
=> Ok(await _service.StartSignAsync(id, signId, body, ct));
|
||||||
|
|
||||||
|
/// <summary>Get the current state of an authorization (sign) transaction.</summary>
|
||||||
|
[HttpGet("{id}/sign/{signId}")]
|
||||||
|
public async Task<IActionResult> SignStatus(string id, string signId, CancellationToken ct)
|
||||||
|
=> Ok(await _service.SignStatusAsync(id, signId, ct));
|
||||||
|
|
||||||
|
/// <summary>Finalize an authorization (sign) transaction.</summary>
|
||||||
|
[HttpPut("{id}/sign/{signId}")]
|
||||||
|
public async Task<IActionResult> FinalizeSign(string id, string signId, [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] JsonNode? body, CancellationToken ct)
|
||||||
|
=> Ok(await _service.FinalizeSignAsync(id, signId, body, ct));
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
namespace Csob.Credentials;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Names of the HTTP headers that carry per-request ČSOB credentials and context.
|
||||||
|
///
|
||||||
|
/// This service is multi-tenant: it stores no credentials itself. Every sensitive value is
|
||||||
|
/// supplied per request in a header (never the query string or body) and is forwarded to ČSOB.
|
||||||
|
/// Headers must therefore only be sent over TLS. Nothing here is logged or persisted.
|
||||||
|
/// </summary>
|
||||||
|
public static class CredentialConstants
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// eIDAS client certificate (QWAC) as a Base64-encoded PKCS#12 / PFX bundle, including the
|
||||||
|
/// private key and the full chain. Used to establish the mutual-TLS connection to ČSOB.
|
||||||
|
/// Analogous to Node's <c>https.Agent({ pfx, passphrase })</c>.
|
||||||
|
/// </summary>
|
||||||
|
public const string CertificateHeader = "X-CSOB-Certificate";
|
||||||
|
|
||||||
|
/// <summary>Optional passphrase protecting the PFX in <see cref="CertificateHeader"/>.</summary>
|
||||||
|
public const string CertificatePasswordHeader = "X-CSOB-Certificate-Password";
|
||||||
|
|
||||||
|
/// <summary>OAuth2 Bearer access token obtained for the PSU; forwarded as <c>Authorization: Bearer</c>.</summary>
|
||||||
|
public const string AccessTokenHeader = "X-Access-Token";
|
||||||
|
|
||||||
|
/// <summary>ČSOB application API key; forwarded as the <c>APIKEY</c> header.</summary>
|
||||||
|
public const string ApiKeyHeader = "X-API-Key";
|
||||||
|
|
||||||
|
/// <summary>TPP (third-party provider) organisation name; forwarded as the <c>TPP-Name</c> header.</summary>
|
||||||
|
public const string TppNameHeader = "X-TPP-Name";
|
||||||
|
|
||||||
|
/// <summary>OAuth2 client id of the registered TPP application (used by the OAuth helper endpoints).</summary>
|
||||||
|
public const string ClientIdHeader = "X-CSOB-Client-Id";
|
||||||
|
|
||||||
|
/// <summary>OAuth2 client secret of the registered TPP application (used by the OAuth helper endpoints).</summary>
|
||||||
|
public const string ClientSecretHeader = "X-CSOB-Client-Secret";
|
||||||
|
|
||||||
|
// Optional PSU (end-user) context, forwarded verbatim to ČSOB when present.
|
||||||
|
|
||||||
|
/// <summary>Whether the PSU is online/involved in the request; forwarded as <c>User-Involved</c> (default false).</summary>
|
||||||
|
public const string UserInvolvedHeader = "X-User-Involved";
|
||||||
|
|
||||||
|
/// <summary>PSU IP address; forwarded as <c>User-IP-Address</c>.</summary>
|
||||||
|
public const string UserIpAddressHeader = "X-User-IP-Address";
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
|
namespace Csob.Credentials;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fully resolved set of per-request credentials and PSU context used to call the ČSOB PSD2 API.
|
||||||
|
/// Built from request headers by <see cref="RequestCredentialsProvider"/>; never logged.
|
||||||
|
/// </summary>
|
||||||
|
public sealed record CsobCredentials
|
||||||
|
{
|
||||||
|
/// <summary>OAuth2 Bearer access token (forwarded as <c>Authorization: Bearer</c>).</summary>
|
||||||
|
public required string AccessToken { get; init; }
|
||||||
|
|
||||||
|
/// <summary>ČSOB application API key (forwarded as <c>APIKEY</c>).</summary>
|
||||||
|
public required string ApiKey { get; init; }
|
||||||
|
|
||||||
|
/// <summary>TPP organisation name (forwarded as <c>TPP-Name</c>).</summary>
|
||||||
|
public required string TppName { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// eIDAS client certificate (with private key) for mutual TLS. Optional at the type level so
|
||||||
|
/// metadata endpoints can resolve context, but required for any real upstream call.
|
||||||
|
/// </summary>
|
||||||
|
public X509Certificate2? Certificate { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Whether the PSU is online for this request (<c>User-Involved</c>); defaults to false.</summary>
|
||||||
|
public bool UserInvolved { get; init; }
|
||||||
|
|
||||||
|
/// <summary>Optional PSU IP address (<c>User-IP-Address</c>).</summary>
|
||||||
|
public string? UserIpAddress { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
namespace Csob.Credentials;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when a request does not provide the credential headers required to call ČSOB.
|
||||||
|
/// Translated to HTTP 401 by the exception-handling middleware.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class MissingCredentialsException : Exception
|
||||||
|
{
|
||||||
|
public IReadOnlyList<string> MissingHeaders { get; }
|
||||||
|
|
||||||
|
public MissingCredentialsException(IReadOnlyList<string> missingHeaders)
|
||||||
|
: base("Incomplete ČSOB credentials. Provide the missing values as request headers (TLS only).")
|
||||||
|
{
|
||||||
|
MissingHeaders = missingHeaders;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
|
namespace Csob.Credentials;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves the ČSOB credentials and PSU context for the current request, exclusively from HTTP
|
||||||
|
/// headers (this service stores no secrets). Missing required headers produce a 401; a malformed
|
||||||
|
/// certificate or wrong passphrase produces a 400 (via <see cref="CryptographicException"/>).
|
||||||
|
/// </summary>
|
||||||
|
public sealed class RequestCredentialsProvider
|
||||||
|
{
|
||||||
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
|
||||||
|
public RequestCredentialsProvider(IHttpContextAccessor httpContextAccessor)
|
||||||
|
{
|
||||||
|
_httpContextAccessor = httpContextAccessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Reads a single request header, returning null when absent or blank.</summary>
|
||||||
|
public string? Header(string name)
|
||||||
|
{
|
||||||
|
var headers = _httpContextAccessor.HttpContext?.Request.Headers;
|
||||||
|
if (headers is not null && headers.TryGetValue(name, out var value))
|
||||||
|
{
|
||||||
|
var raw = value.ToString();
|
||||||
|
if (!string.IsNullOrWhiteSpace(raw))
|
||||||
|
{
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the eIDAS client certificate from the Base64 PFX header (+ optional passphrase).
|
||||||
|
/// Returns null when no certificate header is present.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="CryptographicException">The header is not valid Base64 or the PFX/passphrase is invalid.</exception>
|
||||||
|
public X509Certificate2? TryBuildCertificate()
|
||||||
|
{
|
||||||
|
var base64 = Header(CredentialConstants.CertificateHeader);
|
||||||
|
if (base64 is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] raw;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
raw = Convert.FromBase64String(base64.Trim());
|
||||||
|
}
|
||||||
|
catch (FormatException ex)
|
||||||
|
{
|
||||||
|
throw new CryptographicException($"{CredentialConstants.CertificateHeader} is not valid Base64.", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
var password = Header(CredentialConstants.CertificatePasswordHeader);
|
||||||
|
|
||||||
|
// EphemeralKeySet keeps the private key in memory only — never written to the machine key store / disk.
|
||||||
|
return new X509Certificate2(raw, password, X509KeyStorageFlags.EphemeralKeySet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves the full credential set required for an AISP/PISP/consent call. Throws
|
||||||
|
/// <see cref="MissingCredentialsException"/> if any required header is absent.
|
||||||
|
/// </summary>
|
||||||
|
public CsobCredentials Resolve()
|
||||||
|
{
|
||||||
|
var accessToken = Header(CredentialConstants.AccessTokenHeader);
|
||||||
|
var apiKey = Header(CredentialConstants.ApiKeyHeader);
|
||||||
|
var tppName = Header(CredentialConstants.TppNameHeader);
|
||||||
|
var certificate = TryBuildCertificate();
|
||||||
|
|
||||||
|
var missing = new List<string>();
|
||||||
|
if (string.IsNullOrWhiteSpace(accessToken)) missing.Add(CredentialConstants.AccessTokenHeader);
|
||||||
|
if (string.IsNullOrWhiteSpace(apiKey)) missing.Add(CredentialConstants.ApiKeyHeader);
|
||||||
|
if (string.IsNullOrWhiteSpace(tppName)) missing.Add(CredentialConstants.TppNameHeader);
|
||||||
|
if (certificate is null) missing.Add(CredentialConstants.CertificateHeader);
|
||||||
|
if (missing.Count > 0)
|
||||||
|
{
|
||||||
|
certificate?.Dispose();
|
||||||
|
throw new MissingCredentialsException(missing);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CsobCredentials
|
||||||
|
{
|
||||||
|
AccessToken = accessToken!,
|
||||||
|
ApiKey = apiKey!,
|
||||||
|
TppName = tppName!,
|
||||||
|
Certificate = certificate,
|
||||||
|
UserInvolved = string.Equals(Header(CredentialConstants.UserInvolvedHeader), "true", StringComparison.OrdinalIgnoreCase),
|
||||||
|
UserIpAddress = Header(CredentialConstants.UserIpAddressHeader),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,5 +3,13 @@
|
|||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<RootNamespace>Csob</RootNamespace>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<!-- 1591: missing XML doc comment. We document public endpoints/models but not every member. -->
|
||||||
|
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.9.0" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
using Microsoft.OpenApi.Any;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
|
using Csob.Controllers;
|
||||||
|
using Csob.Credentials;
|
||||||
|
|
||||||
|
namespace Csob.Infrastructure;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Documents the per-request credential headers in Swagger. Metadata endpoints need none; the
|
||||||
|
/// OAuth helper needs the certificate + client id/secret; every other (AISP/PISP/consent) endpoint
|
||||||
|
/// needs the certificate + access token + API key + TPP name. Headers are marked optional at the
|
||||||
|
/// schema level (the service validates them at runtime) but the descriptions state what is required.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class CredentialHeadersOperationFilter : IOperationFilter
|
||||||
|
{
|
||||||
|
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||||
|
{
|
||||||
|
var declaringType = context.MethodInfo.DeclaringType;
|
||||||
|
|
||||||
|
// Metadata endpoints (health/version/status) do not talk to ČSOB.
|
||||||
|
if (declaringType == typeof(MetaController))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
operation.Parameters ??= new List<OpenApiParameter>();
|
||||||
|
|
||||||
|
AddHeader(operation, CredentialConstants.CertificateHeader,
|
||||||
|
"eIDAS client certificate (QWAC) as a Base64-encoded PFX/PKCS#12 bundle incl. private key and chain. Used for mutual TLS to ČSOB. SENSITIVE — TLS only.");
|
||||||
|
AddHeader(operation, CredentialConstants.CertificatePasswordHeader,
|
||||||
|
"Passphrase protecting the PFX in X-CSOB-Certificate (omit if the PFX has no password). SENSITIVE.");
|
||||||
|
|
||||||
|
if (declaringType == typeof(OAuthController))
|
||||||
|
{
|
||||||
|
AddHeader(operation, CredentialConstants.ClientIdHeader,
|
||||||
|
"OAuth2 client id of the registered TPP application. Required.");
|
||||||
|
AddHeader(operation, CredentialConstants.ClientSecretHeader,
|
||||||
|
"OAuth2 client secret of the registered TPP application. Required for token/refresh. SENSITIVE.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddHeader(operation, CredentialConstants.AccessTokenHeader,
|
||||||
|
"OAuth2 Bearer access token obtained for the PSU. Forwarded as 'Authorization: Bearer'. Required. SENSITIVE.");
|
||||||
|
AddHeader(operation, CredentialConstants.ApiKeyHeader,
|
||||||
|
"ČSOB application API key. Forwarded as 'APIKEY'. Required.");
|
||||||
|
AddHeader(operation, CredentialConstants.TppNameHeader,
|
||||||
|
"TPP organisation name. Forwarded as 'TPP-Name'. Required.");
|
||||||
|
AddHeader(operation, CredentialConstants.UserInvolvedHeader,
|
||||||
|
"Optional. 'true' if the PSU is online for this request (forwarded as 'User-Involved'). Defaults to false.", example: "false");
|
||||||
|
AddHeader(operation, CredentialConstants.UserIpAddressHeader,
|
||||||
|
"Optional. PSU IP address (forwarded as 'User-IP-Address').");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddHeader(OpenApiOperation operation, string name, string description, string? example = null)
|
||||||
|
{
|
||||||
|
operation.Parameters.Add(new OpenApiParameter
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
In = ParameterLocation.Header,
|
||||||
|
Required = false,
|
||||||
|
Description = description,
|
||||||
|
Schema = new OpenApiSchema
|
||||||
|
{
|
||||||
|
Type = "string",
|
||||||
|
Example = example is null ? null : new OpenApiString(example),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
using System.Net;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Csob.Client;
|
||||||
|
using Csob.Credentials;
|
||||||
|
|
||||||
|
namespace Csob.Infrastructure;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Translates domain exceptions into JSON <see cref="ProblemDetails"/> responses. Credentials are
|
||||||
|
/// never logged — only upstream status codes and error codes (the upstream's own payload). Errors
|
||||||
|
/// are always logged (no silent failures).
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ExceptionHandlingMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
|
||||||
|
|
||||||
|
public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InvokeAsync(HttpContext context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _next(context);
|
||||||
|
}
|
||||||
|
catch (MissingCredentialsException ex)
|
||||||
|
{
|
||||||
|
await WriteProblem(context, HttpStatusCode.Unauthorized, ex.Message, new Dictionary<string, object?>
|
||||||
|
{
|
||||||
|
["missingHeaders"] = ex.MissingHeaders,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (CryptographicException ex)
|
||||||
|
{
|
||||||
|
// Invalid Base64 PFX or wrong passphrase in the certificate header. Do not log the value.
|
||||||
|
_logger.LogWarning("Client certificate could not be loaded: {Message}", ex.Message);
|
||||||
|
await WriteProblem(context, HttpStatusCode.BadRequest,
|
||||||
|
"The provided client certificate could not be loaded. Check X-CSOB-Certificate (Base64 PFX) and X-CSOB-Certificate-Password.", null);
|
||||||
|
}
|
||||||
|
catch (CsobApiException ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ČSOB PSD2 API call failed: {Status} {Codes}", (int)ex.StatusCode, string.Join(",", ex.ErrorCodes));
|
||||||
|
await WriteProblem(context, ex.StatusCode, ex.Message, new Dictionary<string, object?>
|
||||||
|
{
|
||||||
|
["csobErrorCodes"] = ex.ErrorCodes,
|
||||||
|
["upstreamBody"] = ParseBody(ex.RawBody),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException) when (!context.RequestAborted.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ČSOB PSD2 API call timed out.");
|
||||||
|
await WriteProblem(context, HttpStatusCode.GatewayTimeout, "The ČSOB PSD2 API did not respond in time.", null);
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
// Network failure, or the mutual-TLS handshake was rejected (e.g. wrong/expired eIDAS certificate).
|
||||||
|
_logger.LogWarning(ex, "ČSOB PSD2 API unreachable or TLS handshake failed.");
|
||||||
|
await WriteProblem(context, HttpStatusCode.BadGateway,
|
||||||
|
"The ČSOB PSD2 API is unreachable or the mutual-TLS handshake failed.", null);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Unhandled error while processing the request.");
|
||||||
|
await WriteProblem(context, HttpStatusCode.InternalServerError, "An unexpected error occurred.", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JsonNode? ParseBody(string? rawBody)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(rawBody))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return JsonNode.Parse(rawBody);
|
||||||
|
}
|
||||||
|
catch (System.Text.Json.JsonException)
|
||||||
|
{
|
||||||
|
return JsonValue.Create(rawBody);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task WriteProblem(HttpContext context, HttpStatusCode status, string detail, IDictionary<string, object?>? extensions)
|
||||||
|
{
|
||||||
|
if (context.Response.HasStarted)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var problem = new ProblemDetails
|
||||||
|
{
|
||||||
|
Status = (int)status,
|
||||||
|
Title = ReasonPhrase(status),
|
||||||
|
Detail = detail,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (extensions is not null)
|
||||||
|
{
|
||||||
|
foreach (var (key, value) in extensions)
|
||||||
|
{
|
||||||
|
problem.Extensions[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Response.Clear();
|
||||||
|
context.Response.StatusCode = (int)status;
|
||||||
|
context.Response.ContentType = "application/problem+json";
|
||||||
|
await context.Response.WriteAsJsonAsync(problem);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ReasonPhrase(HttpStatusCode status) => status switch
|
||||||
|
{
|
||||||
|
HttpStatusCode.Unauthorized => "Unauthorized",
|
||||||
|
HttpStatusCode.BadRequest => "Bad Request",
|
||||||
|
HttpStatusCode.BadGateway => "Upstream ČSOB PSD2 API error",
|
||||||
|
HttpStatusCode.GatewayTimeout => "Upstream ČSOB PSD2 API timeout",
|
||||||
|
_ => status.ToString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
using System.Text.Json.Nodes;
|
||||||
|
|
||||||
|
namespace Csob.Models;
|
||||||
|
|
||||||
|
// Reusable building blocks shared by the typed PISP request bodies. Field names follow the COBS
|
||||||
|
// JSON contract (camelCase via System.Text.Json web defaults). Deeply variable sub-trees (party
|
||||||
|
// identification, structured remittance) are typed as JsonNode so any valid COBS payload passes
|
||||||
|
// through unchanged.
|
||||||
|
|
||||||
|
/// <summary>Monetary amount with ISO 4217 currency.</summary>
|
||||||
|
public sealed class Amount
|
||||||
|
{
|
||||||
|
public decimal Value { get; set; }
|
||||||
|
public string Currency { get; set; } = "CZK";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Wrapper matching the <c>amount</c> object whose <c>instructedAmount</c> holds the value/currency.</summary>
|
||||||
|
public sealed class AmountContainer
|
||||||
|
{
|
||||||
|
public Amount InstructedAmount { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Account number identification. For domestic/SEPA writes only <c>iban</c> is required.</summary>
|
||||||
|
public sealed class AccountIdentification
|
||||||
|
{
|
||||||
|
public string? Iban { get; set; }
|
||||||
|
public string? Other { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Account reference with optional currency (<c>debtorAccount</c>/<c>creditorAccount</c>).</summary>
|
||||||
|
public sealed class AccountReference
|
||||||
|
{
|
||||||
|
public AccountIdentification Identification { get; set; } = new();
|
||||||
|
public string? Currency { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ServiceLevel
|
||||||
|
{
|
||||||
|
/// <summary>Payment scheme: DMCT (domestic), ESCT (SEPA), XBCT (cross-border), EXCT, NXCT.</summary>
|
||||||
|
public string? Code { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PaymentTypeInformation
|
||||||
|
{
|
||||||
|
/// <summary>NORM (default), HIGH (express) or INST (instant).</summary>
|
||||||
|
public string? InstructionPriority { get; set; }
|
||||||
|
public ServiceLevel? ServiceLevel { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class FinancialInstitutionIdentification
|
||||||
|
{
|
||||||
|
public string? Bic { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class Agent
|
||||||
|
{
|
||||||
|
public FinancialInstitutionIdentification? FinancialInstitutionIdentification { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class PostalAddress
|
||||||
|
{
|
||||||
|
public string? StreetName { get; set; }
|
||||||
|
public string? BuildingNumber { get; set; }
|
||||||
|
public string? PostCode { get; set; }
|
||||||
|
public string? TownName { get; set; }
|
||||||
|
public string? Country { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>A party (debtor/creditor/ultimate*). Identification is variable, so kept as raw JSON.</summary>
|
||||||
|
public sealed class Party
|
||||||
|
{
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public PostalAddress? PostalAddress { get; set; }
|
||||||
|
public JsonNode? Identification { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remittance information. <c>unstructured</c> is free text (Czech symbols may be encoded as
|
||||||
|
/// <c>/VS/.../SS/.../KS/...</c>); <c>structured</c> varies (its <c>reference</c> may be a string or
|
||||||
|
/// an array) so it is passed through as raw JSON.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class RemittanceInformation
|
||||||
|
{
|
||||||
|
public string? Unstructured { get; set; }
|
||||||
|
public JsonNode? Structured { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class Purpose
|
||||||
|
{
|
||||||
|
public string? Proprietary { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
namespace Csob.Models;
|
||||||
|
|
||||||
|
/// <summary>Body for <c>POST /oauth/token</c> (Authorization Code grant). Secrets travel in headers.</summary>
|
||||||
|
public sealed class TokenExchangeRequest
|
||||||
|
{
|
||||||
|
/// <summary>Authorization code returned to the redirect URI after PSU consent.</summary>
|
||||||
|
public string Code { get; set; } = string.Empty;
|
||||||
|
/// <summary>Redirect URI registered for the TPP app; must match the one used to obtain the code.</summary>
|
||||||
|
public string RedirectUri { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Body for <c>POST /oauth/refresh</c> (Refresh Token grant).</summary>
|
||||||
|
public sealed class TokenRefreshRequest
|
||||||
|
{
|
||||||
|
public string RefreshToken { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Response of <c>GET /oauth/authorization-url</c>.</summary>
|
||||||
|
public sealed class AuthorizationUrlResponse
|
||||||
|
{
|
||||||
|
/// <summary>Fully-built ČSOB authorization URL to which the PSU must be redirected.</summary>
|
||||||
|
public string AuthorizationUrl { get; set; } = string.Empty;
|
||||||
|
/// <summary>The opaque <c>state</c> value echoed back on the redirect (CSRF protection).</summary>
|
||||||
|
public string? State { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Csob.Models;
|
||||||
|
|
||||||
|
public sealed class PaymentIdentification
|
||||||
|
{
|
||||||
|
/// <summary>Unique instruction id assigned by the TPP (idempotency key). Max 35 chars.</summary>
|
||||||
|
public string? InstructionIdentification { get; set; }
|
||||||
|
public string? EndToEndIdentification { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Payment initiation request (<c>POST /my/payments</c>). Covers domestic (DMCT) and SEPA (ESCT)
|
||||||
|
/// payments — SEPA-only fields (creditor address, agent, ultimate parties, purpose) are optional.
|
||||||
|
/// Any additional COBS field not modelled here is preserved via <see cref="AdditionalData"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class PaymentInitiationRequest
|
||||||
|
{
|
||||||
|
public PaymentIdentification? PaymentIdentification { get; set; }
|
||||||
|
public PaymentTypeInformation? PaymentTypeInformation { get; set; }
|
||||||
|
public AmountContainer Amount { get; set; } = new();
|
||||||
|
public string? RequestedExecutionDate { get; set; }
|
||||||
|
|
||||||
|
public Party? UltimateDebtor { get; set; }
|
||||||
|
public AccountReference DebtorAccount { get; set; } = new();
|
||||||
|
public Agent? CreditorAgent { get; set; }
|
||||||
|
public Party? Creditor { get; set; }
|
||||||
|
public AccountReference CreditorAccount { get; set; } = new();
|
||||||
|
public Party? UltimateCreditor { get; set; }
|
||||||
|
|
||||||
|
public RemittanceInformation? RemittanceInformation { get; set; }
|
||||||
|
public Purpose? Purpose { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Any COBS fields not explicitly modelled are forwarded to ČSOB unchanged.</summary>
|
||||||
|
[JsonExtensionData]
|
||||||
|
public Dictionary<string, JsonElement>? AdditionalData { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Csob.Models;
|
||||||
|
|
||||||
|
public sealed class StandingOrderIdentification
|
||||||
|
{
|
||||||
|
public string? InstructionIdentification { get; set; }
|
||||||
|
public string? TransactionIdentification { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class StandingOrderExecution
|
||||||
|
{
|
||||||
|
/// <summary>DAILY, WEEKLY, MONTHLY, BI_MONTHLY, QUARTERLY, HALFYEARLY, YEARLY, SINGLE, IRREGULAR.</summary>
|
||||||
|
public string? Interval { get; set; }
|
||||||
|
/// <summary>Day within the interval (e.g. day-of-month "25").</summary>
|
||||||
|
public string? IntervalDue { get; set; }
|
||||||
|
/// <summary>e.g. MAX_AMOUNT_EXCEEDED, UNTIL_CANCELLATION.</summary>
|
||||||
|
public string? Mode { get; set; }
|
||||||
|
public string? ModeDue { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class StandingOrderDetail
|
||||||
|
{
|
||||||
|
public string? Alias { get; set; }
|
||||||
|
public StandingOrderExecution? Execution { get; set; }
|
||||||
|
/// <summary>Optional <c>exceptions</c> block (stoppages/breaks); shape varies, kept as raw JSON.</summary>
|
||||||
|
public JsonNode? Exceptions { get; set; }
|
||||||
|
/// <summary>Optional <c>validity</c> block (lastExecutionDate/maxAmount); kept as raw JSON.</summary>
|
||||||
|
public JsonNode? Validity { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Standing-order initiation request (<c>POST /my/standingorders</c>).</summary>
|
||||||
|
public sealed class StandingOrderInitiationRequest
|
||||||
|
{
|
||||||
|
public StandingOrderIdentification? StandingOrderIdentification { get; set; }
|
||||||
|
public PaymentTypeInformation? PaymentTypeInformation { get; set; }
|
||||||
|
public AmountContainer Amount { get; set; } = new();
|
||||||
|
public string? RequestedExecutionDate { get; set; }
|
||||||
|
public StandingOrderDetail? StandingOrder { get; set; }
|
||||||
|
public AccountReference DebtorAccount { get; set; } = new();
|
||||||
|
public AccountReference CreditorAccount { get; set; } = new();
|
||||||
|
public RemittanceInformation? RemittanceInformation { get; set; }
|
||||||
|
|
||||||
|
[JsonExtensionData]
|
||||||
|
public Dictionary<string, JsonElement>? AdditionalData { get; set; }
|
||||||
|
}
|
||||||
+106
-12
@@ -1,22 +1,116 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Csob.Client;
|
||||||
|
using Csob.Configuration;
|
||||||
|
using Csob.Credentials;
|
||||||
|
using Csob.Infrastructure;
|
||||||
|
using Csob.Services;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
// Configuration resolved from environment variables (non-secret infra config only).
|
||||||
|
var settings = new CsobSettings();
|
||||||
|
builder.Services.AddSingleton(settings);
|
||||||
|
|
||||||
|
// The eIDAS client certificate is supplied per request as a Base64 PFX header, which is large.
|
||||||
|
// Raise Kestrel's header size limit so the certificate header is not rejected.
|
||||||
|
builder.WebHost.ConfigureKestrel(options =>
|
||||||
|
{
|
||||||
|
options.Limits.MaxRequestHeadersTotalSize = 1024 * 1024; // 1 MiB
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddHttpContextAccessor();
|
||||||
|
|
||||||
|
// Builds/caches mutual-TLS HttpClients by certificate thumbprint; shared across requests.
|
||||||
|
builder.Services.AddSingleton<CsobHttpClientProvider>();
|
||||||
|
|
||||||
|
// Per-request credential resolution and API access.
|
||||||
|
builder.Services.AddScoped<RequestCredentialsProvider>();
|
||||||
|
builder.Services.AddScoped<CsobApiAccessor>();
|
||||||
|
|
||||||
|
// Agenda services.
|
||||||
|
builder.Services.AddScoped<AccountsService>();
|
||||||
|
builder.Services.AddScoped<PaymentsService>();
|
||||||
|
builder.Services.AddScoped<StandingOrdersService>();
|
||||||
|
builder.Services.AddScoped<DirectDebitsService>();
|
||||||
|
builder.Services.AddScoped<ConsentsService>();
|
||||||
|
builder.Services.AddScoped<OAuthService>();
|
||||||
|
|
||||||
|
builder.Services
|
||||||
|
.AddControllers()
|
||||||
|
.AddJsonOptions(options =>
|
||||||
|
{
|
||||||
|
// camelCase (web defaults) matches the COBS JSON contract; omit null properties on write.
|
||||||
|
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
|
||||||
|
});
|
||||||
|
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddSwaggerGen(options =>
|
||||||
|
{
|
||||||
|
options.SwaggerDoc("v1", new OpenApiInfo
|
||||||
|
{
|
||||||
|
Title = settings.AppName,
|
||||||
|
Version = settings.AppVersion,
|
||||||
|
Description =
|
||||||
|
"Multi-tenant REST integration with the ČSOB PSD2 (Open Banking) API, following the " +
|
||||||
|
"Czech Open Banking Standard (COBS). Covers AISP (account information), PISP (payment, " +
|
||||||
|
"standing-order and direct-debit initiation with the sign/SCA flow), consents and the " +
|
||||||
|
"OAuth2 Authorization Code helper.\n\n" +
|
||||||
|
"**Credentials.** This service stores no secrets — every sensitive value is supplied per " +
|
||||||
|
"request in a header and forwarded to ČSOB over TLS (never the query string or body):\n\n" +
|
||||||
|
"- `X-CSOB-Certificate` — eIDAS client certificate (QWAC) as a Base64 PFX (mutual TLS). Required.\n" +
|
||||||
|
"- `X-CSOB-Certificate-Password` — PFX passphrase (optional).\n" +
|
||||||
|
"- `X-Access-Token` — OAuth2 Bearer access token for the PSU (AISP/PISP/consents). Required.\n" +
|
||||||
|
"- `X-API-Key` — ČSOB application API key (sent upstream as `APIKEY`). Required.\n" +
|
||||||
|
"- `X-TPP-Name` — TPP organisation name (sent upstream as `TPP-Name`). Required.\n" +
|
||||||
|
"- `X-CSOB-Client-Id` / `X-CSOB-Client-Secret` — OAuth2 app credentials (OAuth endpoints only).\n\n" +
|
||||||
|
"Obtain the access token via the `/oauth/*` endpoints (Authorization Code flow with PSU redirect).",
|
||||||
|
});
|
||||||
|
options.OperationFilter<CredentialHeadersOperationFilter>();
|
||||||
|
|
||||||
|
var xmlPath = Path.Combine(AppContext.BaseDirectory, $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml");
|
||||||
|
if (File.Exists(xmlPath))
|
||||||
|
{
|
||||||
|
options.IncludeXmlComments(xmlPath, includeControllerXmlComments: true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
var rootPath = Environment.GetEnvironmentVariable("ROOT_PATH");
|
// ROOT_PATH carries the public reverse-proxy prefix (e.g. /apps/csob). AppFactory's Caddy uses
|
||||||
if (!string.IsNullOrWhiteSpace(rootPath))
|
// handle_path, which strips that prefix before the request reaches the container, so PathBase ends
|
||||||
|
// up empty and the OpenAPI server URL must come from ROOT_PATH directly (see the filter below).
|
||||||
|
var publicPrefix = string.IsNullOrWhiteSpace(settings.RootPath) ? null : "/" + settings.RootPath.Trim('/');
|
||||||
|
|
||||||
|
if (publicPrefix is not null)
|
||||||
{
|
{
|
||||||
app.UsePathBase(rootPath);
|
app.UsePathBase(publicPrefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
app.MapGet("/", () => Results.Json(new
|
app.UseMiddleware<ExceptionHandlingMiddleware>();
|
||||||
{
|
|
||||||
name = "csob",
|
|
||||||
service = "csob",
|
|
||||||
status = "ok"
|
|
||||||
}));
|
|
||||||
|
|
||||||
app.MapGet("/health", () => Results.Json(new
|
// Serve the OpenAPI document under the same /docs prefix as the UI so a relative endpoint resolves
|
||||||
|
// correctly both locally and behind a reverse-proxy prefix (ROOT_PATH).
|
||||||
|
app.UseSwagger(options =>
|
||||||
{
|
{
|
||||||
status = "ok"
|
options.RouteTemplate = "docs/{documentName}/swagger.json";
|
||||||
}));
|
// Advertise the public prefix (e.g. /apps/csob) as the OpenAPI server so Swagger UI "Try it out"
|
||||||
|
// targets {prefix}/accounts, not the host root. The prefix comes from ROOT_PATH because
|
||||||
|
// handle_path has already stripped it from the request, leaving PathBase empty. Fall back to
|
||||||
|
// PathBase (a proxy that keeps the prefix) and finally "/".
|
||||||
|
options.PreSerializeFilters.Add((swaggerDoc, httpReq) =>
|
||||||
|
{
|
||||||
|
var serverUrl = publicPrefix ?? (httpReq.PathBase.HasValue ? httpReq.PathBase.Value : "/");
|
||||||
|
swaggerDoc.Servers = new List<OpenApiServer> { new() { Url = serverUrl } };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
app.UseSwaggerUI(options =>
|
||||||
|
{
|
||||||
|
options.RoutePrefix = "docs";
|
||||||
|
options.SwaggerEndpoint("v1/swagger.json", $"{settings.AppName} v1");
|
||||||
|
options.DocumentTitle = $"{settings.AppName} – API docs";
|
||||||
|
});
|
||||||
|
|
||||||
|
app.MapControllers();
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|||||||
@@ -1,8 +1,90 @@
|
|||||||
# csob
|
# csob
|
||||||
|
|
||||||
.NET API služba vytvořená přes CSBot Services Portal.
|
Multi-tenant REST integrace na **ČSOB PSD2 (Open Banking) API** podle Czech Open Banking Standard (COBS).
|
||||||
|
.NET 8 služba běžící v AppFactory za reverse proxy `/apps/csob`.
|
||||||
|
|
||||||
|
Pokrývá **AISP** (informace o účtech), **PISP** (iniciace plateb, trvalých příkazů a inkas vč. sign/SCA flow),
|
||||||
|
**consents** a **OAuth2** helper (Authorization Code flow).
|
||||||
|
|
||||||
|
## Architektura
|
||||||
|
|
||||||
|
Služba je **bezstavový proxy** – neukládá žádné credentials. Volající klientská služba předává veškeré
|
||||||
|
citlivé údaje (eIDAS certifikát, OAuth client id/secret, access token, APIKEY, TPP name) **per-request
|
||||||
|
v HTTP hlavičkách** (jen přes TLS). mTLS se na základě certifikátu z hlavičky staví per-request a klienti
|
||||||
|
se cachují podle thumbprintu certifikátu. Nic se neloguje ani neukládá na disk.
|
||||||
|
|
||||||
|
Strukturně zrcadlí sesterskou službu `idoklad` (Configuration → env, Credentials → hlavičky, Client factory
|
||||||
|
+ Accessor, Services, Controllers, Infrastructure middleware + Swagger operation filter, `/docs`).
|
||||||
|
|
||||||
## Endpointy
|
## Endpointy
|
||||||
|
|
||||||
- GET /
|
Veřejně dostupné přes `https://services.csbot.cz/apps/csob/...`.
|
||||||
- GET /health
|
|
||||||
|
### Meta (bez credentials)
|
||||||
|
- `GET /health` – liveness
|
||||||
|
- `GET /version` – název/verze + API info
|
||||||
|
- `GET /status` – ne-secret konfigurace (base/oauth URL, timeout)
|
||||||
|
- `GET /docs` – Swagger UI
|
||||||
|
- `GET /docs/v1/swagger.json` – OpenAPI dokument
|
||||||
|
|
||||||
|
### OAuth2
|
||||||
|
- `GET /oauth/authorization-url` – sestaví authorize URL pro přesměrování PSU
|
||||||
|
- `POST /oauth/token` – výměna `code` → access/refresh token (mTLS)
|
||||||
|
- `POST /oauth/refresh` – obnova access tokenu (mTLS)
|
||||||
|
|
||||||
|
### AISP – účty
|
||||||
|
- `GET /accounts`
|
||||||
|
- `GET /accounts/{id}/balance`
|
||||||
|
- `GET /accounts/{id}/transactions`
|
||||||
|
- `GET /accounts/{id}/transactions/awaiting`
|
||||||
|
- `GET /accounts/{id}/standing-orders`
|
||||||
|
- `GET /accounts/{id}/standing-orders/{standingOrderId}`
|
||||||
|
- `GET /accounts/{id}/direct-debits`
|
||||||
|
|
||||||
|
### PISP – platby / trvalé příkazy / inkasa
|
||||||
|
Pro každou agendu: `POST` (iniciace), `GET /{id}` (detail), `GET /{id}/status`, `DELETE /{id}` (zrušení),
|
||||||
|
a sign/SCA flow `POST|GET|PUT /{id}/sign/{signId}`.
|
||||||
|
- `/payments`
|
||||||
|
- `/standing-orders`
|
||||||
|
- `/direct-debits`
|
||||||
|
|
||||||
|
### Consents
|
||||||
|
- `POST /consents`, `GET /consents/{id}`, `DELETE /consents/{id}`
|
||||||
|
|
||||||
|
## Credential hlavičky (per-request, jen přes TLS)
|
||||||
|
|
||||||
|
| Hlavička | Význam | Předáno do ČSOB jako |
|
||||||
|
|---|---|---|
|
||||||
|
| `X-CSOB-Certificate` | eIDAS klientský certifikát (QWAC), **Base64 PFX/PKCS#12** vč. privátního klíče a chainu | mutual TLS |
|
||||||
|
| `X-CSOB-Certificate-Password` | heslo k PFX (volitelné) | – |
|
||||||
|
| `X-Access-Token` | OAuth2 Bearer access token PSU (AISP/PISP/consents) | `Authorization: Bearer` |
|
||||||
|
| `X-API-Key` | ČSOB application API key | `APIKEY` |
|
||||||
|
| `X-TPP-Name` | název TPP organizace | `TPP-Name` |
|
||||||
|
| `X-CSOB-Client-Id` / `X-CSOB-Client-Secret` | OAuth2 app credentials (jen `/oauth/*`) | OAuth form |
|
||||||
|
| `X-User-Involved`, `X-User-IP-Address` | volitelný PSU kontext | `User-Involved`, `User-IP-Address` |
|
||||||
|
|
||||||
|
Hlavičky `X-Request-ID` a `Date` generuje služba automaticky.
|
||||||
|
|
||||||
|
## Konfigurace (environment variables – pouze ne-secret)
|
||||||
|
|
||||||
|
| Proměnná | Default |
|
||||||
|
|---|---|
|
||||||
|
| `APP_NAME` | `ČSOB PSD2 Service` |
|
||||||
|
| `APP_VERSION` | `1.0.0` |
|
||||||
|
| `ROOT_PATH` | (prázdné; AppFactory nastaví `/apps/csob`) |
|
||||||
|
| `CSOB_API_BASE_URL` | `https://api.csob.cz/api/csob/psd2/v1` |
|
||||||
|
| `CSOB_OAUTH_AUTHORIZE_URL` | `https://identita.csob.cz/mep/fs/fl/oauth2/auth` |
|
||||||
|
| `CSOB_OAUTH_TOKEN_URL` | `https://api.csob.cz/api/csob/oauth2/v1/token` |
|
||||||
|
| `CSOB_REQUEST_TIMEOUT_SECONDS` | `100` |
|
||||||
|
|
||||||
|
> **Žádné secrets v env.** Certifikát, client id/secret, access token a APIKEY jdou výhradně per-request hlavičkami.
|
||||||
|
|
||||||
|
## Lokální spuštění
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet run --project Csob.csproj
|
||||||
|
# nebo
|
||||||
|
ROOT_PATH=/apps/csob ASPNETCORE_URLS=http://0.0.0.0:8080 dotnet run
|
||||||
|
```
|
||||||
|
|
||||||
|
Podrobnosti k OAuth/SCA flow a kompletní seznam endpointů viz [documentation/](documentation/).
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using Csob.Client;
|
||||||
|
|
||||||
|
namespace Csob.Services;
|
||||||
|
|
||||||
|
/// <summary>AISP – account information (accounts, balance, transactions, standing orders, direct debits).</summary>
|
||||||
|
public sealed class AccountsService
|
||||||
|
{
|
||||||
|
private readonly CsobApiAccessor _accessor;
|
||||||
|
|
||||||
|
public AccountsService(CsobApiAccessor accessor) => _accessor = accessor;
|
||||||
|
|
||||||
|
public Task<JsonNode?> ListAsync(int? page, int? size, string? sort, string? order, CancellationToken ct)
|
||||||
|
=> _accessor.Client.GetAsync(CsobApiPaths.Accounts, Paging(page, size, sort, order), ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> BalanceAsync(string accountId, string? currency, CancellationToken ct)
|
||||||
|
=> _accessor.Client.GetAsync(CsobApiPaths.Balance(accountId),
|
||||||
|
new Dictionary<string, string?> { ["currency"] = currency }, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> TransactionsAsync(string accountId, TransactionQuery query, CancellationToken ct)
|
||||||
|
=> _accessor.Client.GetAsync(CsobApiPaths.Transactions(accountId), query.ToDictionary(), ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> AwaitingTransactionsAsync(string accountId, int? page, int? size, CancellationToken ct)
|
||||||
|
=> _accessor.Client.GetAsync(CsobApiPaths.AwaitingTransactions(accountId), Paging(page, size, null, null), ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> StandingOrdersAsync(string accountId, int? page, int? size, CancellationToken ct)
|
||||||
|
=> _accessor.Client.GetAsync(CsobApiPaths.AccountStandingOrders(accountId), Paging(page, size, null, null), ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> StandingOrderDetailAsync(string accountId, string standingOrderId, CancellationToken ct)
|
||||||
|
=> _accessor.Client.GetAsync(CsobApiPaths.AccountStandingOrder(accountId, standingOrderId), null, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> DirectDebitsAsync(string accountId, int? page, int? size, CancellationToken ct)
|
||||||
|
=> _accessor.Client.GetAsync(CsobApiPaths.AccountDirectDebits(accountId), Paging(page, size, null, null), ct);
|
||||||
|
|
||||||
|
private static Dictionary<string, string?> Paging(int? page, int? size, string? sort, string? order) => new()
|
||||||
|
{
|
||||||
|
["page"] = page?.ToString(),
|
||||||
|
["size"] = size?.ToString(),
|
||||||
|
["sort"] = sort,
|
||||||
|
["order"] = order,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Optional filters for the transactions listing (forwarded as query parameters).</summary>
|
||||||
|
public sealed class TransactionQuery
|
||||||
|
{
|
||||||
|
public int? Page { get; set; }
|
||||||
|
public int? Size { get; set; }
|
||||||
|
public string? Sort { get; set; }
|
||||||
|
public string? Order { get; set; }
|
||||||
|
/// <summary>ISO date (YYYY-MM-DD) lower bound.</summary>
|
||||||
|
public string? DateFrom { get; set; }
|
||||||
|
/// <summary>ISO date (YYYY-MM-DD) upper bound.</summary>
|
||||||
|
public string? DateTo { get; set; }
|
||||||
|
|
||||||
|
public Dictionary<string, string?> ToDictionary() => new()
|
||||||
|
{
|
||||||
|
["page"] = Page?.ToString(),
|
||||||
|
["size"] = Size?.ToString(),
|
||||||
|
["sort"] = Sort,
|
||||||
|
["order"] = Order,
|
||||||
|
["dateFrom"] = DateFrom,
|
||||||
|
["dateTo"] = DateTo,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using Csob.Client;
|
||||||
|
|
||||||
|
namespace Csob.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Common – PSU consent lifecycle (create / detail / revoke). The consent request body varies
|
||||||
|
/// across COBS profiles, so it is accepted and forwarded as raw JSON.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ConsentsService
|
||||||
|
{
|
||||||
|
private readonly CsobApiAccessor _accessor;
|
||||||
|
|
||||||
|
public ConsentsService(CsobApiAccessor accessor) => _accessor = accessor;
|
||||||
|
|
||||||
|
public Task<JsonNode?> CreateAsync(JsonNode request, CancellationToken ct)
|
||||||
|
=> _accessor.Client.PostAsync(CsobApiPaths.Consents, request, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> DetailAsync(string id, CancellationToken ct)
|
||||||
|
=> _accessor.Client.GetAsync(CsobApiPaths.Consent(id), null, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> DeleteAsync(string id, CancellationToken ct)
|
||||||
|
=> _accessor.Client.DeleteAsync(CsobApiPaths.Consent(id), ct);
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using Csob.Client;
|
||||||
|
|
||||||
|
namespace Csob.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PISP – direct-debit mandate initiation, detail/status, revocation and the sign (SCA) flow.
|
||||||
|
/// The mandate creation body varies across COBS profiles, so it is accepted and forwarded as raw JSON.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class DirectDebitsService
|
||||||
|
{
|
||||||
|
private readonly CsobApiAccessor _accessor;
|
||||||
|
|
||||||
|
public DirectDebitsService(CsobApiAccessor accessor) => _accessor = accessor;
|
||||||
|
|
||||||
|
public Task<JsonNode?> InitiateAsync(JsonNode request, CancellationToken ct)
|
||||||
|
=> _accessor.Client.PostAsync(CsobApiPaths.DirectDebits, request, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> DetailAsync(string id, CancellationToken ct)
|
||||||
|
=> _accessor.Client.GetAsync(CsobApiPaths.DirectDebit(id), null, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> StatusAsync(string id, CancellationToken ct)
|
||||||
|
=> _accessor.Client.GetAsync(CsobApiPaths.DirectDebitStatus(id), null, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> CancelAsync(string id, CancellationToken ct)
|
||||||
|
=> _accessor.Client.DeleteAsync(CsobApiPaths.DirectDebit(id), ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> StartSignAsync(string id, string signId, JsonNode? body, CancellationToken ct)
|
||||||
|
=> _accessor.Client.PostAsync(CsobApiPaths.DirectDebitSign(id, signId), body, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> SignStatusAsync(string id, string signId, CancellationToken ct)
|
||||||
|
=> _accessor.Client.GetAsync(CsobApiPaths.DirectDebitSign(id, signId), null, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> FinalizeSignAsync(string id, string signId, JsonNode? body, CancellationToken ct)
|
||||||
|
=> _accessor.Client.PutAsync(CsobApiPaths.DirectDebitSign(id, signId), body, ct);
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
using System.Net;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using Csob.Client;
|
||||||
|
using Csob.Configuration;
|
||||||
|
using Csob.Credentials;
|
||||||
|
using Csob.Models;
|
||||||
|
|
||||||
|
namespace Csob.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OAuth2 Authorization Code helper for the ČSOB PSD2 flow. Builds the PSU authorization URL and
|
||||||
|
/// exchanges/refreshes tokens against the ČSOB token endpoint. The token endpoint is behind mutual
|
||||||
|
/// TLS, so the client certificate header is required for the token/refresh calls. Client id/secret
|
||||||
|
/// are taken from per-request headers (this service is multi-tenant and stores no app credentials).
|
||||||
|
/// </summary>
|
||||||
|
public sealed class OAuthService
|
||||||
|
{
|
||||||
|
private readonly RequestCredentialsProvider _credentials;
|
||||||
|
private readonly CsobHttpClientProvider _httpClientProvider;
|
||||||
|
private readonly CsobSettings _settings;
|
||||||
|
|
||||||
|
public OAuthService(
|
||||||
|
RequestCredentialsProvider credentials,
|
||||||
|
CsobHttpClientProvider httpClientProvider,
|
||||||
|
CsobSettings settings)
|
||||||
|
{
|
||||||
|
_credentials = credentials;
|
||||||
|
_httpClientProvider = httpClientProvider;
|
||||||
|
_settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Builds the authorization URL to which the PSU must be redirected.</summary>
|
||||||
|
public AuthorizationUrlResponse BuildAuthorizationUrl(string redirectUri, string? scope, string? state)
|
||||||
|
{
|
||||||
|
var clientId = RequireHeader(CredentialConstants.ClientIdHeader);
|
||||||
|
|
||||||
|
var query = new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
["response_type"] = "code",
|
||||||
|
["client_id"] = clientId,
|
||||||
|
["redirect_uri"] = redirectUri,
|
||||||
|
["scope"] = scope,
|
||||||
|
["state"] = state,
|
||||||
|
};
|
||||||
|
|
||||||
|
var url = _settings.OAuthAuthorizeUrl + "?" + string.Join("&", query
|
||||||
|
.Where(kvp => !string.IsNullOrWhiteSpace(kvp.Value))
|
||||||
|
.Select(kvp => $"{Uri.EscapeDataString(kvp.Key)}={Uri.EscapeDataString(kvp.Value!)}"));
|
||||||
|
|
||||||
|
return new AuthorizationUrlResponse { AuthorizationUrl = url, State = state };
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<JsonNode?> ExchangeCodeAsync(TokenExchangeRequest request, CancellationToken ct)
|
||||||
|
=> PostTokenAsync(new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["grant_type"] = "authorization_code",
|
||||||
|
["code"] = request.Code,
|
||||||
|
["redirect_uri"] = request.RedirectUri,
|
||||||
|
}, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> RefreshAsync(TokenRefreshRequest request, CancellationToken ct)
|
||||||
|
=> PostTokenAsync(new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["grant_type"] = "refresh_token",
|
||||||
|
["refresh_token"] = request.RefreshToken,
|
||||||
|
}, ct);
|
||||||
|
|
||||||
|
private async Task<JsonNode?> PostTokenAsync(Dictionary<string, string> form, CancellationToken ct)
|
||||||
|
{
|
||||||
|
var clientId = RequireHeader(CredentialConstants.ClientIdHeader);
|
||||||
|
var clientSecret = RequireHeader(CredentialConstants.ClientSecretHeader);
|
||||||
|
|
||||||
|
var certificate = _credentials.TryBuildCertificate()
|
||||||
|
?? throw new MissingCredentialsException(new[] { CredentialConstants.CertificateHeader });
|
||||||
|
|
||||||
|
form["client_id"] = clientId;
|
||||||
|
form["client_secret"] = clientSecret;
|
||||||
|
|
||||||
|
var http = _httpClientProvider.GetClient(certificate);
|
||||||
|
|
||||||
|
using var content = new FormUrlEncodedContent(form);
|
||||||
|
using var response = await http.PostAsync(_settings.OAuthTokenUrl, content, ct);
|
||||||
|
var payload = await response.Content.ReadAsStringAsync(ct);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
throw new CsobApiException(response.StatusCode, ParseOAuthError(payload), payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.IsNullOrWhiteSpace(payload) ? null : JsonNode.Parse(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string RequireHeader(string name)
|
||||||
|
=> _credentials.Header(name) ?? throw new MissingCredentialsException(new[] { name });
|
||||||
|
|
||||||
|
private static IReadOnlyList<string> ParseOAuthError(string payload)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(payload))
|
||||||
|
{
|
||||||
|
return Array.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// OAuth2 errors use { "error": "...", "error_description": "..." }.
|
||||||
|
var error = JsonNode.Parse(payload)?["error"]?.GetValue<string>();
|
||||||
|
return string.IsNullOrWhiteSpace(error) ? Array.Empty<string>() : new[] { error! };
|
||||||
|
}
|
||||||
|
catch (System.Text.Json.JsonException)
|
||||||
|
{
|
||||||
|
return Array.Empty<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using Csob.Client;
|
||||||
|
using Csob.Models;
|
||||||
|
|
||||||
|
namespace Csob.Services;
|
||||||
|
|
||||||
|
/// <summary>PISP – single payment initiation, status/detail, cancellation and the sign (SCA) flow.</summary>
|
||||||
|
public sealed class PaymentsService
|
||||||
|
{
|
||||||
|
private readonly CsobApiAccessor _accessor;
|
||||||
|
|
||||||
|
public PaymentsService(CsobApiAccessor accessor) => _accessor = accessor;
|
||||||
|
|
||||||
|
public Task<JsonNode?> InitiateAsync(PaymentInitiationRequest request, CancellationToken ct)
|
||||||
|
=> _accessor.Client.PostAsync(CsobApiPaths.Payments, request, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> DetailAsync(string id, CancellationToken ct)
|
||||||
|
=> _accessor.Client.GetAsync(CsobApiPaths.Payment(id), null, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> StatusAsync(string id, CancellationToken ct)
|
||||||
|
=> _accessor.Client.GetAsync(CsobApiPaths.PaymentStatus(id), null, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> CancelAsync(string id, CancellationToken ct)
|
||||||
|
=> _accessor.Client.DeleteAsync(CsobApiPaths.Payment(id), ct);
|
||||||
|
|
||||||
|
/// <summary>Starts transaction authorization (SCA). Returns the redirect details for the PSU.</summary>
|
||||||
|
public Task<JsonNode?> StartSignAsync(string id, string signId, JsonNode? body, CancellationToken ct)
|
||||||
|
=> _accessor.Client.PostAsync(CsobApiPaths.PaymentSign(id, signId), body, ct);
|
||||||
|
|
||||||
|
/// <summary>Gets the current state of an authorization (sign) transaction.</summary>
|
||||||
|
public Task<JsonNode?> SignStatusAsync(string id, string signId, CancellationToken ct)
|
||||||
|
=> _accessor.Client.GetAsync(CsobApiPaths.PaymentSign(id, signId), null, ct);
|
||||||
|
|
||||||
|
/// <summary>Finalizes an authorization (sign) transaction.</summary>
|
||||||
|
public Task<JsonNode?> FinalizeSignAsync(string id, string signId, JsonNode? body, CancellationToken ct)
|
||||||
|
=> _accessor.Client.PutAsync(CsobApiPaths.PaymentSign(id, signId), body, ct);
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using Csob.Client;
|
||||||
|
using Csob.Models;
|
||||||
|
|
||||||
|
namespace Csob.Services;
|
||||||
|
|
||||||
|
/// <summary>PISP – standing-order initiation, detail/status, cancellation and the sign (SCA) flow.</summary>
|
||||||
|
public sealed class StandingOrdersService
|
||||||
|
{
|
||||||
|
private readonly CsobApiAccessor _accessor;
|
||||||
|
|
||||||
|
public StandingOrdersService(CsobApiAccessor accessor) => _accessor = accessor;
|
||||||
|
|
||||||
|
public Task<JsonNode?> InitiateAsync(StandingOrderInitiationRequest request, CancellationToken ct)
|
||||||
|
=> _accessor.Client.PostAsync(CsobApiPaths.StandingOrders, request, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> DetailAsync(string id, CancellationToken ct)
|
||||||
|
=> _accessor.Client.GetAsync(CsobApiPaths.StandingOrder(id), null, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> StatusAsync(string id, CancellationToken ct)
|
||||||
|
=> _accessor.Client.GetAsync(CsobApiPaths.StandingOrderStatus(id), null, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> CancelAsync(string id, CancellationToken ct)
|
||||||
|
=> _accessor.Client.DeleteAsync(CsobApiPaths.StandingOrder(id), ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> StartSignAsync(string id, string signId, JsonNode? body, CancellationToken ct)
|
||||||
|
=> _accessor.Client.PostAsync(CsobApiPaths.StandingOrderSign(id, signId), body, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> SignStatusAsync(string id, string signId, CancellationToken ct)
|
||||||
|
=> _accessor.Client.GetAsync(CsobApiPaths.StandingOrderSign(id, signId), null, ct);
|
||||||
|
|
||||||
|
public Task<JsonNode?> FinalizeSignAsync(string id, string signId, JsonNode? body, CancellationToken ct)
|
||||||
|
=> _accessor.Client.PutAsync(CsobApiPaths.StandingOrderSign(id, signId), body, ct);
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
{
|
||||||
|
"runtimeTarget": {
|
||||||
|
"name": ".NETCoreApp,Version=v8.0",
|
||||||
|
"signature": ""
|
||||||
|
},
|
||||||
|
"compilationOptions": {},
|
||||||
|
"targets": {
|
||||||
|
".NETCoreApp,Version=v8.0": {
|
||||||
|
"Csob/1.0.0": {
|
||||||
|
"dependencies": {
|
||||||
|
"Swashbuckle.AspNetCore": "6.9.0"
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"Csob.dll": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.OpenApi/1.6.14": {
|
||||||
|
"runtime": {
|
||||||
|
"lib/netstandard2.0/Microsoft.OpenApi.dll": {
|
||||||
|
"assemblyVersion": "1.6.14.0",
|
||||||
|
"fileVersion": "1.6.14.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Swashbuckle.AspNetCore/6.9.0": {
|
||||||
|
"dependencies": {
|
||||||
|
"Swashbuckle.AspNetCore.Swagger": "6.9.0",
|
||||||
|
"Swashbuckle.AspNetCore.SwaggerGen": "6.9.0",
|
||||||
|
"Swashbuckle.AspNetCore.SwaggerUI": "6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Swashbuckle.AspNetCore.Swagger/6.9.0": {
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.OpenApi": "1.6.14"
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"lib/net8.0/Swashbuckle.AspNetCore.Swagger.dll": {
|
||||||
|
"assemblyVersion": "6.9.0.0",
|
||||||
|
"fileVersion": "6.9.0.799"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Swashbuckle.AspNetCore.SwaggerGen/6.9.0": {
|
||||||
|
"dependencies": {
|
||||||
|
"Swashbuckle.AspNetCore.Swagger": "6.9.0"
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"lib/net8.0/Swashbuckle.AspNetCore.SwaggerGen.dll": {
|
||||||
|
"assemblyVersion": "6.9.0.0",
|
||||||
|
"fileVersion": "6.9.0.799"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Swashbuckle.AspNetCore.SwaggerUI/6.9.0": {
|
||||||
|
"runtime": {
|
||||||
|
"lib/net8.0/Swashbuckle.AspNetCore.SwaggerUI.dll": {
|
||||||
|
"assemblyVersion": "6.9.0.0",
|
||||||
|
"fileVersion": "6.9.0.799"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libraries": {
|
||||||
|
"Csob/1.0.0": {
|
||||||
|
"type": "project",
|
||||||
|
"serviceable": false,
|
||||||
|
"sha512": ""
|
||||||
|
},
|
||||||
|
"Microsoft.OpenApi/1.6.14": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-tTaBT8qjk3xINfESyOPE2rIellPvB7qpVqiWiyA/lACVvz+xOGiXhFUfohcx82NLbi5avzLW0lx+s6oAqQijfw==",
|
||||||
|
"path": "microsoft.openapi/1.6.14",
|
||||||
|
"hashPath": "microsoft.openapi.1.6.14.nupkg.sha512"
|
||||||
|
},
|
||||||
|
"Swashbuckle.AspNetCore/6.9.0": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-lvI+XHF21tkwXd2nDCLGJsdhdUYsY3Ax2fWUlvw81Oa6EedtnIAf5tThy8ZnPcz/9/TwsLgjgtX9ifOCIjbEPA==",
|
||||||
|
"path": "swashbuckle.aspnetcore/6.9.0",
|
||||||
|
"hashPath": "swashbuckle.aspnetcore.6.9.0.nupkg.sha512"
|
||||||
|
},
|
||||||
|
"Swashbuckle.AspNetCore.Swagger/6.9.0": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-P316kpxx5DnDvJwNWW8iTAXkh9DVenAxFGe9v4OUS0gil+vitH7F1feXhCtVeHN/616EFNTMh4pV2lcr9kkw/w==",
|
||||||
|
"path": "swashbuckle.aspnetcore.swagger/6.9.0",
|
||||||
|
"hashPath": "swashbuckle.aspnetcore.swagger.6.9.0.nupkg.sha512"
|
||||||
|
},
|
||||||
|
"Swashbuckle.AspNetCore.SwaggerGen/6.9.0": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-FjeMR3fBzwVc5plfYjoHw9ptf8SOWMupvO9X35J5EgzT3L9dRqSxa+cBKzL8PwCyemY0xNrggQSB5+MFWx1axg==",
|
||||||
|
"path": "swashbuckle.aspnetcore.swaggergen/6.9.0",
|
||||||
|
"hashPath": "swashbuckle.aspnetcore.swaggergen.6.9.0.nupkg.sha512"
|
||||||
|
},
|
||||||
|
"Swashbuckle.AspNetCore.SwaggerUI/6.9.0": {
|
||||||
|
"type": "package",
|
||||||
|
"serviceable": true,
|
||||||
|
"sha512": "sha512-0OxlWBFLl2gUESZX/K7QCTz9KctKy0VxHTvLIBcyWGD4z/fv5MCMW02qzYGcReLJr4yBnNDRzApKtLh6oBpe9A==",
|
||||||
|
"path": "swashbuckle.aspnetcore.swaggerui/6.9.0",
|
||||||
|
"hashPath": "swashbuckle.aspnetcore.swaggerui.6.9.0.nupkg.sha512"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"runtimeOptions": {
|
||||||
|
"tfm": "net8.0",
|
||||||
|
"frameworks": [
|
||||||
|
{
|
||||||
|
"name": "Microsoft.NETCore.App",
|
||||||
|
"version": "8.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Microsoft.AspNetCore.App",
|
||||||
|
"version": "8.0.0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configProperties": {
|
||||||
|
"System.GC.Server": true,
|
||||||
|
"System.Reflection.Metadata.MetadataUpdater.IsSupported": false,
|
||||||
|
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"Version":1,"ManifestType":"Build","Endpoints":[]}
|
||||||
@@ -0,0 +1,502 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<doc>
|
||||||
|
<assembly>
|
||||||
|
<name>Csob</name>
|
||||||
|
</assembly>
|
||||||
|
<members>
|
||||||
|
<member name="T:Csob.Client.CsobApiAccessor">
|
||||||
|
<summary>
|
||||||
|
Scoped accessor that resolves the credentials for the current request and lazily builds a
|
||||||
|
single <see cref="T:Csob.Client.CsobApiClient"/> (bound to the request's mutual-TLS client) shared by all
|
||||||
|
services handling that request.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Client.CsobApiClient">
|
||||||
|
<summary>
|
||||||
|
Thin HTTP wrapper around the ČSOB PSD2 resource API for a single request. It attaches the
|
||||||
|
mandatory COBS headers (<c>Authorization</c>, <c>APIKEY</c>, <c>TPP-Name</c>, <c>X-Request-ID</c>,
|
||||||
|
<c>Date</c>, <c>User-Involved</c>), sends the call over the per-request mutual-TLS client and
|
||||||
|
returns the response JSON verbatim (<see cref="T:System.Text.Json.Nodes.JsonNode"/>) so no field is lost in translation.
|
||||||
|
Non-success responses become a <see cref="T:Csob.Client.CsobApiException"/>.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Client.CsobApiClient.JsonOptions">
|
||||||
|
<summary>Web defaults (camelCase) match the COBS JSON contract; null properties are omitted on write.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Client.CsobApiClient.ParseErrorCodes(System.String)">
|
||||||
|
<summary>Best-effort parse of the COBS error shape <c>{ "errors": [ { "error": "CODE" } ] }</c>.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Client.CsobApiException">
|
||||||
|
<summary>
|
||||||
|
Raised when the ČSOB PSD2 API returns a non-success HTTP status. Carries the upstream status
|
||||||
|
and the raw error body so the middleware can surface it without leaking credentials.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Client.CsobApiException.ErrorCodes">
|
||||||
|
<summary>Machine-readable error codes parsed from the ČSOB <c>errors[].error</c> array (best effort).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Client.CsobApiException.RawBody">
|
||||||
|
<summary>Raw response body (already credential-free — it is the upstream's own error payload).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Client.CsobApiPaths">
|
||||||
|
<summary>
|
||||||
|
Central registry of ČSOB PSD2 resource path templates (relative to <c>CSOB_API_BASE_URL</c>).
|
||||||
|
|
||||||
|
Paths follow the Czech Open Banking Standard (COBS) as implemented by ČSOB. The account-scoped
|
||||||
|
AISP paths and the PISP <c>/my/payments/.../sign/{signId}</c> authorization flow are confirmed
|
||||||
|
against the ČSOB developer portal; the remaining COBS resources use the same <c>/my/</c> prefix.
|
||||||
|
Keep every path here so a portal-specific correction is a single-file change.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Client.CsobHttpClientProvider">
|
||||||
|
<summary>
|
||||||
|
Provides <see cref="T:System.Net.Http.HttpClient"/> instances configured for mutual TLS with a per-request eIDAS
|
||||||
|
client certificate. ČSOB requires the client certificate at the transport layer, so a single
|
||||||
|
shared client cannot be used across tenants.
|
||||||
|
|
||||||
|
Clients are cached by certificate thumbprint and reused for connection pooling. In practice the
|
||||||
|
certificate identifies the TPP application (not the PSU), so the number of distinct certificates
|
||||||
|
is small and bounded by the set of calling tenants. Certificates and clients live in memory only
|
||||||
|
and never touch disk.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Client.CsobHttpClientProvider.GetClient(System.Security.Cryptography.X509Certificates.X509Certificate2)">
|
||||||
|
<summary>
|
||||||
|
Returns a (cached) mutual-TLS <see cref="T:System.Net.Http.HttpClient"/> presenting <paramref name="certificate"/>.
|
||||||
|
A fresh certificate instance is built per request; if an equivalent one (same thumbprint) is
|
||||||
|
already cached, the redundant instance is disposed so it does not leak.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Configuration.CsobSettings">
|
||||||
|
<summary>
|
||||||
|
Service configuration resolved from environment variables.
|
||||||
|
|
||||||
|
This service is a stateless, multi-tenant proxy in front of the ČSOB PSD2 (Open Banking)
|
||||||
|
API. Unlike the sibling iDoklad service, it holds <b>no per-client secrets</b>: the eIDAS
|
||||||
|
client certificate, OAuth client id/secret, access token, API key and TPP name are all
|
||||||
|
supplied per request as HTTP headers by the calling client service
|
||||||
|
(see <see cref="T:Csob.Credentials.CredentialConstants"/>). Only non-secret infrastructure
|
||||||
|
configuration (API/OAuth base URLs, app metadata, reverse-proxy prefix, timeout) lives here.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Configuration.CsobSettings.RootPath">
|
||||||
|
<summary>Public reverse-proxy prefix (e.g. <c>/apps/csob</c>) injected by AppFactory.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Configuration.CsobSettings.ApiBaseUrl">
|
||||||
|
<summary>
|
||||||
|
Base URL of the ČSOB PSD2 resource API. Production default; override for any other
|
||||||
|
environment. All AISP/PISP/consent path templates are appended to this base.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Configuration.CsobSettings.OAuthAuthorizeUrl">
|
||||||
|
<summary>
|
||||||
|
OAuth2 authorization endpoint (Authorization Code flow, PSU redirect). Production default;
|
||||||
|
verify against the current ČSOB developer portal as the host may change.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Configuration.CsobSettings.OAuthTokenUrl">
|
||||||
|
<summary>
|
||||||
|
OAuth2 token endpoint (code->token and refresh). Production default; verify against the
|
||||||
|
current ČSOB developer portal.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Configuration.CsobSettings.RequestTimeoutSeconds">
|
||||||
|
<summary>Upstream HTTP request timeout in seconds.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Controllers.AccountsController">
|
||||||
|
<summary>
|
||||||
|
AISP – account information. All endpoints require the ČSOB credential headers
|
||||||
|
(eIDAS certificate, access token, API key, TPP name). See the Swagger description for details.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.AccountsController.List(System.Nullable{System.Int32},System.Nullable{System.Int32},System.String,System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>List the PSU's payment accounts (paged).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.AccountsController.Balance(System.String,System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the balance(s) of an account.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.AccountsController.Transactions(System.String,System.Nullable{System.Int32},System.Nullable{System.Int32},System.String,System.String,System.String,System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>List booked transactions of an account (paged, optional date range).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.AccountsController.Awaiting(System.String,System.Nullable{System.Int32},System.Nullable{System.Int32},System.Threading.CancellationToken)">
|
||||||
|
<summary>List awaiting (pending) transactions of an account.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.AccountsController.StandingOrders(System.String,System.Nullable{System.Int32},System.Nullable{System.Int32},System.Threading.CancellationToken)">
|
||||||
|
<summary>List the account's existing standing orders.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.AccountsController.StandingOrderDetail(System.String,System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get a standing-order detail for the account.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.AccountsController.DirectDebits(System.String,System.Nullable{System.Int32},System.Nullable{System.Int32},System.Threading.CancellationToken)">
|
||||||
|
<summary>List the account's direct-debit mandates.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Controllers.ConsentsController">
|
||||||
|
<summary>
|
||||||
|
Common – PSU consent lifecycle. Requires the ČSOB credential headers. The consent body is
|
||||||
|
forwarded to ČSOB as raw JSON (COBS consent shape).
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.ConsentsController.Create(System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Create a consent.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.ConsentsController.Detail(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get a consent detail.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.ConsentsController.Delete(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Revoke a consent.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Controllers.DirectDebitsController">
|
||||||
|
<summary>
|
||||||
|
PISP – direct-debit mandate initiation and its authorization (sign) flow. Requires the ČSOB
|
||||||
|
credential headers. The mandate body is forwarded to ČSOB as raw JSON (COBS direct-debit shape).
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.DirectDebitsController.Initiate(System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Initiate a direct-debit mandate.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.DirectDebitsController.Detail(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the direct-debit mandate detail.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.DirectDebitsController.Status(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the direct-debit instruction status.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.DirectDebitsController.Cancel(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Revoke a direct-debit mandate.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.DirectDebitsController.StartSign(System.String,System.String,System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Start the authorization (SCA) of a direct-debit mandate. Returns the PSU redirect details.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.DirectDebitsController.SignStatus(System.String,System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the current state of an authorization (sign) transaction.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.DirectDebitsController.FinalizeSign(System.String,System.String,System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Finalize an authorization (sign) transaction.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Controllers.MetaController">
|
||||||
|
<summary>Service metadata endpoints. These do not require ČSOB credentials.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.MetaController.Health">
|
||||||
|
<summary>Liveness probe.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.MetaController.Version">
|
||||||
|
<summary>Service name, version and the configured upstream endpoints (no secrets).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.MetaController.Status">
|
||||||
|
<summary>
|
||||||
|
Reports the non-secret configuration. This service is stateless and multi-tenant: it holds
|
||||||
|
no credentials, so there is nothing per-client to report — every secret is supplied per
|
||||||
|
request via headers.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Controllers.OAuthController">
|
||||||
|
<summary>
|
||||||
|
OAuth2 Authorization Code helper for the ČSOB PSD2 PSU consent flow. Build the authorization URL,
|
||||||
|
redirect the PSU to it, then exchange the returned code for tokens. The token/refresh calls run
|
||||||
|
over mutual TLS, so they require the <c>X-CSOB-Certificate</c> header; client id/secret are also
|
||||||
|
supplied per request (this service is multi-tenant).
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.OAuthController.AuthorizationUrl(System.String,System.String,System.String)">
|
||||||
|
<summary>Build the ČSOB authorization URL to which the PSU must be redirected.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.OAuthController.Token(Csob.Models.TokenExchangeRequest,System.Threading.CancellationToken)">
|
||||||
|
<summary>Exchange an authorization code for an access/refresh token (mutual TLS).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.OAuthController.Refresh(Csob.Models.TokenRefreshRequest,System.Threading.CancellationToken)">
|
||||||
|
<summary>Refresh an access token using a refresh token (mutual TLS).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Controllers.PaymentsController">
|
||||||
|
<summary>
|
||||||
|
PISP – single payment initiation and its authorization (sign) flow. Requires the ČSOB credential
|
||||||
|
headers. After <c>POST /payments</c> the response carries <c>signInfo.signId</c>; use it with the
|
||||||
|
sign endpoints to drive Strong Customer Authentication (SCA).
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.PaymentsController.Initiate(Csob.Models.PaymentInitiationRequest,System.Threading.CancellationToken)">
|
||||||
|
<summary>Initiate a domestic (DMCT) or SEPA (ESCT) payment.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.PaymentsController.Detail(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the full payment detail.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.PaymentsController.Status(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the payment instruction status.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.PaymentsController.Cancel(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Cancel a not-yet-authorized payment.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.PaymentsController.StartSign(System.String,System.String,System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Start the authorization (SCA) of a payment. Returns the PSU redirect details.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.PaymentsController.SignStatus(System.String,System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the current state of an authorization (sign) transaction.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.PaymentsController.FinalizeSign(System.String,System.String,System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Finalize an authorization (sign) transaction.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Controllers.StandingOrdersController">
|
||||||
|
<summary>PISP – standing-order initiation and its authorization (sign) flow. Requires the ČSOB credential headers.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.StandingOrdersController.Initiate(Csob.Models.StandingOrderInitiationRequest,System.Threading.CancellationToken)">
|
||||||
|
<summary>Initiate a standing order.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.StandingOrdersController.Detail(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the standing-order detail.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.StandingOrdersController.Status(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the standing-order instruction status.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.StandingOrdersController.Cancel(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Cancel a standing order.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.StandingOrdersController.StartSign(System.String,System.String,System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Start the authorization (SCA) of a standing order. Returns the PSU redirect details.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.StandingOrdersController.SignStatus(System.String,System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the current state of an authorization (sign) transaction.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.StandingOrdersController.FinalizeSign(System.String,System.String,System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Finalize an authorization (sign) transaction.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Credentials.CredentialConstants">
|
||||||
|
<summary>
|
||||||
|
Names of the HTTP headers that carry per-request ČSOB credentials and context.
|
||||||
|
|
||||||
|
This service is multi-tenant: it stores no credentials itself. Every sensitive value is
|
||||||
|
supplied per request in a header (never the query string or body) and is forwarded to ČSOB.
|
||||||
|
Headers must therefore only be sent over TLS. Nothing here is logged or persisted.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Credentials.CredentialConstants.CertificateHeader">
|
||||||
|
<summary>
|
||||||
|
eIDAS client certificate (QWAC) as a Base64-encoded PKCS#12 / PFX bundle, including the
|
||||||
|
private key and the full chain. Used to establish the mutual-TLS connection to ČSOB.
|
||||||
|
Analogous to Node's <c>https.Agent({ pfx, passphrase })</c>.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Credentials.CredentialConstants.CertificatePasswordHeader">
|
||||||
|
<summary>Optional passphrase protecting the PFX in <see cref="F:Csob.Credentials.CredentialConstants.CertificateHeader"/>.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Credentials.CredentialConstants.AccessTokenHeader">
|
||||||
|
<summary>OAuth2 Bearer access token obtained for the PSU; forwarded as <c>Authorization: Bearer</c>.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Credentials.CredentialConstants.ApiKeyHeader">
|
||||||
|
<summary>ČSOB application API key; forwarded as the <c>APIKEY</c> header.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Credentials.CredentialConstants.TppNameHeader">
|
||||||
|
<summary>TPP (third-party provider) organisation name; forwarded as the <c>TPP-Name</c> header.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Credentials.CredentialConstants.ClientIdHeader">
|
||||||
|
<summary>OAuth2 client id of the registered TPP application (used by the OAuth helper endpoints).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Credentials.CredentialConstants.ClientSecretHeader">
|
||||||
|
<summary>OAuth2 client secret of the registered TPP application (used by the OAuth helper endpoints).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Credentials.CredentialConstants.UserInvolvedHeader">
|
||||||
|
<summary>Whether the PSU is online/involved in the request; forwarded as <c>User-Involved</c> (default false).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Credentials.CredentialConstants.UserIpAddressHeader">
|
||||||
|
<summary>PSU IP address; forwarded as <c>User-IP-Address</c>.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Credentials.CsobCredentials">
|
||||||
|
<summary>
|
||||||
|
Fully resolved set of per-request credentials and PSU context used to call the ČSOB PSD2 API.
|
||||||
|
Built from request headers by <see cref="T:Csob.Credentials.RequestCredentialsProvider"/>; never logged.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Credentials.CsobCredentials.AccessToken">
|
||||||
|
<summary>OAuth2 Bearer access token (forwarded as <c>Authorization: Bearer</c>).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Credentials.CsobCredentials.ApiKey">
|
||||||
|
<summary>ČSOB application API key (forwarded as <c>APIKEY</c>).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Credentials.CsobCredentials.TppName">
|
||||||
|
<summary>TPP organisation name (forwarded as <c>TPP-Name</c>).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Credentials.CsobCredentials.Certificate">
|
||||||
|
<summary>
|
||||||
|
eIDAS client certificate (with private key) for mutual TLS. Optional at the type level so
|
||||||
|
metadata endpoints can resolve context, but required for any real upstream call.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Credentials.CsobCredentials.UserInvolved">
|
||||||
|
<summary>Whether the PSU is online for this request (<c>User-Involved</c>); defaults to false.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Credentials.CsobCredentials.UserIpAddress">
|
||||||
|
<summary>Optional PSU IP address (<c>User-IP-Address</c>).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Credentials.MissingCredentialsException">
|
||||||
|
<summary>
|
||||||
|
Raised when a request does not provide the credential headers required to call ČSOB.
|
||||||
|
Translated to HTTP 401 by the exception-handling middleware.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Credentials.RequestCredentialsProvider">
|
||||||
|
<summary>
|
||||||
|
Resolves the ČSOB credentials and PSU context for the current request, exclusively from HTTP
|
||||||
|
headers (this service stores no secrets). Missing required headers produce a 401; a malformed
|
||||||
|
certificate or wrong passphrase produces a 400 (via <see cref="T:System.Security.Cryptography.CryptographicException"/>).
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Credentials.RequestCredentialsProvider.Header(System.String)">
|
||||||
|
<summary>Reads a single request header, returning null when absent or blank.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Credentials.RequestCredentialsProvider.TryBuildCertificate">
|
||||||
|
<summary>
|
||||||
|
Builds the eIDAS client certificate from the Base64 PFX header (+ optional passphrase).
|
||||||
|
Returns null when no certificate header is present.
|
||||||
|
</summary>
|
||||||
|
<exception cref="T:System.Security.Cryptography.CryptographicException">The header is not valid Base64 or the PFX/passphrase is invalid.</exception>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Credentials.RequestCredentialsProvider.Resolve">
|
||||||
|
<summary>
|
||||||
|
Resolves the full credential set required for an AISP/PISP/consent call. Throws
|
||||||
|
<see cref="T:Csob.Credentials.MissingCredentialsException"/> if any required header is absent.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Infrastructure.CredentialHeadersOperationFilter">
|
||||||
|
<summary>
|
||||||
|
Documents the per-request credential headers in Swagger. Metadata endpoints need none; the
|
||||||
|
OAuth helper needs the certificate + client id/secret; every other (AISP/PISP/consent) endpoint
|
||||||
|
needs the certificate + access token + API key + TPP name. Headers are marked optional at the
|
||||||
|
schema level (the service validates them at runtime) but the descriptions state what is required.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Infrastructure.ExceptionHandlingMiddleware">
|
||||||
|
<summary>
|
||||||
|
Translates domain exceptions into JSON <see cref="T:Microsoft.AspNetCore.Mvc.ProblemDetails"/> responses. Credentials are
|
||||||
|
never logged — only upstream status codes and error codes (the upstream's own payload). Errors
|
||||||
|
are always logged (no silent failures).
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.Amount">
|
||||||
|
<summary>Monetary amount with ISO 4217 currency.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.AmountContainer">
|
||||||
|
<summary>Wrapper matching the <c>amount</c> object whose <c>instructedAmount</c> holds the value/currency.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.AccountIdentification">
|
||||||
|
<summary>Account number identification. For domestic/SEPA writes only <c>iban</c> is required.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.AccountReference">
|
||||||
|
<summary>Account reference with optional currency (<c>debtorAccount</c>/<c>creditorAccount</c>).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.ServiceLevel.Code">
|
||||||
|
<summary>Payment scheme: DMCT (domestic), ESCT (SEPA), XBCT (cross-border), EXCT, NXCT.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.PaymentTypeInformation.InstructionPriority">
|
||||||
|
<summary>NORM (default), HIGH (express) or INST (instant).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.Party">
|
||||||
|
<summary>A party (debtor/creditor/ultimate*). Identification is variable, so kept as raw JSON.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.RemittanceInformation">
|
||||||
|
<summary>
|
||||||
|
Remittance information. <c>unstructured</c> is free text (Czech symbols may be encoded as
|
||||||
|
<c>/VS/.../SS/.../KS/...</c>); <c>structured</c> varies (its <c>reference</c> may be a string or
|
||||||
|
an array) so it is passed through as raw JSON.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.TokenExchangeRequest">
|
||||||
|
<summary>Body for <c>POST /oauth/token</c> (Authorization Code grant). Secrets travel in headers.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.TokenExchangeRequest.Code">
|
||||||
|
<summary>Authorization code returned to the redirect URI after PSU consent.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.TokenExchangeRequest.RedirectUri">
|
||||||
|
<summary>Redirect URI registered for the TPP app; must match the one used to obtain the code.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.TokenRefreshRequest">
|
||||||
|
<summary>Body for <c>POST /oauth/refresh</c> (Refresh Token grant).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.AuthorizationUrlResponse">
|
||||||
|
<summary>Response of <c>GET /oauth/authorization-url</c>.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.AuthorizationUrlResponse.AuthorizationUrl">
|
||||||
|
<summary>Fully-built ČSOB authorization URL to which the PSU must be redirected.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.AuthorizationUrlResponse.State">
|
||||||
|
<summary>The opaque <c>state</c> value echoed back on the redirect (CSRF protection).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.PaymentIdentification.InstructionIdentification">
|
||||||
|
<summary>Unique instruction id assigned by the TPP (idempotency key). Max 35 chars.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.PaymentInitiationRequest">
|
||||||
|
<summary>
|
||||||
|
Payment initiation request (<c>POST /my/payments</c>). Covers domestic (DMCT) and SEPA (ESCT)
|
||||||
|
payments — SEPA-only fields (creditor address, agent, ultimate parties, purpose) are optional.
|
||||||
|
Any additional COBS field not modelled here is preserved via <see cref="P:Csob.Models.PaymentInitiationRequest.AdditionalData"/>.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.PaymentInitiationRequest.AdditionalData">
|
||||||
|
<summary>Any COBS fields not explicitly modelled are forwarded to ČSOB unchanged.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.StandingOrderExecution.Interval">
|
||||||
|
<summary>DAILY, WEEKLY, MONTHLY, BI_MONTHLY, QUARTERLY, HALFYEARLY, YEARLY, SINGLE, IRREGULAR.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.StandingOrderExecution.IntervalDue">
|
||||||
|
<summary>Day within the interval (e.g. day-of-month "25").</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.StandingOrderExecution.Mode">
|
||||||
|
<summary>e.g. MAX_AMOUNT_EXCEEDED, UNTIL_CANCELLATION.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.StandingOrderDetail.Exceptions">
|
||||||
|
<summary>Optional <c>exceptions</c> block (stoppages/breaks); shape varies, kept as raw JSON.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.StandingOrderDetail.Validity">
|
||||||
|
<summary>Optional <c>validity</c> block (lastExecutionDate/maxAmount); kept as raw JSON.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.StandingOrderInitiationRequest">
|
||||||
|
<summary>Standing-order initiation request (<c>POST /my/standingorders</c>).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Services.AccountsService">
|
||||||
|
<summary>AISP – account information (accounts, balance, transactions, standing orders, direct debits).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Services.TransactionQuery">
|
||||||
|
<summary>Optional filters for the transactions listing (forwarded as query parameters).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Services.TransactionQuery.DateFrom">
|
||||||
|
<summary>ISO date (YYYY-MM-DD) lower bound.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Services.TransactionQuery.DateTo">
|
||||||
|
<summary>ISO date (YYYY-MM-DD) upper bound.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Services.ConsentsService">
|
||||||
|
<summary>
|
||||||
|
Common – PSU consent lifecycle (create / detail / revoke). The consent request body varies
|
||||||
|
across COBS profiles, so it is accepted and forwarded as raw JSON.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Services.DirectDebitsService">
|
||||||
|
<summary>
|
||||||
|
PISP – direct-debit mandate initiation, detail/status, revocation and the sign (SCA) flow.
|
||||||
|
The mandate creation body varies across COBS profiles, so it is accepted and forwarded as raw JSON.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Services.OAuthService">
|
||||||
|
<summary>
|
||||||
|
OAuth2 Authorization Code helper for the ČSOB PSD2 flow. Builds the PSU authorization URL and
|
||||||
|
exchanges/refreshes tokens against the ČSOB token endpoint. The token endpoint is behind mutual
|
||||||
|
TLS, so the client certificate header is required for the token/refresh calls. Client id/secret
|
||||||
|
are taken from per-request headers (this service is multi-tenant and stores no app credentials).
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Services.OAuthService.BuildAuthorizationUrl(System.String,System.String,System.String)">
|
||||||
|
<summary>Builds the authorization URL to which the PSU must be redirected.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Services.PaymentsService">
|
||||||
|
<summary>PISP – single payment initiation, status/detail, cancellation and the sign (SCA) flow.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Services.PaymentsService.StartSignAsync(System.String,System.String,System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Starts transaction authorization (SCA). Returns the redirect details for the PSU.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Services.PaymentsService.SignStatusAsync(System.String,System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Gets the current state of an authorization (sign) transaction.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Services.PaymentsService.FinalizeSignAsync(System.String,System.String,System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Finalizes an authorization (sign) transaction.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Services.StandingOrdersService">
|
||||||
|
<summary>PISP – standing-order initiation, detail/status, cancellation and the sign (SCA) flow.</summary>
|
||||||
|
</member>
|
||||||
|
</members>
|
||||||
|
</doc>
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
# Autentizace a OAuth2 / SCA flow
|
||||||
|
|
||||||
|
ČSOB PSD2 vyžaduje tři vrstvy:
|
||||||
|
|
||||||
|
1. **Mutual TLS** s eIDAS QWAC certifikátem (na transportní vrstvě).
|
||||||
|
2. **APIKEY** identifikující registrovanou TPP aplikaci.
|
||||||
|
3. **OAuth2 Bearer access token** prokazující souhlas (consent) PSU – získaný Authorization Code flow.
|
||||||
|
|
||||||
|
Tato služba je multi-tenant a **neukládá nic** – všechny tři vrstvy dostává per-request v hlavičkách.
|
||||||
|
|
||||||
|
## Credential hlavičky
|
||||||
|
|
||||||
|
| Hlavička | Povinná pro | Předáno do ČSOB jako |
|
||||||
|
|---|---|---|
|
||||||
|
| `X-CSOB-Certificate` (Base64 PFX) | vše (mTLS) | TLS klientský certifikát |
|
||||||
|
| `X-CSOB-Certificate-Password` | volitelné | – |
|
||||||
|
| `X-Access-Token` | AISP/PISP/consents | `Authorization: Bearer` |
|
||||||
|
| `X-API-Key` | AISP/PISP/consents | `APIKEY` |
|
||||||
|
| `X-TPP-Name` | AISP/PISP/consents | `TPP-Name` |
|
||||||
|
| `X-CSOB-Client-Id` | `/oauth/*` | OAuth `client_id` |
|
||||||
|
| `X-CSOB-Client-Secret` | `/oauth/token`, `/oauth/refresh` | OAuth `client_secret` |
|
||||||
|
|
||||||
|
`X-Request-ID` (UUID) a `Date` (RFC 7231) doplňuje služba sama.
|
||||||
|
|
||||||
|
### Příprava Base64 PFX
|
||||||
|
|
||||||
|
```bash
|
||||||
|
base64 -w0 client_qwac.pfx # Linux/macOS -> hodnota X-CSOB-Certificate
|
||||||
|
certutil -encode client_qwac.pfx out.txt # Windows (odstranit hlavičky/řádky)
|
||||||
|
```
|
||||||
|
|
||||||
|
Heslo k PFX jde do `X-CSOB-Certificate-Password`. Hodnoty posílej **výhradně přes HTTPS**.
|
||||||
|
|
||||||
|
## OAuth2 Authorization Code flow
|
||||||
|
|
||||||
|
```
|
||||||
|
1. GET /oauth/authorization-url?redirect_uri=...&scope=...&state=...
|
||||||
|
hlavičky: X-CSOB-Client-Id
|
||||||
|
-> { "authorizationUrl": "...", "state": "..." }
|
||||||
|
|
||||||
|
2. Klient přesměruje PSU na authorizationUrl. PSU se přihlásí a udělí souhlas v ČSOB.
|
||||||
|
ČSOB přesměruje zpět na redirect_uri s parametrem ?code=...&state=...
|
||||||
|
|
||||||
|
3. POST /oauth/token { "code": "...", "redirectUri": "..." }
|
||||||
|
hlavičky: X-CSOB-Certificate (+password), X-CSOB-Client-Id, X-CSOB-Client-Secret
|
||||||
|
-> { "access_token": "...", "refresh_token": "...", "expires_in": ... }
|
||||||
|
|
||||||
|
4. AISP/PISP volání s hlavičkami:
|
||||||
|
X-CSOB-Certificate, X-Access-Token, X-API-Key, X-TPP-Name
|
||||||
|
|
||||||
|
5. Obnova: POST /oauth/refresh { "refreshToken": "..." } (stejné hlavičky jako krok 3)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scopes (COBS)
|
||||||
|
|
||||||
|
`aisp.accounts`, `aisp.balances`, `aisp.transactions`, `aisp.standingorders`, `aisp.directdebits`,
|
||||||
|
`aisp.notifications`, `pisp.payments`, `pisp.standingorders`, `pisp.directdebits`, `pisp.accounts`.
|
||||||
|
|
||||||
|
## PISP autorizace platby (SCA / sign flow)
|
||||||
|
|
||||||
|
```
|
||||||
|
1. POST /payments (body s platbou)
|
||||||
|
-> odpověď obsahuje signInfo.signId a instructionStatus
|
||||||
|
|
||||||
|
2. POST /payments/{id}/sign/{signId}
|
||||||
|
-> autorizační detaily pro PSU (např. authorizationType, href.url pro redirect SCA)
|
||||||
|
|
||||||
|
3. (volitelně) GET /payments/{id}/sign/{signId} – stav autorizace
|
||||||
|
(volitelně) PUT /payments/{id}/sign/{signId} – finalizace
|
||||||
|
|
||||||
|
4. GET /payments/{id}/status – konečný stav (ACTC, ACSC, RJCT, …)
|
||||||
|
```
|
||||||
|
|
||||||
|
Stejný sign flow platí pro `/standing-orders` a `/direct-debits`.
|
||||||
|
|
||||||
|
## Chybové odpovědi
|
||||||
|
|
||||||
|
Služba vrací `application/problem+json`:
|
||||||
|
|
||||||
|
- `401` – chybí povinná credential hlavička (`missingHeaders`).
|
||||||
|
- `400` – neplatný Base64 PFX nebo špatné heslo certifikátu.
|
||||||
|
- upstream status (4xx/5xx) – chyba z ČSOB; pole `csobErrorCodes` a `upstreamBody` nesou originální payload.
|
||||||
|
- `502` – ČSOB nedostupné nebo selhal mTLS handshake (např. neplatný/expirovaný certifikát).
|
||||||
|
- `504` – timeout.
|
||||||
|
|
||||||
|
Secrets se nikdy nelogují ani nevracejí.
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# ČSOB PSD2 služba – přehled
|
||||||
|
|
||||||
|
Tato služba je tenký, **bezstavový multi-tenant proxy** mezi klientskými aplikacemi a produkčním
|
||||||
|
**ČSOB PSD2 (Open Banking) API** (Czech Open Banking Standard – COBS). Vystavuje vlastní REST endpointy
|
||||||
|
a každý request 1:1 přeloží na odpovídající ČSOB volání, přičemž doplní povinné COBS hlavičky a naváže
|
||||||
|
mutual TLS pomocí certifikátu předaného v hlavičce.
|
||||||
|
|
||||||
|
## Klíčové principy
|
||||||
|
|
||||||
|
- **Žádné uložené secrets.** Certifikát, OAuth client id/secret, access token, APIKEY i TPP name přicházejí
|
||||||
|
per-request v hlavičkách (viz [authentication.md](authentication.md)). V env jsou jen ne-secret URL a metadata.
|
||||||
|
- **Věrný průchod dat.** Čtecí (GET) odpovědi a méně stabilní těla (consent, direct-debit, sign) se předávají
|
||||||
|
jako surové JSON (`JsonNode`), takže se neztrácí žádné pole. Typovaná těla mají jen platba a trvalý příkaz
|
||||||
|
(pro kvalitní Swagger), s `additionalData` pro forward-kompatibilitu.
|
||||||
|
- **mTLS per-request.** Certifikát z `X-CSOB-Certificate` (Base64 PFX) se načte do paměti (nikdy na disk)
|
||||||
|
a použije pro TLS handshake. `HttpClient` se cachuje podle thumbprintu certifikátu kvůli znovupoužití spojení.
|
||||||
|
- **Reverse proxy.** OpenAPI `servers` se nastavuje z `ROOT_PATH`, takže Swagger „Try it out“ volá přes
|
||||||
|
`/apps/csob/...` (Caddy `handle_path` prefix předtím odstraní).
|
||||||
|
|
||||||
|
## Mapování na ČSOB
|
||||||
|
|
||||||
|
| Tato služba | ČSOB PSD2 (relativně k `CSOB_API_BASE_URL`) |
|
||||||
|
|---|---|
|
||||||
|
| `GET /accounts` | `GET /my/accounts` |
|
||||||
|
| `GET /accounts/{id}/balance` | `GET /my/accounts/{id}/balance` |
|
||||||
|
| `GET /accounts/{id}/transactions` | `GET /my/accounts/{id}/transactions` |
|
||||||
|
| `GET /accounts/{id}/transactions/awaiting` | `GET /my/accounts/{id}/transactions/awaiting` |
|
||||||
|
| `GET /accounts/{id}/standing-orders` | `GET /my/accounts/{id}/standingorders` |
|
||||||
|
| `GET /accounts/{id}/direct-debits` | `GET /my/accounts/{id}/directdebits` |
|
||||||
|
| `POST /payments` | `POST /my/payments` |
|
||||||
|
| `GET /payments/{id}` / `/status` | `GET /my/payments/{id}` / `/status` |
|
||||||
|
| `POST|GET|PUT /payments/{id}/sign/{signId}` | `…/my/payments/{id}/sign/{signId}` |
|
||||||
|
| `…/standing-orders…` | `…/my/standingorders…` |
|
||||||
|
| `…/direct-debits…` | `…/my/directdebits…` |
|
||||||
|
| `POST|GET|DELETE /consents…` | `…/consents…` |
|
||||||
|
|
||||||
|
Cesty jsou centralizované v `Client/CsobApiPaths.cs`.
|
||||||
|
|
||||||
|
## Co je vědomě mimo rozsah (zatím nenapojeno)
|
||||||
|
|
||||||
|
V souladu s philosophy sesterské služby `idoklad` (dokumentovat, co není napojeno):
|
||||||
|
|
||||||
|
- **Batch payments** (`/batchpayments`) a **co-signing list** (`/authorizations`) z COBS PISP.
|
||||||
|
- **PIISP** balance check (`/balanceCheck`).
|
||||||
|
- **Šifrování hlavičkových credentials** (jako v microsoft-365-service) – odloženo, spoléháme na TLS.
|
||||||
|
|
||||||
|
## Ověření a rizika
|
||||||
|
|
||||||
|
- Build je čistý (0 warnings). Lokálně ověřeno: `/health`, `/docs`, OpenAPI `servers`=`/apps/csob`,
|
||||||
|
401 bez credentials, 400 při neplatném certifikátu, dokumentace credential hlaviček per operace.
|
||||||
|
- **Reálné ČSOB volání** vyžaduje platnou PSD2 licenci + eIDAS QWAC certifikát, takže ho nelze otestovat bez
|
||||||
|
produkčních údajů klienta.
|
||||||
|
- **OAuth authorize/token URL** a přesný tvar `sign` sub-cest pocházejí z výzkumu COBS + ČSOB portálu;
|
||||||
|
URL jsou env-konfigurovatelné a cesty centralizované – při odchylce ČSOB portálu je oprava jednoho místa.
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
{
|
||||||
|
"format": 1,
|
||||||
|
"restore": {
|
||||||
|
"d:\\GitHubRepository\\Hracicky\\x\\csob\\Csob.csproj": {}
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"d:\\GitHubRepository\\Hracicky\\x\\csob\\Csob.csproj": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"restore": {
|
||||||
|
"projectUniqueName": "d:\\GitHubRepository\\Hracicky\\x\\csob\\Csob.csproj",
|
||||||
|
"projectName": "Csob",
|
||||||
|
"projectPath": "d:\\GitHubRepository\\Hracicky\\x\\csob\\Csob.csproj",
|
||||||
|
"packagesPath": "C:\\Users\\GamingPC\\.nuget\\packages\\",
|
||||||
|
"outputPath": "d:\\GitHubRepository\\Hracicky\\x\\csob\\obj\\",
|
||||||
|
"projectStyle": "PackageReference",
|
||||||
|
"fallbackFolders": [
|
||||||
|
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
|
||||||
|
],
|
||||||
|
"configFilePaths": [
|
||||||
|
"C:\\Users\\GamingPC\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||||
|
],
|
||||||
|
"originalTargetFrameworks": [
|
||||||
|
"net8.0"
|
||||||
|
],
|
||||||
|
"sources": {
|
||||||
|
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||||
|
"C:\\Program Files\\dotnet\\library-packs": {},
|
||||||
|
"D:\\CustomNuGetPackages": {},
|
||||||
|
"https://api.nuget.org/v3/index.json": {}
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net8.0": {
|
||||||
|
"targetAlias": "net8.0",
|
||||||
|
"projectReferences": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warningProperties": {
|
||||||
|
"warnAsError": [
|
||||||
|
"NU1605"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"restoreAuditProperties": {
|
||||||
|
"enableAudit": "true",
|
||||||
|
"auditLevel": "low",
|
||||||
|
"auditMode": "direct"
|
||||||
|
},
|
||||||
|
"SdkAnalysisLevel": "10.0.200"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net8.0": {
|
||||||
|
"targetAlias": "net8.0",
|
||||||
|
"dependencies": {
|
||||||
|
"Swashbuckle.AspNetCore": {
|
||||||
|
"target": "Package",
|
||||||
|
"version": "[6.9.0, )"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"imports": [
|
||||||
|
"net461",
|
||||||
|
"net462",
|
||||||
|
"net47",
|
||||||
|
"net471",
|
||||||
|
"net472",
|
||||||
|
"net48",
|
||||||
|
"net481"
|
||||||
|
],
|
||||||
|
"assetTargetFallback": true,
|
||||||
|
"warn": true,
|
||||||
|
"frameworkReferences": {
|
||||||
|
"Microsoft.AspNetCore.App": {
|
||||||
|
"privateAssets": "none"
|
||||||
|
},
|
||||||
|
"Microsoft.NETCore.App": {
|
||||||
|
"privateAssets": "all"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.204/PortableRuntimeIdentifierGraph.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
|
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||||
|
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
|
||||||
|
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
|
||||||
|
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
|
||||||
|
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||||
|
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\GamingPC\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
||||||
|
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||||
|
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">7.0.0</NuGetToolVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||||
|
<SourceRoot Include="C:\Users\GamingPC\.nuget\packages\" />
|
||||||
|
<SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||||
|
<Import Project="$(NuGetPackageRoot)microsoft.extensions.apidescription.server\6.0.5\build\Microsoft.Extensions.ApiDescription.Server.props" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.apidescription.server\6.0.5\build\Microsoft.Extensions.ApiDescription.Server.props')" />
|
||||||
|
<Import Project="$(NuGetPackageRoot)swashbuckle.aspnetcore\6.9.0\build\Swashbuckle.AspNetCore.props" Condition="Exists('$(NuGetPackageRoot)swashbuckle.aspnetcore\6.9.0\build\Swashbuckle.AspNetCore.props')" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||||
|
<PkgMicrosoft_Extensions_ApiDescription_Server Condition=" '$(PkgMicrosoft_Extensions_ApiDescription_Server)' == '' ">C:\Users\GamingPC\.nuget\packages\microsoft.extensions.apidescription.server\6.0.5</PkgMicrosoft_Extensions_ApiDescription_Server>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
|
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||||
|
<Import Project="$(NuGetPackageRoot)microsoft.extensions.apidescription.server\6.0.5\build\Microsoft.Extensions.ApiDescription.Server.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.apidescription.server\6.0.5\build\Microsoft.Extensions.ApiDescription.Server.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// <autogenerated />
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
[assembly: System.Reflection.AssemblyCompanyAttribute("Csob")]
|
||||||
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||||
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+f44ffae9fc38f3033750d626a06752e62097876f")]
|
||||||
|
[assembly: System.Reflection.AssemblyProductAttribute("Csob")]
|
||||||
|
[assembly: System.Reflection.AssemblyTitleAttribute("Csob")]
|
||||||
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|
||||||
|
// Generated by the MSBuild WriteCodeFragment class.
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
0991943a86580b954544318f8a8536f244a189324540e319d6b1d62a8de99070
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
is_global = true
|
||||||
|
build_property.TargetFramework = net8.0
|
||||||
|
build_property.TargetFrameworkIdentifier = .NETCoreApp
|
||||||
|
build_property.TargetFrameworkVersion = v8.0
|
||||||
|
build_property.TargetPlatformMinVersion =
|
||||||
|
build_property.UsingMicrosoftNETSdkWeb = true
|
||||||
|
build_property.ProjectTypeGuids =
|
||||||
|
build_property.InvariantGlobalization =
|
||||||
|
build_property.PlatformNeutralAssembly =
|
||||||
|
build_property.EnforceExtendedAnalyzerRules =
|
||||||
|
build_property._SupportedPlatformList = Linux,macOS,Windows
|
||||||
|
build_property.RootNamespace = Csob
|
||||||
|
build_property.RootNamespace = Csob
|
||||||
|
build_property.ProjectDir = d:\GitHubRepository\Hracicky\x\csob\
|
||||||
|
build_property.EnableComHosting =
|
||||||
|
build_property.EnableGeneratedComInterfaceComImportInterop =
|
||||||
|
build_property.RazorLangVersion = 8.0
|
||||||
|
build_property.SupportLocalizedComponentNames =
|
||||||
|
build_property.GenerateRazorMetadataSourceChecksumAttributes =
|
||||||
|
build_property.MSBuildProjectDirectory = d:\GitHubRepository\Hracicky\x\csob
|
||||||
|
build_property._RazorSourceGeneratorDebug =
|
||||||
|
build_property.EffectiveAnalysisLevelStyle = 8.0
|
||||||
|
build_property.EnableCodeStyleSeverity =
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// <auto-generated/>
|
||||||
|
global using Microsoft.AspNetCore.Builder;
|
||||||
|
global using Microsoft.AspNetCore.Hosting;
|
||||||
|
global using Microsoft.AspNetCore.Http;
|
||||||
|
global using Microsoft.AspNetCore.Routing;
|
||||||
|
global using Microsoft.Extensions.Configuration;
|
||||||
|
global using Microsoft.Extensions.DependencyInjection;
|
||||||
|
global using Microsoft.Extensions.Hosting;
|
||||||
|
global using Microsoft.Extensions.Logging;
|
||||||
|
global using System;
|
||||||
|
global using System.Collections.Generic;
|
||||||
|
global using System.IO;
|
||||||
|
global using System.Linq;
|
||||||
|
global using System.Net.Http;
|
||||||
|
global using System.Net.Http.Json;
|
||||||
|
global using System.Threading;
|
||||||
|
global using System.Threading.Tasks;
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,4 @@
|
|||||||
|
// <autogenerated />
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
[assembly: System.Reflection.AssemblyCompanyAttribute("Csob")]
|
||||||
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
||||||
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+f44ffae9fc38f3033750d626a06752e62097876f")]
|
||||||
|
[assembly: System.Reflection.AssemblyProductAttribute("Csob")]
|
||||||
|
[assembly: System.Reflection.AssemblyTitleAttribute("Csob")]
|
||||||
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|
||||||
|
// Generated by the MSBuild WriteCodeFragment class.
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
60c4996bbd19af9863eeaf63653e6911961f28af17e965a302a0cd7e40f09823
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
is_global = true
|
||||||
|
build_property.TargetFramework = net8.0
|
||||||
|
build_property.TargetFrameworkIdentifier = .NETCoreApp
|
||||||
|
build_property.TargetFrameworkVersion = v8.0
|
||||||
|
build_property.TargetPlatformMinVersion =
|
||||||
|
build_property.UsingMicrosoftNETSdkWeb = true
|
||||||
|
build_property.ProjectTypeGuids =
|
||||||
|
build_property.InvariantGlobalization =
|
||||||
|
build_property.PlatformNeutralAssembly =
|
||||||
|
build_property.EnforceExtendedAnalyzerRules =
|
||||||
|
build_property._SupportedPlatformList = Linux,macOS,Windows
|
||||||
|
build_property.RootNamespace = Csob
|
||||||
|
build_property.RootNamespace = Csob
|
||||||
|
build_property.ProjectDir = d:\GitHubRepository\Hracicky\x\csob\
|
||||||
|
build_property.EnableComHosting =
|
||||||
|
build_property.EnableGeneratedComInterfaceComImportInterop =
|
||||||
|
build_property.RazorLangVersion = 8.0
|
||||||
|
build_property.SupportLocalizedComponentNames =
|
||||||
|
build_property.GenerateRazorMetadataSourceChecksumAttributes =
|
||||||
|
build_property.MSBuildProjectDirectory = d:\GitHubRepository\Hracicky\x\csob
|
||||||
|
build_property._RazorSourceGeneratorDebug =
|
||||||
|
build_property.EffectiveAnalysisLevelStyle = 8.0
|
||||||
|
build_property.EnableCodeStyleSeverity =
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// <auto-generated/>
|
||||||
|
global using Microsoft.AspNetCore.Builder;
|
||||||
|
global using Microsoft.AspNetCore.Hosting;
|
||||||
|
global using Microsoft.AspNetCore.Http;
|
||||||
|
global using Microsoft.AspNetCore.Routing;
|
||||||
|
global using Microsoft.Extensions.Configuration;
|
||||||
|
global using Microsoft.Extensions.DependencyInjection;
|
||||||
|
global using Microsoft.Extensions.Hosting;
|
||||||
|
global using Microsoft.Extensions.Logging;
|
||||||
|
global using System;
|
||||||
|
global using System.Collections.Generic;
|
||||||
|
global using System.IO;
|
||||||
|
global using System.Linq;
|
||||||
|
global using System.Net.Http;
|
||||||
|
global using System.Net.Http.Json;
|
||||||
|
global using System.Threading;
|
||||||
|
global using System.Threading.Tasks;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
[assembly: Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartAttribute("Swashbuckle.AspNetCore.SwaggerGen")]
|
||||||
|
|
||||||
|
// Generated by the MSBuild WriteCodeFragment class.
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
4fe5ffce92f1961f66e6e5263d5719ffa1de25f67d0273a38e9aa8b4e6cbe7ab
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
d:\GitHubRepository\Hracicky\x\csob\bin\Release\net8.0\appsettings.json
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\bin\Release\net8.0\Csob.staticwebassets.endpoints.json
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\bin\Release\net8.0\Csob.exe
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\bin\Release\net8.0\Csob.deps.json
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\bin\Release\net8.0\Csob.runtimeconfig.json
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\bin\Release\net8.0\Csob.dll
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\bin\Release\net8.0\Csob.pdb
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\bin\Release\net8.0\Csob.xml
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\bin\Release\net8.0\Microsoft.OpenApi.dll
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\bin\Release\net8.0\Swashbuckle.AspNetCore.Swagger.dll
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\bin\Release\net8.0\Swashbuckle.AspNetCore.SwaggerGen.dll
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\bin\Release\net8.0\Swashbuckle.AspNetCore.SwaggerUI.dll
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\Csob.csproj.AssemblyReference.cache
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\rpswa.dswa.cache.json
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\Csob.GeneratedMSBuildEditorConfig.editorconfig
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\Csob.AssemblyInfoInputs.cache
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\Csob.AssemblyInfo.cs
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\Csob.csproj.CoreCompileInputs.cache
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\Csob.MvcApplicationPartsAssemblyInfo.cs
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\Csob.MvcApplicationPartsAssemblyInfo.cache
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\rjimswa.dswa.cache.json
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\rjsmrazor.dswa.cache.json
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\rjsmcshtml.dswa.cache.json
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\scopedcss\bundle\Csob.styles.css
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\staticwebassets.build.json
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\staticwebassets.build.json.cache
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\staticwebassets.development.json
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\staticwebassets.build.endpoints.json
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\swae.build.ex.cache
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\Csob.csproj.Up2Date
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\Csob.dll
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\refint\Csob.dll
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\Csob.xml
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\Csob.pdb
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\Csob.genruntimeconfig.cache
|
||||||
|
d:\GitHubRepository\Hracicky\x\csob\obj\Release\net8.0\ref\Csob.dll
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
aa0d3e069791e9af17dc7d6aa9ccb6ac770b84a356fe5664aef85ad9983cc8d5
|
||||||
Binary file not shown.
@@ -0,0 +1,502 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<doc>
|
||||||
|
<assembly>
|
||||||
|
<name>Csob</name>
|
||||||
|
</assembly>
|
||||||
|
<members>
|
||||||
|
<member name="T:Csob.Client.CsobApiAccessor">
|
||||||
|
<summary>
|
||||||
|
Scoped accessor that resolves the credentials for the current request and lazily builds a
|
||||||
|
single <see cref="T:Csob.Client.CsobApiClient"/> (bound to the request's mutual-TLS client) shared by all
|
||||||
|
services handling that request.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Client.CsobApiClient">
|
||||||
|
<summary>
|
||||||
|
Thin HTTP wrapper around the ČSOB PSD2 resource API for a single request. It attaches the
|
||||||
|
mandatory COBS headers (<c>Authorization</c>, <c>APIKEY</c>, <c>TPP-Name</c>, <c>X-Request-ID</c>,
|
||||||
|
<c>Date</c>, <c>User-Involved</c>), sends the call over the per-request mutual-TLS client and
|
||||||
|
returns the response JSON verbatim (<see cref="T:System.Text.Json.Nodes.JsonNode"/>) so no field is lost in translation.
|
||||||
|
Non-success responses become a <see cref="T:Csob.Client.CsobApiException"/>.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Client.CsobApiClient.JsonOptions">
|
||||||
|
<summary>Web defaults (camelCase) match the COBS JSON contract; null properties are omitted on write.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Client.CsobApiClient.ParseErrorCodes(System.String)">
|
||||||
|
<summary>Best-effort parse of the COBS error shape <c>{ "errors": [ { "error": "CODE" } ] }</c>.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Client.CsobApiException">
|
||||||
|
<summary>
|
||||||
|
Raised when the ČSOB PSD2 API returns a non-success HTTP status. Carries the upstream status
|
||||||
|
and the raw error body so the middleware can surface it without leaking credentials.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Client.CsobApiException.ErrorCodes">
|
||||||
|
<summary>Machine-readable error codes parsed from the ČSOB <c>errors[].error</c> array (best effort).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Client.CsobApiException.RawBody">
|
||||||
|
<summary>Raw response body (already credential-free — it is the upstream's own error payload).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Client.CsobApiPaths">
|
||||||
|
<summary>
|
||||||
|
Central registry of ČSOB PSD2 resource path templates (relative to <c>CSOB_API_BASE_URL</c>).
|
||||||
|
|
||||||
|
Paths follow the Czech Open Banking Standard (COBS) as implemented by ČSOB. The account-scoped
|
||||||
|
AISP paths and the PISP <c>/my/payments/.../sign/{signId}</c> authorization flow are confirmed
|
||||||
|
against the ČSOB developer portal; the remaining COBS resources use the same <c>/my/</c> prefix.
|
||||||
|
Keep every path here so a portal-specific correction is a single-file change.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Client.CsobHttpClientProvider">
|
||||||
|
<summary>
|
||||||
|
Provides <see cref="T:System.Net.Http.HttpClient"/> instances configured for mutual TLS with a per-request eIDAS
|
||||||
|
client certificate. ČSOB requires the client certificate at the transport layer, so a single
|
||||||
|
shared client cannot be used across tenants.
|
||||||
|
|
||||||
|
Clients are cached by certificate thumbprint and reused for connection pooling. In practice the
|
||||||
|
certificate identifies the TPP application (not the PSU), so the number of distinct certificates
|
||||||
|
is small and bounded by the set of calling tenants. Certificates and clients live in memory only
|
||||||
|
and never touch disk.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Client.CsobHttpClientProvider.GetClient(System.Security.Cryptography.X509Certificates.X509Certificate2)">
|
||||||
|
<summary>
|
||||||
|
Returns a (cached) mutual-TLS <see cref="T:System.Net.Http.HttpClient"/> presenting <paramref name="certificate"/>.
|
||||||
|
A fresh certificate instance is built per request; if an equivalent one (same thumbprint) is
|
||||||
|
already cached, the redundant instance is disposed so it does not leak.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Configuration.CsobSettings">
|
||||||
|
<summary>
|
||||||
|
Service configuration resolved from environment variables.
|
||||||
|
|
||||||
|
This service is a stateless, multi-tenant proxy in front of the ČSOB PSD2 (Open Banking)
|
||||||
|
API. Unlike the sibling iDoklad service, it holds <b>no per-client secrets</b>: the eIDAS
|
||||||
|
client certificate, OAuth client id/secret, access token, API key and TPP name are all
|
||||||
|
supplied per request as HTTP headers by the calling client service
|
||||||
|
(see <see cref="T:Csob.Credentials.CredentialConstants"/>). Only non-secret infrastructure
|
||||||
|
configuration (API/OAuth base URLs, app metadata, reverse-proxy prefix, timeout) lives here.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Configuration.CsobSettings.RootPath">
|
||||||
|
<summary>Public reverse-proxy prefix (e.g. <c>/apps/csob</c>) injected by AppFactory.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Configuration.CsobSettings.ApiBaseUrl">
|
||||||
|
<summary>
|
||||||
|
Base URL of the ČSOB PSD2 resource API. Production default; override for any other
|
||||||
|
environment. All AISP/PISP/consent path templates are appended to this base.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Configuration.CsobSettings.OAuthAuthorizeUrl">
|
||||||
|
<summary>
|
||||||
|
OAuth2 authorization endpoint (Authorization Code flow, PSU redirect). Production default;
|
||||||
|
verify against the current ČSOB developer portal as the host may change.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Configuration.CsobSettings.OAuthTokenUrl">
|
||||||
|
<summary>
|
||||||
|
OAuth2 token endpoint (code->token and refresh). Production default; verify against the
|
||||||
|
current ČSOB developer portal.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Configuration.CsobSettings.RequestTimeoutSeconds">
|
||||||
|
<summary>Upstream HTTP request timeout in seconds.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Controllers.AccountsController">
|
||||||
|
<summary>
|
||||||
|
AISP – account information. All endpoints require the ČSOB credential headers
|
||||||
|
(eIDAS certificate, access token, API key, TPP name). See the Swagger description for details.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.AccountsController.List(System.Nullable{System.Int32},System.Nullable{System.Int32},System.String,System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>List the PSU's payment accounts (paged).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.AccountsController.Balance(System.String,System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the balance(s) of an account.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.AccountsController.Transactions(System.String,System.Nullable{System.Int32},System.Nullable{System.Int32},System.String,System.String,System.String,System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>List booked transactions of an account (paged, optional date range).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.AccountsController.Awaiting(System.String,System.Nullable{System.Int32},System.Nullable{System.Int32},System.Threading.CancellationToken)">
|
||||||
|
<summary>List awaiting (pending) transactions of an account.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.AccountsController.StandingOrders(System.String,System.Nullable{System.Int32},System.Nullable{System.Int32},System.Threading.CancellationToken)">
|
||||||
|
<summary>List the account's existing standing orders.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.AccountsController.StandingOrderDetail(System.String,System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get a standing-order detail for the account.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.AccountsController.DirectDebits(System.String,System.Nullable{System.Int32},System.Nullable{System.Int32},System.Threading.CancellationToken)">
|
||||||
|
<summary>List the account's direct-debit mandates.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Controllers.ConsentsController">
|
||||||
|
<summary>
|
||||||
|
Common – PSU consent lifecycle. Requires the ČSOB credential headers. The consent body is
|
||||||
|
forwarded to ČSOB as raw JSON (COBS consent shape).
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.ConsentsController.Create(System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Create a consent.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.ConsentsController.Detail(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get a consent detail.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.ConsentsController.Delete(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Revoke a consent.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Controllers.DirectDebitsController">
|
||||||
|
<summary>
|
||||||
|
PISP – direct-debit mandate initiation and its authorization (sign) flow. Requires the ČSOB
|
||||||
|
credential headers. The mandate body is forwarded to ČSOB as raw JSON (COBS direct-debit shape).
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.DirectDebitsController.Initiate(System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Initiate a direct-debit mandate.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.DirectDebitsController.Detail(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the direct-debit mandate detail.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.DirectDebitsController.Status(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the direct-debit instruction status.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.DirectDebitsController.Cancel(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Revoke a direct-debit mandate.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.DirectDebitsController.StartSign(System.String,System.String,System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Start the authorization (SCA) of a direct-debit mandate. Returns the PSU redirect details.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.DirectDebitsController.SignStatus(System.String,System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the current state of an authorization (sign) transaction.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.DirectDebitsController.FinalizeSign(System.String,System.String,System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Finalize an authorization (sign) transaction.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Controllers.MetaController">
|
||||||
|
<summary>Service metadata endpoints. These do not require ČSOB credentials.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.MetaController.Health">
|
||||||
|
<summary>Liveness probe.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.MetaController.Version">
|
||||||
|
<summary>Service name, version and the configured upstream endpoints (no secrets).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.MetaController.Status">
|
||||||
|
<summary>
|
||||||
|
Reports the non-secret configuration. This service is stateless and multi-tenant: it holds
|
||||||
|
no credentials, so there is nothing per-client to report — every secret is supplied per
|
||||||
|
request via headers.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Controllers.OAuthController">
|
||||||
|
<summary>
|
||||||
|
OAuth2 Authorization Code helper for the ČSOB PSD2 PSU consent flow. Build the authorization URL,
|
||||||
|
redirect the PSU to it, then exchange the returned code for tokens. The token/refresh calls run
|
||||||
|
over mutual TLS, so they require the <c>X-CSOB-Certificate</c> header; client id/secret are also
|
||||||
|
supplied per request (this service is multi-tenant).
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.OAuthController.AuthorizationUrl(System.String,System.String,System.String)">
|
||||||
|
<summary>Build the ČSOB authorization URL to which the PSU must be redirected.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.OAuthController.Token(Csob.Models.TokenExchangeRequest,System.Threading.CancellationToken)">
|
||||||
|
<summary>Exchange an authorization code for an access/refresh token (mutual TLS).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.OAuthController.Refresh(Csob.Models.TokenRefreshRequest,System.Threading.CancellationToken)">
|
||||||
|
<summary>Refresh an access token using a refresh token (mutual TLS).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Controllers.PaymentsController">
|
||||||
|
<summary>
|
||||||
|
PISP – single payment initiation and its authorization (sign) flow. Requires the ČSOB credential
|
||||||
|
headers. After <c>POST /payments</c> the response carries <c>signInfo.signId</c>; use it with the
|
||||||
|
sign endpoints to drive Strong Customer Authentication (SCA).
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.PaymentsController.Initiate(Csob.Models.PaymentInitiationRequest,System.Threading.CancellationToken)">
|
||||||
|
<summary>Initiate a domestic (DMCT) or SEPA (ESCT) payment.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.PaymentsController.Detail(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the full payment detail.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.PaymentsController.Status(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the payment instruction status.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.PaymentsController.Cancel(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Cancel a not-yet-authorized payment.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.PaymentsController.StartSign(System.String,System.String,System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Start the authorization (SCA) of a payment. Returns the PSU redirect details.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.PaymentsController.SignStatus(System.String,System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the current state of an authorization (sign) transaction.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.PaymentsController.FinalizeSign(System.String,System.String,System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Finalize an authorization (sign) transaction.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Controllers.StandingOrdersController">
|
||||||
|
<summary>PISP – standing-order initiation and its authorization (sign) flow. Requires the ČSOB credential headers.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.StandingOrdersController.Initiate(Csob.Models.StandingOrderInitiationRequest,System.Threading.CancellationToken)">
|
||||||
|
<summary>Initiate a standing order.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.StandingOrdersController.Detail(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the standing-order detail.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.StandingOrdersController.Status(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the standing-order instruction status.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.StandingOrdersController.Cancel(System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Cancel a standing order.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.StandingOrdersController.StartSign(System.String,System.String,System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Start the authorization (SCA) of a standing order. Returns the PSU redirect details.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.StandingOrdersController.SignStatus(System.String,System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Get the current state of an authorization (sign) transaction.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Controllers.StandingOrdersController.FinalizeSign(System.String,System.String,System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Finalize an authorization (sign) transaction.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Credentials.CredentialConstants">
|
||||||
|
<summary>
|
||||||
|
Names of the HTTP headers that carry per-request ČSOB credentials and context.
|
||||||
|
|
||||||
|
This service is multi-tenant: it stores no credentials itself. Every sensitive value is
|
||||||
|
supplied per request in a header (never the query string or body) and is forwarded to ČSOB.
|
||||||
|
Headers must therefore only be sent over TLS. Nothing here is logged or persisted.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Credentials.CredentialConstants.CertificateHeader">
|
||||||
|
<summary>
|
||||||
|
eIDAS client certificate (QWAC) as a Base64-encoded PKCS#12 / PFX bundle, including the
|
||||||
|
private key and the full chain. Used to establish the mutual-TLS connection to ČSOB.
|
||||||
|
Analogous to Node's <c>https.Agent({ pfx, passphrase })</c>.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Credentials.CredentialConstants.CertificatePasswordHeader">
|
||||||
|
<summary>Optional passphrase protecting the PFX in <see cref="F:Csob.Credentials.CredentialConstants.CertificateHeader"/>.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Credentials.CredentialConstants.AccessTokenHeader">
|
||||||
|
<summary>OAuth2 Bearer access token obtained for the PSU; forwarded as <c>Authorization: Bearer</c>.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Credentials.CredentialConstants.ApiKeyHeader">
|
||||||
|
<summary>ČSOB application API key; forwarded as the <c>APIKEY</c> header.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Credentials.CredentialConstants.TppNameHeader">
|
||||||
|
<summary>TPP (third-party provider) organisation name; forwarded as the <c>TPP-Name</c> header.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Credentials.CredentialConstants.ClientIdHeader">
|
||||||
|
<summary>OAuth2 client id of the registered TPP application (used by the OAuth helper endpoints).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Credentials.CredentialConstants.ClientSecretHeader">
|
||||||
|
<summary>OAuth2 client secret of the registered TPP application (used by the OAuth helper endpoints).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Credentials.CredentialConstants.UserInvolvedHeader">
|
||||||
|
<summary>Whether the PSU is online/involved in the request; forwarded as <c>User-Involved</c> (default false).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:Csob.Credentials.CredentialConstants.UserIpAddressHeader">
|
||||||
|
<summary>PSU IP address; forwarded as <c>User-IP-Address</c>.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Credentials.CsobCredentials">
|
||||||
|
<summary>
|
||||||
|
Fully resolved set of per-request credentials and PSU context used to call the ČSOB PSD2 API.
|
||||||
|
Built from request headers by <see cref="T:Csob.Credentials.RequestCredentialsProvider"/>; never logged.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Credentials.CsobCredentials.AccessToken">
|
||||||
|
<summary>OAuth2 Bearer access token (forwarded as <c>Authorization: Bearer</c>).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Credentials.CsobCredentials.ApiKey">
|
||||||
|
<summary>ČSOB application API key (forwarded as <c>APIKEY</c>).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Credentials.CsobCredentials.TppName">
|
||||||
|
<summary>TPP organisation name (forwarded as <c>TPP-Name</c>).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Credentials.CsobCredentials.Certificate">
|
||||||
|
<summary>
|
||||||
|
eIDAS client certificate (with private key) for mutual TLS. Optional at the type level so
|
||||||
|
metadata endpoints can resolve context, but required for any real upstream call.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Credentials.CsobCredentials.UserInvolved">
|
||||||
|
<summary>Whether the PSU is online for this request (<c>User-Involved</c>); defaults to false.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Credentials.CsobCredentials.UserIpAddress">
|
||||||
|
<summary>Optional PSU IP address (<c>User-IP-Address</c>).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Credentials.MissingCredentialsException">
|
||||||
|
<summary>
|
||||||
|
Raised when a request does not provide the credential headers required to call ČSOB.
|
||||||
|
Translated to HTTP 401 by the exception-handling middleware.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Credentials.RequestCredentialsProvider">
|
||||||
|
<summary>
|
||||||
|
Resolves the ČSOB credentials and PSU context for the current request, exclusively from HTTP
|
||||||
|
headers (this service stores no secrets). Missing required headers produce a 401; a malformed
|
||||||
|
certificate or wrong passphrase produces a 400 (via <see cref="T:System.Security.Cryptography.CryptographicException"/>).
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Credentials.RequestCredentialsProvider.Header(System.String)">
|
||||||
|
<summary>Reads a single request header, returning null when absent or blank.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Credentials.RequestCredentialsProvider.TryBuildCertificate">
|
||||||
|
<summary>
|
||||||
|
Builds the eIDAS client certificate from the Base64 PFX header (+ optional passphrase).
|
||||||
|
Returns null when no certificate header is present.
|
||||||
|
</summary>
|
||||||
|
<exception cref="T:System.Security.Cryptography.CryptographicException">The header is not valid Base64 or the PFX/passphrase is invalid.</exception>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Credentials.RequestCredentialsProvider.Resolve">
|
||||||
|
<summary>
|
||||||
|
Resolves the full credential set required for an AISP/PISP/consent call. Throws
|
||||||
|
<see cref="T:Csob.Credentials.MissingCredentialsException"/> if any required header is absent.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Infrastructure.CredentialHeadersOperationFilter">
|
||||||
|
<summary>
|
||||||
|
Documents the per-request credential headers in Swagger. Metadata endpoints need none; the
|
||||||
|
OAuth helper needs the certificate + client id/secret; every other (AISP/PISP/consent) endpoint
|
||||||
|
needs the certificate + access token + API key + TPP name. Headers are marked optional at the
|
||||||
|
schema level (the service validates them at runtime) but the descriptions state what is required.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Infrastructure.ExceptionHandlingMiddleware">
|
||||||
|
<summary>
|
||||||
|
Translates domain exceptions into JSON <see cref="T:Microsoft.AspNetCore.Mvc.ProblemDetails"/> responses. Credentials are
|
||||||
|
never logged — only upstream status codes and error codes (the upstream's own payload). Errors
|
||||||
|
are always logged (no silent failures).
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.Amount">
|
||||||
|
<summary>Monetary amount with ISO 4217 currency.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.AmountContainer">
|
||||||
|
<summary>Wrapper matching the <c>amount</c> object whose <c>instructedAmount</c> holds the value/currency.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.AccountIdentification">
|
||||||
|
<summary>Account number identification. For domestic/SEPA writes only <c>iban</c> is required.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.AccountReference">
|
||||||
|
<summary>Account reference with optional currency (<c>debtorAccount</c>/<c>creditorAccount</c>).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.ServiceLevel.Code">
|
||||||
|
<summary>Payment scheme: DMCT (domestic), ESCT (SEPA), XBCT (cross-border), EXCT, NXCT.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.PaymentTypeInformation.InstructionPriority">
|
||||||
|
<summary>NORM (default), HIGH (express) or INST (instant).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.Party">
|
||||||
|
<summary>A party (debtor/creditor/ultimate*). Identification is variable, so kept as raw JSON.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.RemittanceInformation">
|
||||||
|
<summary>
|
||||||
|
Remittance information. <c>unstructured</c> is free text (Czech symbols may be encoded as
|
||||||
|
<c>/VS/.../SS/.../KS/...</c>); <c>structured</c> varies (its <c>reference</c> may be a string or
|
||||||
|
an array) so it is passed through as raw JSON.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.TokenExchangeRequest">
|
||||||
|
<summary>Body for <c>POST /oauth/token</c> (Authorization Code grant). Secrets travel in headers.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.TokenExchangeRequest.Code">
|
||||||
|
<summary>Authorization code returned to the redirect URI after PSU consent.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.TokenExchangeRequest.RedirectUri">
|
||||||
|
<summary>Redirect URI registered for the TPP app; must match the one used to obtain the code.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.TokenRefreshRequest">
|
||||||
|
<summary>Body for <c>POST /oauth/refresh</c> (Refresh Token grant).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.AuthorizationUrlResponse">
|
||||||
|
<summary>Response of <c>GET /oauth/authorization-url</c>.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.AuthorizationUrlResponse.AuthorizationUrl">
|
||||||
|
<summary>Fully-built ČSOB authorization URL to which the PSU must be redirected.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.AuthorizationUrlResponse.State">
|
||||||
|
<summary>The opaque <c>state</c> value echoed back on the redirect (CSRF protection).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.PaymentIdentification.InstructionIdentification">
|
||||||
|
<summary>Unique instruction id assigned by the TPP (idempotency key). Max 35 chars.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.PaymentInitiationRequest">
|
||||||
|
<summary>
|
||||||
|
Payment initiation request (<c>POST /my/payments</c>). Covers domestic (DMCT) and SEPA (ESCT)
|
||||||
|
payments — SEPA-only fields (creditor address, agent, ultimate parties, purpose) are optional.
|
||||||
|
Any additional COBS field not modelled here is preserved via <see cref="P:Csob.Models.PaymentInitiationRequest.AdditionalData"/>.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.PaymentInitiationRequest.AdditionalData">
|
||||||
|
<summary>Any COBS fields not explicitly modelled are forwarded to ČSOB unchanged.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.StandingOrderExecution.Interval">
|
||||||
|
<summary>DAILY, WEEKLY, MONTHLY, BI_MONTHLY, QUARTERLY, HALFYEARLY, YEARLY, SINGLE, IRREGULAR.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.StandingOrderExecution.IntervalDue">
|
||||||
|
<summary>Day within the interval (e.g. day-of-month "25").</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.StandingOrderExecution.Mode">
|
||||||
|
<summary>e.g. MAX_AMOUNT_EXCEEDED, UNTIL_CANCELLATION.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.StandingOrderDetail.Exceptions">
|
||||||
|
<summary>Optional <c>exceptions</c> block (stoppages/breaks); shape varies, kept as raw JSON.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Models.StandingOrderDetail.Validity">
|
||||||
|
<summary>Optional <c>validity</c> block (lastExecutionDate/maxAmount); kept as raw JSON.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Models.StandingOrderInitiationRequest">
|
||||||
|
<summary>Standing-order initiation request (<c>POST /my/standingorders</c>).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Services.AccountsService">
|
||||||
|
<summary>AISP – account information (accounts, balance, transactions, standing orders, direct debits).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Services.TransactionQuery">
|
||||||
|
<summary>Optional filters for the transactions listing (forwarded as query parameters).</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Services.TransactionQuery.DateFrom">
|
||||||
|
<summary>ISO date (YYYY-MM-DD) lower bound.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="P:Csob.Services.TransactionQuery.DateTo">
|
||||||
|
<summary>ISO date (YYYY-MM-DD) upper bound.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Services.ConsentsService">
|
||||||
|
<summary>
|
||||||
|
Common – PSU consent lifecycle (create / detail / revoke). The consent request body varies
|
||||||
|
across COBS profiles, so it is accepted and forwarded as raw JSON.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Services.DirectDebitsService">
|
||||||
|
<summary>
|
||||||
|
PISP – direct-debit mandate initiation, detail/status, revocation and the sign (SCA) flow.
|
||||||
|
The mandate creation body varies across COBS profiles, so it is accepted and forwarded as raw JSON.
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Services.OAuthService">
|
||||||
|
<summary>
|
||||||
|
OAuth2 Authorization Code helper for the ČSOB PSD2 flow. Builds the PSU authorization URL and
|
||||||
|
exchanges/refreshes tokens against the ČSOB token endpoint. The token endpoint is behind mutual
|
||||||
|
TLS, so the client certificate header is required for the token/refresh calls. Client id/secret
|
||||||
|
are taken from per-request headers (this service is multi-tenant and stores no app credentials).
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Services.OAuthService.BuildAuthorizationUrl(System.String,System.String,System.String)">
|
||||||
|
<summary>Builds the authorization URL to which the PSU must be redirected.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Services.PaymentsService">
|
||||||
|
<summary>PISP – single payment initiation, status/detail, cancellation and the sign (SCA) flow.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Services.PaymentsService.StartSignAsync(System.String,System.String,System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Starts transaction authorization (SCA). Returns the redirect details for the PSU.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Services.PaymentsService.SignStatusAsync(System.String,System.String,System.Threading.CancellationToken)">
|
||||||
|
<summary>Gets the current state of an authorization (sign) transaction.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:Csob.Services.PaymentsService.FinalizeSignAsync(System.String,System.String,System.Text.Json.Nodes.JsonNode,System.Threading.CancellationToken)">
|
||||||
|
<summary>Finalizes an authorization (sign) transaction.</summary>
|
||||||
|
</member>
|
||||||
|
<member name="T:Csob.Services.StandingOrdersService">
|
||||||
|
<summary>PISP – standing-order initiation, detail/status, cancellation and the sign (SCA) flow.</summary>
|
||||||
|
</member>
|
||||||
|
</members>
|
||||||
|
</doc>
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
{"GlobalPropertiesHash":"cmk/g0ZNXu9gJ27DjjLcpkDBlTy8RbJJZDNXpi5zobE=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["RIECXlOw3nHsuvqvxZXaPj/jy\u002BfI4EndOE5nviSURLE=","7d9SLaipaa/OPEXpY1q2f1TJiMYg8zqpA6Pih/R8Tcs=","lyWTB1SAn8j26WrlgTpjAfgPiRk8CHzueKPU4K7RU/w=","AqlAhVdvD3E/meEAwnjKEEXVbI0nCoFmuUVcx4RWscw=","9bwgN9Y/AEO67vqkjG6ZJRLuRMuKd2T8iH5YYodZ8Qo=","qnfluIVdGAut31To7iOrysqO/P2XiKnuymbhW00B3F4=","lF1S3jjLqtF4rW5S0esV3nAvTIcaMB/6tBlOniDiUvI=","pHVlcHSyz8AmNqJCN6sger3xiF6u4d2yhyHAaRW4PsM="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"GlobalPropertiesHash":"g3hu5zSe6T51z8fRKoyDb/XOC4JY4b05h9YvX9zmd1I=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["RIECXlOw3nHsuvqvxZXaPj/jy\u002BfI4EndOE5nviSURLE=","7d9SLaipaa/OPEXpY1q2f1TJiMYg8zqpA6Pih/R8Tcs=","lyWTB1SAn8j26WrlgTpjAfgPiRk8CHzueKPU4K7RU/w=","AqlAhVdvD3E/meEAwnjKEEXVbI0nCoFmuUVcx4RWscw=","9bwgN9Y/AEO67vqkjG6ZJRLuRMuKd2T8iH5YYodZ8Qo=","qnfluIVdGAut31To7iOrysqO/P2XiKnuymbhW00B3F4=","lF1S3jjLqtF4rW5S0esV3nAvTIcaMB/6tBlOniDiUvI=","pHVlcHSyz8AmNqJCN6sger3xiF6u4d2yhyHAaRW4PsM="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"GlobalPropertiesHash":"s18Zph11o4XKbouZq9V6XR416tqQ2cEqoV1zo+Pq7KQ=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["RIECXlOw3nHsuvqvxZXaPj/jy\u002BfI4EndOE5nviSURLE="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"Version":1,"ManifestType":"Build","Endpoints":[]}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"Version":1,"Hash":"BhivtHlvAyC9wdGc/wHwm/2EEkmCBBuuzAERe0/lTAw=","Source":"Csob","BasePath":"/","Mode":"Root","ManifestType":"Build","ReferencedProjectsConfiguration":[],"DiscoveryPatterns":[],"Assets":[],"Endpoints":[]}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
BhivtHlvAyC9wdGc/wHwm/2EEkmCBBuuzAERe0/lTAw=
|
||||||
@@ -0,0 +1,533 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"targets": {
|
||||||
|
"net8.0": {
|
||||||
|
"Microsoft.Extensions.ApiDescription.Server/6.0.5": {
|
||||||
|
"type": "package",
|
||||||
|
"build": {
|
||||||
|
"build/Microsoft.Extensions.ApiDescription.Server.props": {},
|
||||||
|
"build/Microsoft.Extensions.ApiDescription.Server.targets": {}
|
||||||
|
},
|
||||||
|
"buildMultiTargeting": {
|
||||||
|
"buildMultiTargeting/Microsoft.Extensions.ApiDescription.Server.props": {},
|
||||||
|
"buildMultiTargeting/Microsoft.Extensions.ApiDescription.Server.targets": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.OpenApi/1.6.14": {
|
||||||
|
"type": "package",
|
||||||
|
"compile": {
|
||||||
|
"lib/netstandard2.0/Microsoft.OpenApi.dll": {
|
||||||
|
"related": ".pdb;.xml"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"lib/netstandard2.0/Microsoft.OpenApi.dll": {
|
||||||
|
"related": ".pdb;.xml"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Swashbuckle.AspNetCore/6.9.0": {
|
||||||
|
"type": "package",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.Extensions.ApiDescription.Server": "6.0.5",
|
||||||
|
"Swashbuckle.AspNetCore.Swagger": "6.9.0",
|
||||||
|
"Swashbuckle.AspNetCore.SwaggerGen": "6.9.0",
|
||||||
|
"Swashbuckle.AspNetCore.SwaggerUI": "6.9.0"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"build/Swashbuckle.AspNetCore.props": {}
|
||||||
|
},
|
||||||
|
"buildMultiTargeting": {
|
||||||
|
"buildMultiTargeting/Swashbuckle.AspNetCore.props": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Swashbuckle.AspNetCore.Swagger/6.9.0": {
|
||||||
|
"type": "package",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.OpenApi": "1.6.14"
|
||||||
|
},
|
||||||
|
"compile": {
|
||||||
|
"lib/net8.0/Swashbuckle.AspNetCore.Swagger.dll": {
|
||||||
|
"related": ".pdb;.xml"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"lib/net8.0/Swashbuckle.AspNetCore.Swagger.dll": {
|
||||||
|
"related": ".pdb;.xml"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"frameworkReferences": [
|
||||||
|
"Microsoft.AspNetCore.App"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Swashbuckle.AspNetCore.SwaggerGen/6.9.0": {
|
||||||
|
"type": "package",
|
||||||
|
"dependencies": {
|
||||||
|
"Swashbuckle.AspNetCore.Swagger": "6.9.0"
|
||||||
|
},
|
||||||
|
"compile": {
|
||||||
|
"lib/net8.0/Swashbuckle.AspNetCore.SwaggerGen.dll": {
|
||||||
|
"related": ".pdb;.xml"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"lib/net8.0/Swashbuckle.AspNetCore.SwaggerGen.dll": {
|
||||||
|
"related": ".pdb;.xml"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Swashbuckle.AspNetCore.SwaggerUI/6.9.0": {
|
||||||
|
"type": "package",
|
||||||
|
"compile": {
|
||||||
|
"lib/net8.0/Swashbuckle.AspNetCore.SwaggerUI.dll": {
|
||||||
|
"related": ".pdb;.xml"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"lib/net8.0/Swashbuckle.AspNetCore.SwaggerUI.dll": {
|
||||||
|
"related": ".pdb;.xml"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"frameworkReferences": [
|
||||||
|
"Microsoft.AspNetCore.App"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libraries": {
|
||||||
|
"Microsoft.Extensions.ApiDescription.Server/6.0.5": {
|
||||||
|
"sha512": "Ckb5EDBUNJdFWyajfXzUIMRkhf52fHZOQuuZg/oiu8y7zDCVwD0iHhew6MnThjHmevanpxL3f5ci2TtHQEN6bw==",
|
||||||
|
"type": "package",
|
||||||
|
"path": "microsoft.extensions.apidescription.server/6.0.5",
|
||||||
|
"hasTools": true,
|
||||||
|
"files": [
|
||||||
|
".nupkg.metadata",
|
||||||
|
".signature.p7s",
|
||||||
|
"Icon.png",
|
||||||
|
"build/Microsoft.Extensions.ApiDescription.Server.props",
|
||||||
|
"build/Microsoft.Extensions.ApiDescription.Server.targets",
|
||||||
|
"buildMultiTargeting/Microsoft.Extensions.ApiDescription.Server.props",
|
||||||
|
"buildMultiTargeting/Microsoft.Extensions.ApiDescription.Server.targets",
|
||||||
|
"microsoft.extensions.apidescription.server.6.0.5.nupkg.sha512",
|
||||||
|
"microsoft.extensions.apidescription.server.nuspec",
|
||||||
|
"tools/Newtonsoft.Json.dll",
|
||||||
|
"tools/dotnet-getdocument.deps.json",
|
||||||
|
"tools/dotnet-getdocument.dll",
|
||||||
|
"tools/dotnet-getdocument.runtimeconfig.json",
|
||||||
|
"tools/net461-x86/GetDocument.Insider.exe",
|
||||||
|
"tools/net461-x86/GetDocument.Insider.exe.config",
|
||||||
|
"tools/net461-x86/Microsoft.Win32.Primitives.dll",
|
||||||
|
"tools/net461-x86/System.AppContext.dll",
|
||||||
|
"tools/net461-x86/System.Buffers.dll",
|
||||||
|
"tools/net461-x86/System.Collections.Concurrent.dll",
|
||||||
|
"tools/net461-x86/System.Collections.NonGeneric.dll",
|
||||||
|
"tools/net461-x86/System.Collections.Specialized.dll",
|
||||||
|
"tools/net461-x86/System.Collections.dll",
|
||||||
|
"tools/net461-x86/System.ComponentModel.EventBasedAsync.dll",
|
||||||
|
"tools/net461-x86/System.ComponentModel.Primitives.dll",
|
||||||
|
"tools/net461-x86/System.ComponentModel.TypeConverter.dll",
|
||||||
|
"tools/net461-x86/System.ComponentModel.dll",
|
||||||
|
"tools/net461-x86/System.Console.dll",
|
||||||
|
"tools/net461-x86/System.Data.Common.dll",
|
||||||
|
"tools/net461-x86/System.Diagnostics.Contracts.dll",
|
||||||
|
"tools/net461-x86/System.Diagnostics.Debug.dll",
|
||||||
|
"tools/net461-x86/System.Diagnostics.DiagnosticSource.dll",
|
||||||
|
"tools/net461-x86/System.Diagnostics.FileVersionInfo.dll",
|
||||||
|
"tools/net461-x86/System.Diagnostics.Process.dll",
|
||||||
|
"tools/net461-x86/System.Diagnostics.StackTrace.dll",
|
||||||
|
"tools/net461-x86/System.Diagnostics.TextWriterTraceListener.dll",
|
||||||
|
"tools/net461-x86/System.Diagnostics.Tools.dll",
|
||||||
|
"tools/net461-x86/System.Diagnostics.TraceSource.dll",
|
||||||
|
"tools/net461-x86/System.Diagnostics.Tracing.dll",
|
||||||
|
"tools/net461-x86/System.Drawing.Primitives.dll",
|
||||||
|
"tools/net461-x86/System.Dynamic.Runtime.dll",
|
||||||
|
"tools/net461-x86/System.Globalization.Calendars.dll",
|
||||||
|
"tools/net461-x86/System.Globalization.Extensions.dll",
|
||||||
|
"tools/net461-x86/System.Globalization.dll",
|
||||||
|
"tools/net461-x86/System.IO.Compression.ZipFile.dll",
|
||||||
|
"tools/net461-x86/System.IO.Compression.dll",
|
||||||
|
"tools/net461-x86/System.IO.FileSystem.DriveInfo.dll",
|
||||||
|
"tools/net461-x86/System.IO.FileSystem.Primitives.dll",
|
||||||
|
"tools/net461-x86/System.IO.FileSystem.Watcher.dll",
|
||||||
|
"tools/net461-x86/System.IO.FileSystem.dll",
|
||||||
|
"tools/net461-x86/System.IO.IsolatedStorage.dll",
|
||||||
|
"tools/net461-x86/System.IO.MemoryMappedFiles.dll",
|
||||||
|
"tools/net461-x86/System.IO.Pipes.dll",
|
||||||
|
"tools/net461-x86/System.IO.UnmanagedMemoryStream.dll",
|
||||||
|
"tools/net461-x86/System.IO.dll",
|
||||||
|
"tools/net461-x86/System.Linq.Expressions.dll",
|
||||||
|
"tools/net461-x86/System.Linq.Parallel.dll",
|
||||||
|
"tools/net461-x86/System.Linq.Queryable.dll",
|
||||||
|
"tools/net461-x86/System.Linq.dll",
|
||||||
|
"tools/net461-x86/System.Memory.dll",
|
||||||
|
"tools/net461-x86/System.Net.Http.dll",
|
||||||
|
"tools/net461-x86/System.Net.NameResolution.dll",
|
||||||
|
"tools/net461-x86/System.Net.NetworkInformation.dll",
|
||||||
|
"tools/net461-x86/System.Net.Ping.dll",
|
||||||
|
"tools/net461-x86/System.Net.Primitives.dll",
|
||||||
|
"tools/net461-x86/System.Net.Requests.dll",
|
||||||
|
"tools/net461-x86/System.Net.Security.dll",
|
||||||
|
"tools/net461-x86/System.Net.Sockets.dll",
|
||||||
|
"tools/net461-x86/System.Net.WebHeaderCollection.dll",
|
||||||
|
"tools/net461-x86/System.Net.WebSockets.Client.dll",
|
||||||
|
"tools/net461-x86/System.Net.WebSockets.dll",
|
||||||
|
"tools/net461-x86/System.Numerics.Vectors.dll",
|
||||||
|
"tools/net461-x86/System.ObjectModel.dll",
|
||||||
|
"tools/net461-x86/System.Reflection.Extensions.dll",
|
||||||
|
"tools/net461-x86/System.Reflection.Primitives.dll",
|
||||||
|
"tools/net461-x86/System.Reflection.dll",
|
||||||
|
"tools/net461-x86/System.Resources.Reader.dll",
|
||||||
|
"tools/net461-x86/System.Resources.ResourceManager.dll",
|
||||||
|
"tools/net461-x86/System.Resources.Writer.dll",
|
||||||
|
"tools/net461-x86/System.Runtime.CompilerServices.Unsafe.dll",
|
||||||
|
"tools/net461-x86/System.Runtime.CompilerServices.VisualC.dll",
|
||||||
|
"tools/net461-x86/System.Runtime.Extensions.dll",
|
||||||
|
"tools/net461-x86/System.Runtime.Handles.dll",
|
||||||
|
"tools/net461-x86/System.Runtime.InteropServices.RuntimeInformation.dll",
|
||||||
|
"tools/net461-x86/System.Runtime.InteropServices.dll",
|
||||||
|
"tools/net461-x86/System.Runtime.Numerics.dll",
|
||||||
|
"tools/net461-x86/System.Runtime.Serialization.Formatters.dll",
|
||||||
|
"tools/net461-x86/System.Runtime.Serialization.Json.dll",
|
||||||
|
"tools/net461-x86/System.Runtime.Serialization.Primitives.dll",
|
||||||
|
"tools/net461-x86/System.Runtime.Serialization.Xml.dll",
|
||||||
|
"tools/net461-x86/System.Runtime.dll",
|
||||||
|
"tools/net461-x86/System.Security.Claims.dll",
|
||||||
|
"tools/net461-x86/System.Security.Cryptography.Algorithms.dll",
|
||||||
|
"tools/net461-x86/System.Security.Cryptography.Csp.dll",
|
||||||
|
"tools/net461-x86/System.Security.Cryptography.Encoding.dll",
|
||||||
|
"tools/net461-x86/System.Security.Cryptography.Primitives.dll",
|
||||||
|
"tools/net461-x86/System.Security.Cryptography.X509Certificates.dll",
|
||||||
|
"tools/net461-x86/System.Security.Principal.dll",
|
||||||
|
"tools/net461-x86/System.Security.SecureString.dll",
|
||||||
|
"tools/net461-x86/System.Text.Encoding.Extensions.dll",
|
||||||
|
"tools/net461-x86/System.Text.Encoding.dll",
|
||||||
|
"tools/net461-x86/System.Text.RegularExpressions.dll",
|
||||||
|
"tools/net461-x86/System.Threading.Overlapped.dll",
|
||||||
|
"tools/net461-x86/System.Threading.Tasks.Parallel.dll",
|
||||||
|
"tools/net461-x86/System.Threading.Tasks.dll",
|
||||||
|
"tools/net461-x86/System.Threading.Thread.dll",
|
||||||
|
"tools/net461-x86/System.Threading.ThreadPool.dll",
|
||||||
|
"tools/net461-x86/System.Threading.Timer.dll",
|
||||||
|
"tools/net461-x86/System.Threading.dll",
|
||||||
|
"tools/net461-x86/System.ValueTuple.dll",
|
||||||
|
"tools/net461-x86/System.Xml.ReaderWriter.dll",
|
||||||
|
"tools/net461-x86/System.Xml.XDocument.dll",
|
||||||
|
"tools/net461-x86/System.Xml.XPath.XDocument.dll",
|
||||||
|
"tools/net461-x86/System.Xml.XPath.dll",
|
||||||
|
"tools/net461-x86/System.Xml.XmlDocument.dll",
|
||||||
|
"tools/net461-x86/System.Xml.XmlSerializer.dll",
|
||||||
|
"tools/net461-x86/netstandard.dll",
|
||||||
|
"tools/net461/GetDocument.Insider.exe",
|
||||||
|
"tools/net461/GetDocument.Insider.exe.config",
|
||||||
|
"tools/net461/Microsoft.Win32.Primitives.dll",
|
||||||
|
"tools/net461/System.AppContext.dll",
|
||||||
|
"tools/net461/System.Buffers.dll",
|
||||||
|
"tools/net461/System.Collections.Concurrent.dll",
|
||||||
|
"tools/net461/System.Collections.NonGeneric.dll",
|
||||||
|
"tools/net461/System.Collections.Specialized.dll",
|
||||||
|
"tools/net461/System.Collections.dll",
|
||||||
|
"tools/net461/System.ComponentModel.EventBasedAsync.dll",
|
||||||
|
"tools/net461/System.ComponentModel.Primitives.dll",
|
||||||
|
"tools/net461/System.ComponentModel.TypeConverter.dll",
|
||||||
|
"tools/net461/System.ComponentModel.dll",
|
||||||
|
"tools/net461/System.Console.dll",
|
||||||
|
"tools/net461/System.Data.Common.dll",
|
||||||
|
"tools/net461/System.Diagnostics.Contracts.dll",
|
||||||
|
"tools/net461/System.Diagnostics.Debug.dll",
|
||||||
|
"tools/net461/System.Diagnostics.DiagnosticSource.dll",
|
||||||
|
"tools/net461/System.Diagnostics.FileVersionInfo.dll",
|
||||||
|
"tools/net461/System.Diagnostics.Process.dll",
|
||||||
|
"tools/net461/System.Diagnostics.StackTrace.dll",
|
||||||
|
"tools/net461/System.Diagnostics.TextWriterTraceListener.dll",
|
||||||
|
"tools/net461/System.Diagnostics.Tools.dll",
|
||||||
|
"tools/net461/System.Diagnostics.TraceSource.dll",
|
||||||
|
"tools/net461/System.Diagnostics.Tracing.dll",
|
||||||
|
"tools/net461/System.Drawing.Primitives.dll",
|
||||||
|
"tools/net461/System.Dynamic.Runtime.dll",
|
||||||
|
"tools/net461/System.Globalization.Calendars.dll",
|
||||||
|
"tools/net461/System.Globalization.Extensions.dll",
|
||||||
|
"tools/net461/System.Globalization.dll",
|
||||||
|
"tools/net461/System.IO.Compression.ZipFile.dll",
|
||||||
|
"tools/net461/System.IO.Compression.dll",
|
||||||
|
"tools/net461/System.IO.FileSystem.DriveInfo.dll",
|
||||||
|
"tools/net461/System.IO.FileSystem.Primitives.dll",
|
||||||
|
"tools/net461/System.IO.FileSystem.Watcher.dll",
|
||||||
|
"tools/net461/System.IO.FileSystem.dll",
|
||||||
|
"tools/net461/System.IO.IsolatedStorage.dll",
|
||||||
|
"tools/net461/System.IO.MemoryMappedFiles.dll",
|
||||||
|
"tools/net461/System.IO.Pipes.dll",
|
||||||
|
"tools/net461/System.IO.UnmanagedMemoryStream.dll",
|
||||||
|
"tools/net461/System.IO.dll",
|
||||||
|
"tools/net461/System.Linq.Expressions.dll",
|
||||||
|
"tools/net461/System.Linq.Parallel.dll",
|
||||||
|
"tools/net461/System.Linq.Queryable.dll",
|
||||||
|
"tools/net461/System.Linq.dll",
|
||||||
|
"tools/net461/System.Memory.dll",
|
||||||
|
"tools/net461/System.Net.Http.dll",
|
||||||
|
"tools/net461/System.Net.NameResolution.dll",
|
||||||
|
"tools/net461/System.Net.NetworkInformation.dll",
|
||||||
|
"tools/net461/System.Net.Ping.dll",
|
||||||
|
"tools/net461/System.Net.Primitives.dll",
|
||||||
|
"tools/net461/System.Net.Requests.dll",
|
||||||
|
"tools/net461/System.Net.Security.dll",
|
||||||
|
"tools/net461/System.Net.Sockets.dll",
|
||||||
|
"tools/net461/System.Net.WebHeaderCollection.dll",
|
||||||
|
"tools/net461/System.Net.WebSockets.Client.dll",
|
||||||
|
"tools/net461/System.Net.WebSockets.dll",
|
||||||
|
"tools/net461/System.Numerics.Vectors.dll",
|
||||||
|
"tools/net461/System.ObjectModel.dll",
|
||||||
|
"tools/net461/System.Reflection.Extensions.dll",
|
||||||
|
"tools/net461/System.Reflection.Primitives.dll",
|
||||||
|
"tools/net461/System.Reflection.dll",
|
||||||
|
"tools/net461/System.Resources.Reader.dll",
|
||||||
|
"tools/net461/System.Resources.ResourceManager.dll",
|
||||||
|
"tools/net461/System.Resources.Writer.dll",
|
||||||
|
"tools/net461/System.Runtime.CompilerServices.Unsafe.dll",
|
||||||
|
"tools/net461/System.Runtime.CompilerServices.VisualC.dll",
|
||||||
|
"tools/net461/System.Runtime.Extensions.dll",
|
||||||
|
"tools/net461/System.Runtime.Handles.dll",
|
||||||
|
"tools/net461/System.Runtime.InteropServices.RuntimeInformation.dll",
|
||||||
|
"tools/net461/System.Runtime.InteropServices.dll",
|
||||||
|
"tools/net461/System.Runtime.Numerics.dll",
|
||||||
|
"tools/net461/System.Runtime.Serialization.Formatters.dll",
|
||||||
|
"tools/net461/System.Runtime.Serialization.Json.dll",
|
||||||
|
"tools/net461/System.Runtime.Serialization.Primitives.dll",
|
||||||
|
"tools/net461/System.Runtime.Serialization.Xml.dll",
|
||||||
|
"tools/net461/System.Runtime.dll",
|
||||||
|
"tools/net461/System.Security.Claims.dll",
|
||||||
|
"tools/net461/System.Security.Cryptography.Algorithms.dll",
|
||||||
|
"tools/net461/System.Security.Cryptography.Csp.dll",
|
||||||
|
"tools/net461/System.Security.Cryptography.Encoding.dll",
|
||||||
|
"tools/net461/System.Security.Cryptography.Primitives.dll",
|
||||||
|
"tools/net461/System.Security.Cryptography.X509Certificates.dll",
|
||||||
|
"tools/net461/System.Security.Principal.dll",
|
||||||
|
"tools/net461/System.Security.SecureString.dll",
|
||||||
|
"tools/net461/System.Text.Encoding.Extensions.dll",
|
||||||
|
"tools/net461/System.Text.Encoding.dll",
|
||||||
|
"tools/net461/System.Text.RegularExpressions.dll",
|
||||||
|
"tools/net461/System.Threading.Overlapped.dll",
|
||||||
|
"tools/net461/System.Threading.Tasks.Parallel.dll",
|
||||||
|
"tools/net461/System.Threading.Tasks.dll",
|
||||||
|
"tools/net461/System.Threading.Thread.dll",
|
||||||
|
"tools/net461/System.Threading.ThreadPool.dll",
|
||||||
|
"tools/net461/System.Threading.Timer.dll",
|
||||||
|
"tools/net461/System.Threading.dll",
|
||||||
|
"tools/net461/System.ValueTuple.dll",
|
||||||
|
"tools/net461/System.Xml.ReaderWriter.dll",
|
||||||
|
"tools/net461/System.Xml.XDocument.dll",
|
||||||
|
"tools/net461/System.Xml.XPath.XDocument.dll",
|
||||||
|
"tools/net461/System.Xml.XPath.dll",
|
||||||
|
"tools/net461/System.Xml.XmlDocument.dll",
|
||||||
|
"tools/net461/System.Xml.XmlSerializer.dll",
|
||||||
|
"tools/net461/netstandard.dll",
|
||||||
|
"tools/netcoreapp2.1/GetDocument.Insider.deps.json",
|
||||||
|
"tools/netcoreapp2.1/GetDocument.Insider.dll",
|
||||||
|
"tools/netcoreapp2.1/GetDocument.Insider.runtimeconfig.json",
|
||||||
|
"tools/netcoreapp2.1/System.Diagnostics.DiagnosticSource.dll"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Microsoft.OpenApi/1.6.14": {
|
||||||
|
"sha512": "tTaBT8qjk3xINfESyOPE2rIellPvB7qpVqiWiyA/lACVvz+xOGiXhFUfohcx82NLbi5avzLW0lx+s6oAqQijfw==",
|
||||||
|
"type": "package",
|
||||||
|
"path": "microsoft.openapi/1.6.14",
|
||||||
|
"files": [
|
||||||
|
".nupkg.metadata",
|
||||||
|
".signature.p7s",
|
||||||
|
"README.md",
|
||||||
|
"lib/netstandard2.0/Microsoft.OpenApi.dll",
|
||||||
|
"lib/netstandard2.0/Microsoft.OpenApi.pdb",
|
||||||
|
"lib/netstandard2.0/Microsoft.OpenApi.xml",
|
||||||
|
"microsoft.openapi.1.6.14.nupkg.sha512",
|
||||||
|
"microsoft.openapi.nuspec"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Swashbuckle.AspNetCore/6.9.0": {
|
||||||
|
"sha512": "lvI+XHF21tkwXd2nDCLGJsdhdUYsY3Ax2fWUlvw81Oa6EedtnIAf5tThy8ZnPcz/9/TwsLgjgtX9ifOCIjbEPA==",
|
||||||
|
"type": "package",
|
||||||
|
"path": "swashbuckle.aspnetcore/6.9.0",
|
||||||
|
"files": [
|
||||||
|
".nupkg.metadata",
|
||||||
|
".signature.p7s",
|
||||||
|
"build/Swashbuckle.AspNetCore.props",
|
||||||
|
"buildMultiTargeting/Swashbuckle.AspNetCore.props",
|
||||||
|
"swashbuckle.aspnetcore.6.9.0.nupkg.sha512",
|
||||||
|
"swashbuckle.aspnetcore.nuspec"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Swashbuckle.AspNetCore.Swagger/6.9.0": {
|
||||||
|
"sha512": "P316kpxx5DnDvJwNWW8iTAXkh9DVenAxFGe9v4OUS0gil+vitH7F1feXhCtVeHN/616EFNTMh4pV2lcr9kkw/w==",
|
||||||
|
"type": "package",
|
||||||
|
"path": "swashbuckle.aspnetcore.swagger/6.9.0",
|
||||||
|
"files": [
|
||||||
|
".nupkg.metadata",
|
||||||
|
".signature.p7s",
|
||||||
|
"lib/net5.0/Swashbuckle.AspNetCore.Swagger.dll",
|
||||||
|
"lib/net5.0/Swashbuckle.AspNetCore.Swagger.pdb",
|
||||||
|
"lib/net5.0/Swashbuckle.AspNetCore.Swagger.xml",
|
||||||
|
"lib/net6.0/Swashbuckle.AspNetCore.Swagger.dll",
|
||||||
|
"lib/net6.0/Swashbuckle.AspNetCore.Swagger.pdb",
|
||||||
|
"lib/net6.0/Swashbuckle.AspNetCore.Swagger.xml",
|
||||||
|
"lib/net7.0/Swashbuckle.AspNetCore.Swagger.dll",
|
||||||
|
"lib/net7.0/Swashbuckle.AspNetCore.Swagger.pdb",
|
||||||
|
"lib/net7.0/Swashbuckle.AspNetCore.Swagger.xml",
|
||||||
|
"lib/net8.0/Swashbuckle.AspNetCore.Swagger.dll",
|
||||||
|
"lib/net8.0/Swashbuckle.AspNetCore.Swagger.pdb",
|
||||||
|
"lib/net8.0/Swashbuckle.AspNetCore.Swagger.xml",
|
||||||
|
"lib/netcoreapp3.0/Swashbuckle.AspNetCore.Swagger.dll",
|
||||||
|
"lib/netcoreapp3.0/Swashbuckle.AspNetCore.Swagger.pdb",
|
||||||
|
"lib/netcoreapp3.0/Swashbuckle.AspNetCore.Swagger.xml",
|
||||||
|
"lib/netstandard2.0/Swashbuckle.AspNetCore.Swagger.dll",
|
||||||
|
"lib/netstandard2.0/Swashbuckle.AspNetCore.Swagger.pdb",
|
||||||
|
"lib/netstandard2.0/Swashbuckle.AspNetCore.Swagger.xml",
|
||||||
|
"package-readme.md",
|
||||||
|
"swashbuckle.aspnetcore.swagger.6.9.0.nupkg.sha512",
|
||||||
|
"swashbuckle.aspnetcore.swagger.nuspec"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Swashbuckle.AspNetCore.SwaggerGen/6.9.0": {
|
||||||
|
"sha512": "FjeMR3fBzwVc5plfYjoHw9ptf8SOWMupvO9X35J5EgzT3L9dRqSxa+cBKzL8PwCyemY0xNrggQSB5+MFWx1axg==",
|
||||||
|
"type": "package",
|
||||||
|
"path": "swashbuckle.aspnetcore.swaggergen/6.9.0",
|
||||||
|
"files": [
|
||||||
|
".nupkg.metadata",
|
||||||
|
".signature.p7s",
|
||||||
|
"lib/net5.0/Swashbuckle.AspNetCore.SwaggerGen.dll",
|
||||||
|
"lib/net5.0/Swashbuckle.AspNetCore.SwaggerGen.pdb",
|
||||||
|
"lib/net5.0/Swashbuckle.AspNetCore.SwaggerGen.xml",
|
||||||
|
"lib/net6.0/Swashbuckle.AspNetCore.SwaggerGen.dll",
|
||||||
|
"lib/net6.0/Swashbuckle.AspNetCore.SwaggerGen.pdb",
|
||||||
|
"lib/net6.0/Swashbuckle.AspNetCore.SwaggerGen.xml",
|
||||||
|
"lib/net7.0/Swashbuckle.AspNetCore.SwaggerGen.dll",
|
||||||
|
"lib/net7.0/Swashbuckle.AspNetCore.SwaggerGen.pdb",
|
||||||
|
"lib/net7.0/Swashbuckle.AspNetCore.SwaggerGen.xml",
|
||||||
|
"lib/net8.0/Swashbuckle.AspNetCore.SwaggerGen.dll",
|
||||||
|
"lib/net8.0/Swashbuckle.AspNetCore.SwaggerGen.pdb",
|
||||||
|
"lib/net8.0/Swashbuckle.AspNetCore.SwaggerGen.xml",
|
||||||
|
"lib/netcoreapp3.0/Swashbuckle.AspNetCore.SwaggerGen.dll",
|
||||||
|
"lib/netcoreapp3.0/Swashbuckle.AspNetCore.SwaggerGen.pdb",
|
||||||
|
"lib/netcoreapp3.0/Swashbuckle.AspNetCore.SwaggerGen.xml",
|
||||||
|
"lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerGen.dll",
|
||||||
|
"lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerGen.pdb",
|
||||||
|
"lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerGen.xml",
|
||||||
|
"package-readme.md",
|
||||||
|
"swashbuckle.aspnetcore.swaggergen.6.9.0.nupkg.sha512",
|
||||||
|
"swashbuckle.aspnetcore.swaggergen.nuspec"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Swashbuckle.AspNetCore.SwaggerUI/6.9.0": {
|
||||||
|
"sha512": "0OxlWBFLl2gUESZX/K7QCTz9KctKy0VxHTvLIBcyWGD4z/fv5MCMW02qzYGcReLJr4yBnNDRzApKtLh6oBpe9A==",
|
||||||
|
"type": "package",
|
||||||
|
"path": "swashbuckle.aspnetcore.swaggerui/6.9.0",
|
||||||
|
"files": [
|
||||||
|
".nupkg.metadata",
|
||||||
|
".signature.p7s",
|
||||||
|
"lib/net5.0/Swashbuckle.AspNetCore.SwaggerUI.dll",
|
||||||
|
"lib/net5.0/Swashbuckle.AspNetCore.SwaggerUI.pdb",
|
||||||
|
"lib/net5.0/Swashbuckle.AspNetCore.SwaggerUI.xml",
|
||||||
|
"lib/net6.0/Swashbuckle.AspNetCore.SwaggerUI.dll",
|
||||||
|
"lib/net6.0/Swashbuckle.AspNetCore.SwaggerUI.pdb",
|
||||||
|
"lib/net6.0/Swashbuckle.AspNetCore.SwaggerUI.xml",
|
||||||
|
"lib/net7.0/Swashbuckle.AspNetCore.SwaggerUI.dll",
|
||||||
|
"lib/net7.0/Swashbuckle.AspNetCore.SwaggerUI.pdb",
|
||||||
|
"lib/net7.0/Swashbuckle.AspNetCore.SwaggerUI.xml",
|
||||||
|
"lib/net8.0/Swashbuckle.AspNetCore.SwaggerUI.dll",
|
||||||
|
"lib/net8.0/Swashbuckle.AspNetCore.SwaggerUI.pdb",
|
||||||
|
"lib/net8.0/Swashbuckle.AspNetCore.SwaggerUI.xml",
|
||||||
|
"lib/netcoreapp3.0/Swashbuckle.AspNetCore.SwaggerUI.dll",
|
||||||
|
"lib/netcoreapp3.0/Swashbuckle.AspNetCore.SwaggerUI.pdb",
|
||||||
|
"lib/netcoreapp3.0/Swashbuckle.AspNetCore.SwaggerUI.xml",
|
||||||
|
"lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerUI.dll",
|
||||||
|
"lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerUI.pdb",
|
||||||
|
"lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerUI.xml",
|
||||||
|
"package-readme.md",
|
||||||
|
"swashbuckle.aspnetcore.swaggerui.6.9.0.nupkg.sha512",
|
||||||
|
"swashbuckle.aspnetcore.swaggerui.nuspec"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"projectFileDependencyGroups": {
|
||||||
|
"net8.0": [
|
||||||
|
"Swashbuckle.AspNetCore >= 6.9.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"packageFolders": {
|
||||||
|
"C:\\Users\\GamingPC\\.nuget\\packages\\": {},
|
||||||
|
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {}
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"restore": {
|
||||||
|
"projectUniqueName": "d:\\GitHubRepository\\Hracicky\\x\\csob\\Csob.csproj",
|
||||||
|
"projectName": "Csob",
|
||||||
|
"projectPath": "d:\\GitHubRepository\\Hracicky\\x\\csob\\Csob.csproj",
|
||||||
|
"packagesPath": "C:\\Users\\GamingPC\\.nuget\\packages\\",
|
||||||
|
"outputPath": "d:\\GitHubRepository\\Hracicky\\x\\csob\\obj\\",
|
||||||
|
"projectStyle": "PackageReference",
|
||||||
|
"fallbackFolders": [
|
||||||
|
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
|
||||||
|
],
|
||||||
|
"configFilePaths": [
|
||||||
|
"C:\\Users\\GamingPC\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||||
|
],
|
||||||
|
"originalTargetFrameworks": [
|
||||||
|
"net8.0"
|
||||||
|
],
|
||||||
|
"sources": {
|
||||||
|
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||||
|
"C:\\Program Files\\dotnet\\library-packs": {},
|
||||||
|
"D:\\CustomNuGetPackages": {},
|
||||||
|
"https://api.nuget.org/v3/index.json": {}
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net8.0": {
|
||||||
|
"targetAlias": "net8.0",
|
||||||
|
"projectReferences": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warningProperties": {
|
||||||
|
"warnAsError": [
|
||||||
|
"NU1605"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"restoreAuditProperties": {
|
||||||
|
"enableAudit": "true",
|
||||||
|
"auditLevel": "low",
|
||||||
|
"auditMode": "direct"
|
||||||
|
},
|
||||||
|
"SdkAnalysisLevel": "10.0.200"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net8.0": {
|
||||||
|
"targetAlias": "net8.0",
|
||||||
|
"dependencies": {
|
||||||
|
"Swashbuckle.AspNetCore": {
|
||||||
|
"target": "Package",
|
||||||
|
"version": "[6.9.0, )"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"imports": [
|
||||||
|
"net461",
|
||||||
|
"net462",
|
||||||
|
"net47",
|
||||||
|
"net471",
|
||||||
|
"net472",
|
||||||
|
"net48",
|
||||||
|
"net481"
|
||||||
|
],
|
||||||
|
"assetTargetFallback": true,
|
||||||
|
"warn": true,
|
||||||
|
"frameworkReferences": {
|
||||||
|
"Microsoft.AspNetCore.App": {
|
||||||
|
"privateAssets": "none"
|
||||||
|
},
|
||||||
|
"Microsoft.NETCore.App": {
|
||||||
|
"privateAssets": "all"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.204/PortableRuntimeIdentifierGraph.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"dgSpecHash": "oxCvoNmhuPk=",
|
||||||
|
"success": true,
|
||||||
|
"projectFilePath": "d:\\GitHubRepository\\Hracicky\\x\\csob\\Csob.csproj",
|
||||||
|
"expectedPackageFiles": [
|
||||||
|
"C:\\Users\\GamingPC\\.nuget\\packages\\microsoft.extensions.apidescription.server\\6.0.5\\microsoft.extensions.apidescription.server.6.0.5.nupkg.sha512",
|
||||||
|
"C:\\Users\\GamingPC\\.nuget\\packages\\microsoft.openapi\\1.6.14\\microsoft.openapi.1.6.14.nupkg.sha512",
|
||||||
|
"C:\\Users\\GamingPC\\.nuget\\packages\\swashbuckle.aspnetcore\\6.9.0\\swashbuckle.aspnetcore.6.9.0.nupkg.sha512",
|
||||||
|
"C:\\Users\\GamingPC\\.nuget\\packages\\swashbuckle.aspnetcore.swagger\\6.9.0\\swashbuckle.aspnetcore.swagger.6.9.0.nupkg.sha512",
|
||||||
|
"C:\\Users\\GamingPC\\.nuget\\packages\\swashbuckle.aspnetcore.swaggergen\\6.9.0\\swashbuckle.aspnetcore.swaggergen.6.9.0.nupkg.sha512",
|
||||||
|
"C:\\Users\\GamingPC\\.nuget\\packages\\swashbuckle.aspnetcore.swaggerui\\6.9.0\\swashbuckle.aspnetcore.swaggerui.6.9.0.nupkg.sha512"
|
||||||
|
],
|
||||||
|
"logs": []
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user