SDK de .NET
N1co.EPay llama al API de ePay desde tu servidor .NET, con inyección de dependencias, excepciones con código y verificación de webhooks.
- Instala
N1co.EPayen tu servidor (net8.0onet10.0). - Registra el cliente con
AddN1coEPayy tu llave secreta. - Crea intents con
PaymentIntents.CreateAsyncy verifica webhooks conN1coWebhook.ConstructEvent.
dotnet add package N1co.EPayUna minimal API completa: crea el intent y recibe el webhook.
using N1co.EPay;
var builder = WebApplication.CreateBuilder(args);
// Sección "N1co": 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) =>
{
// El monto sale de tu pedido, nunca del navegador.
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); // los bytes crudos, tal como llegaron
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)
{
// Entrega el pedido de intent.Id; descarta los evt.Id repetidos.
}
return Results.NoContent();
});
app.Run();AddN1coEPay registra N1coClient como singleton y valida las opciones al arrancar. Sin DI, crea un solo new N1coClient(secretKey, options) y reúsalo. La página confirma el pago como en Cobrar.
Errores#
Toda falla del API, de red o de firma es una N1coException con Code, StatusCode, GetDetail("field") y RequestId. Decide con Code, nunca con 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);
}Los códigos están en Errores. Cancelar con tu CancellationToken lanza OperationCanceledException, no una excepción del SDK.
Lo que suele fallar#
- Leer el cuerpo como JSON antes de verificar. Verifica los bytes crudos; un modelo deserializado y vuelto a serializar no coincide con la firma.
- La llave secreta en Blazor WebAssembly. La llave vive solo en el servidor: tu app WebAssembly llama a tu backend, nunca al API. Un componente de Blazor Server sí puede inyectar
N1coClient. - Esperar reintentos de un handler de resiliencia. El API no tiene claves de idempotencia, así que el SDK quita los handlers de reintento de su cliente, también los de los service defaults de .NET Aspire. Tras una
ConnectionExceptionen un POST, lee el objeto o espera su webhook. - Omitir
BaseUrl. Sin él, el cliente usa el host de producción, que llega con el lanzamiento a producción.
Las opciones del cliente, el uso con tu propio HttpClient, los modelos y los reintentos de GET están en el README del paquete.