.NET SDK
N1co.EPay calls the ePay API from your .NET server, with dependency injection, coded exceptions and webhook verification.
- Install
N1co.EPayon your server (net8.0ornet10.0). - Register the client with
AddN1coEPayand your secret key. - Create intents with
PaymentIntents.CreateAsyncand verify webhooks withN1coWebhook.ConstructEvent.
dotnet add package N1co.EPayA complete minimal API: it creates the intent and receives the webhook.
using N1co.EPay;
var builder = WebApplication.CreateBuilder(args);
// "N1co" section: SecretKey, BaseUrl, Timeout, MaxRetries, AppInfo.
builder.Services.AddN1coEPay(o =>
{
o.SecretKey = builder.Configuration["N1co:SecretKey"]!;
o.BaseUrl = N1coClient.DevBaseUrl; // https://api-epay.dev.n1co.dev
});
var webhookSecret = builder.Configuration["N1co:WebhookSecret"]!; // whsec_…
var app = builder.Build();
app.MapPost("/checkout/payment-intent", async (N1coClient n1co) =>
{
// The amount comes from your order, never from the browser.
var intent = await n1co.PaymentIntents.CreateAsync(new()
{
Amount = 1250,
Currency = "USD",
Customer = new CustomerInput { Id = "cust-1001", Name = "Ana López", Email = "ana.lopez@example.com", Phone = "+503 7000-1234" },
Reference = "order-8812",
});
return Results.Ok(new { client_secret = intent.ClientSecret });
});
app.MapPost("/webhooks/n1co", async (HttpRequest request) =>
{
using var body = new MemoryStream();
await request.Body.CopyToAsync(body); // the raw bytes, exactly as sent
Event evt;
try
{
evt = N1coWebhook.ConstructEvent(body.ToArray(), request.Headers, webhookSecret);
}
catch (WebhookSignatureException)
{
return Results.BadRequest();
}
if (evt.Type == EventType.PaymentIntentSucceeded && evt.Data.Object is PaymentIntentEventObject intent)
{
// Fulfill the order for intent.Id; skip repeated evt.Id values.
}
return Results.NoContent();
});
app.Run();AddN1coEPay registers N1coClient as a singleton and validates the options at startup. Without DI, create a single new N1coClient(secretKey, options) and reuse it. The page confirms the payment as in Charge.
Errors#
Every API, network or signature failure is an N1coException with Code, StatusCode, GetDetail("field") and RequestId. Branch on Code, never on Message.
try
{
await n1co.PaymentIntents.CreateAsync(parameters);
}
catch (InvalidRequestException e)
{
logger.LogWarning("{Field} {Code}", e.GetDetail("field"), e.GetDetail("code"));
}
catch (N1coException e)
{
logger.LogError("{Code} {Status} {RequestId}", e.Code, e.StatusCode, e.RequestId);
}The codes are in Errors. Canceling through your CancellationToken throws OperationCanceledException, not an SDK exception.
Common mistakes#
- Reading the body as JSON before verifying. Verify the raw bytes; a deserialized and reserialized model does not match the signature.
- The secret key in Blazor WebAssembly. The key lives only on the server: your WebAssembly app calls your backend, never the API. A Blazor Server component may inject
N1coClient. - Expecting retries from a resilience handler. The API has no idempotency keys, so the SDK strips retry handlers from its client, including those from .NET Aspire service defaults. After a
ConnectionExceptionon a POST, read the object or wait for its webhook. - Leaving out
BaseUrl. Without it, the client uses the production host, which arrives with the production release.
The client options, using your own HttpClient, the models and GET retries are in the package README.