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