.NET SDK

Anis.Partners.Sdk — for .NET 8 and .NET 10.

dotnet add package Anis.Partners.Sdk

Register it

builder.Services
    .AddAnisPartners(builder.Configuration.GetSection("AnisPartners"))
    .WithSigner(EcdsaP256Signer.FromPemFile("/secure/partner-key.pem", keyId));
{
  "AnisPartners": {
    "Authority": "https://<the address Anis gave you>",
    "SignatureLifetime": "00:01:00",
    "AcceptLanguage": "English",
    "SigningKeyCacheDuration": "00:10:00",
    "Timeout": "00:00:30"
  }
}

Then take IAnisPartnersClient from the container. The settings are checked when your application starts: a missing address, or a signature lifetime outside 1 to 60 seconds, stops it there.

The signer is a separate step on purpose — there is no safe default for where your private key lives. EcdsaP256Signer signs with a key file; for a vault or hardware module, implement IRequestSigner — your key id, and a method that signs bytes and returns the 64-byte signature — and the key never enters your process.

Use it

var profile = await anis.Profile.GetAsync(ct);

await foreach (var wallet in anis.Wallets.ListAsync(ct))       // pages followed for you
    Console.WriteLine($"{wallet.Name} {wallet.Balance}");

var cards = await anis.Catalogue.ListCardsAsync(walletId, subcategoryId, cancellationToken: ct);

var credential = await anis.OwnedCards.RevealAsync(walletId, soldCardId, ct);

Orders

var operationId = Guid.NewGuid();                  // yours: store it with the request BEFORE sending

var outcome = await anis.Orders.CreateAsync(walletId, operationId, new CreateOrderRequest
{
    CardId            = card.Id,
    Quantity          = 2,
    ExpectedUnitPrice = card.UnitPrice!.Value,
    ExpectedTotal     = card.UnitPrice!.Value.Multiply(2),   // exact decimal
}, ct);

switch (outcome)
{
    case OrderCompleted completed:   await vault.StoreAsync(completed.Credentials, ct); break;  // store FIRST
    case OrderProcessing processing: await ResumeAfterAsync(operationId, processing.RetryAfter); break;
    case OrderReplayed replayed:     await EnsureStoredAsync(replayed.Order, ct); break;
        // sent earlier: if you never stored them (the first answer was lost), reveal by replayed.Order.InvoiceId
}

ResumeAsync sends the same order again under the same id — the way to recover. See Orders and recovery for the full rules and the error handling.

Refusals

Every refusal is an AnisApiException (or a subclass for the cases you handle differently) carrying Code, RequestId, RetryAfter, IsReplayed and — on an order — OrderOutcome: NotPlaced or Unknown. Each error page names its exception.

Three other exceptions mean something different:

Exception Means On an order
UnverifiableResponseException An answer could not be shown to come from Anis; it was thrown away Resume with the same id
RequestSigningException Your signer failed; nothing was sent Send it again once the signer works
TaskCanceledException, HttpRequestException Timed out, or the connection failed Resume with the same id

Several Anis applications in one host

builder.Services.AddAnisPartners("brand-a", config.GetSection("AnisPartners:BrandA")).WithSigner(signerA);
builder.Services.AddAnisPartners("brand-b", config.GetSection("AnisPartners:BrandB")).WithSigner(signerB);

var brandA = factory.GetClient("brand-a");   // IAnisPartnersClientFactory, or [FromKeyedServices("brand-a")]

Each application has its own pipeline and key, and its traces and metrics carry anis.client.

No automatic retries on this client

A retry policy added to every HTTP client (for example the .NET Aspire service defaults) gives up on each attempt after 10 seconds by default, sooner than the SDK’s 30. A slow order can then complete at Anis while its answer — with the card codes — is thrown away. Retry reads yourself if you like; recover orders with ResumeAsync.

Observability

Traces and metrics under Anis.Partners.Sdk; logs through your own logging. See Observability for the metric names and what to alert on.

If your host replaces .NET’s built-in container

The SDK uses .NET 8 keyed services. A third-party container must support them — Autofac 9 or later, Lamar 12.1 or later, and SimpleInjector all do. Otherwise build AnisPartnersClient directly with an HttpClient that carries a PartnerVerifyingHandler around a PartnerSigningHandler.