StampMitraDevelopers
आधिकारिक SDK

.NET SDK

StampMitra Developer API का आधिकारिक .NET क्लाइंट: typed records, IAsyncEnumerable pagination, अपने आप retries, signed-webhook helpers और ASP.NET Core dependency injection के साथ async-first identity verification और e-Stamp।

Pre-release 0.1.0 - package registry पर प्रकाशन जारी है

Package
StampMitra
संस्करण
0.1.0
आवश्यकता
net6.0 या net8.0 (और इसलिए हर बाद का runtime)। netstandard2.1 या .NET Framework build नहीं है। Core package सिर्फ़ System.Net.Http.HttpClient और System.Text.Json इस्तेमाल करता है, कोई third-party runtime dependency नहीं। C# 9 या नया सुझाया जाता है (records और init-only properties)। .NET 6 का support समाप्त हो चुका है और मौजूदा applications के लिए target के रूप में रखा गया है।
लाइसेंस
MIT
Changelog
CHANGELOG.md
इंस्टॉल

Package इंस्टॉल करें

.NET CLI
dotnet add package StampMitra
# optional: services.AddStampMitra(...) for ASP.NET Core / generic host
dotnet add package StampMitra.Extensions.DependencyInjection

प्रकाशित होने के बाद उपलब्ध। अभी NuGet पर प्रकाशित नहीं हुआ है। यह कमांड package के रिलीज़ होने के बाद काम करेगा; तब तक रिलीज़ के लिए GitHub repository देखते रहें।

Quickstart

पाँच पंक्तियों में PAN verify करें

बनाते समय sandbox key (sk_test_...) इस्तेमाल करें। यह मुफ़्त है, आपके वॉलेट से कुछ नहीं कटता और sandbox डेटा पर चलती है।

C#
using StampMitra;
using StampMitra.Models;

using var client = new StampMitraClient();   // reads STAMPMITRA_API_KEY
var pan = await client.Verify.PanAsync(new PanVerifyRequest { Pan = "ABCDE1234F", Name = "Rahul Kumar" }, ct);
Console.WriteLine($"{(pan.Valid == true ? "valid" : "invalid")} {pan.NameMatchResult} {pan.ReferenceId}");
Authentication

API keys और environments

Sandbox

Sandbox keys sk_test_ से शुरू होती हैं। ये मुफ़्त हैं और sandbox डेटा लौटाती हैं।

Live

Live keys sk_live_ से शुरू होती हैं। इनके लिए स्वीकृत production access request चाहिए और हर सफल कॉल पर शुल्क लगता है।

StampMitraClientOptions.ApiKey न दें तो क्लाइंट STAMPMITRA_API_KEY पढ़ता है। जो key ^sk_(test|live)_[A-Za-z0-9_-]{8,}$ से मेल नहीं खाती वह अस्वीकार होती है, जब तक AllowCustomKey सेट न हो। client.Environment का मान Test, Live या Custom होता है। BaseUrl https होना चाहिए (loopback http सिर्फ़ mock servers के लिए) और URL में credentials नहीं होने चाहिए। API key ToString(), exceptions या logs में कभी नहीं आती। उदाहरणों में ct एक CancellationToken है; cancellation न चाहिए तो CancellationToken.None दें।

सिर्फ़ server-side। sk_ keys को कभी browser या mobile ऐप में न डालें और न ही किसी repository में commit करें। इन्हें environment variables या secrets manager में रखें। Authentication
Client options
using var client = new StampMitraClient(new StampMitraClientOptions
{
    ApiKey = Environment.GetEnvironmentVariable("STAMPMITRA_API_KEY"),
    BaseUrl = "https://api-b2b.stampmitra.in",
    Timeout = TimeSpan.FromSeconds(20),   // per attempt, default 30 s
    MaxRetries = 3,                       // default 2, range 0 to 10
    AppInfo = new AppInfo("my-app", "1.4.0", "https://example.com"),
    DebugLog = line => Console.Error.WriteLine(line),
    ValidateInput = true,
    AllowCustomKey = false,
});

// Also available: HttpClient (yours is never disposed), DefaultHeaders.
// Every method also takes a per-call RequestOptions (Timeout, MaxRetries, IdempotencyKey, Headers) and a CancellationToken.
Resources

हर resource एक जगह

Responses immutable records हैं (pan.RegisteredName)। जो fields SDK नहीं जानता वे AdditionalProperties में रखे जाते हैं (नाम से JsonElement), और HTTP metadata (status, headers, request id, attempts) model.Response पर है। जिन responses का आकार API ने तय नहीं किया (e-Stamp reference data, duty, parties, webhook events) वे GenericResponse हैं (Data, Get(name), Items, NextCursor, RawJson), और GET /v1/services एक सादा array लौटाता है जो ServiceList के रूप में मिलता है।

All resources
using StampMitra.Models;      // models and request types
using StampMitra.Resources;   // LogsListParams

// Verification: every call is billed, including valid = false results
var pan = await client.Verify.PanAsync(new PanVerifyRequest { Pan = "ABCDE1234F", Name = "Rahul Kumar" }, ct);
var gst = await client.Verify.GstinAsync(new GstinVerifyRequest { Gstin = "27ABCDE1234F1Z5" }, ct);
var rc = await client.Verify.RcAsync(new VehicleRcVerifyRequest { VehicleNumber = "MH12AB1234" }, ct);
var bank = await client.Verify.BankAccountAsync(new BankAccountVerifyRequest { AccountNumber = "000123456789", Ifsc = "ICIC0001234", Name = "Rahul Kumar" }, ct);
// Also: VoterIdAsync, DrivingLicenseAsync* , PassportAsync* , CinAsync, UdyamAsync, PanToGstinAsync, PanToUdyamAsync
// * needs an IP-allowlisted key; otherwise PermissionDeniedException carries the server message

// Services and e-Stamp reference data
ServiceList services = await client.Services.ListAsync(ct);
GenericResponse states = await client.Estamp.ListStatesAsync(ct);
GenericResponse duty = await client.Estamp.CalculateDutyAsync(new EstampDutyCalculateRequest { StateCode = "MH", ArticleCode = "5", StampDutyAmount = 500 }, ct);
// Also: Services.GetAsync, Estamp.GetSchemaAsync, ListInstrumentsAsync(stateCode), ListArticlesAsync(instrumentCode), AddPartiesAsync(orderId, ...)

// Orders and payments (addressed by the externalOrderId you supplied)
var order = await client.Orders.CreateAsync(
    new CreateOrderRequest { ExternalOrderId = "o1", ExternalCustomerId = "c1", ServiceCode = "estamp.instrument.rent_agreement" },
    new RequestOptions { IdempotencyKey = "order-o1", Timeout = TimeSpan.FromSeconds(10) },
    ct);
OrderStatusResponse status = await client.Orders.GetStatusAsync(order.ExternalOrderId!, ct);
// Also: Orders.GetAsync, GetPaymentAsync, ListDocumentsAsync, GetDocumentDownloadAsync(id, documentId), Payments.Intents.CreateAsync / GetAsync

// Usage, logs, webhook deliveries
UsageSummary usage = await client.Usage.SummaryAsync(ct);
LogsPage logs = await client.Logs.ListAsync(new LogsListParams { Status = "failure", Limit = 50 }, ct);
GenericResponse events = await client.Webhooks.Events.ListAsync(20, null, null, ct);
// Also: Webhooks.Events.ReplayAsync
Pagination

Pagination

ListAllAsync एक IAsyncEnumerable<LogEntry> लौटाता है जो nextCursor को lazily आगे बढ़ाता है; ListAsync Items और NextCursor वाला एक पेज लौटाता है।

C#
await foreach (var entry in client.Logs.ListAllAsync(new LogsListParams { Service = "pan.verify", Limit = 100 }, null, ct))
{
    Console.WriteLine($"{entry.Id} {entry.CreatedAt}");   // follows nextCursor lazily
}

LogsPage page = await client.Logs.ListAsync(new LogsListParams { Status = "failure", Limit = 50 }, ct);   // page.Items, page.NextCursor
Errors

Error classes

सभी errors StampMitraException से बने हैं, जो Status, Code, Message, RequestId, Field और Body देता है। Header मौजूद हो तो RateLimitException में RetryAfter जुड़ता है। कोई भी दूसरा status base StampMitraException उठाता है, और non-JSON error body पर भी status से सही class मिलती है। कॉलर का cancellation OperationCanceledException के रूप में आता है, कभी wrap नहीं होता।

HTTP statusClassटिप्पणी
400 / local validationValidationExceptionगलत इनपुट। Validation helpers इसे request भेजने से पहले local रूप से भी उठाते हैं।
401AuthenticationExceptionAPI key न मिली, गलत है या रद्द हो चुकी है।
402PaymentRequiredExceptionवॉलेट में बैलेंस या क्रेडिट कम है।
403PermissionDeniedExceptionसर्वर का संदेश जस का तस दिया जाता है, जैसे Passport और Driving Licence के लिए IP-allowlisted key चाहिए।
404NotFoundExceptionऑर्डर, सेवा या इवेंट की id मौजूद नहीं है।
409ConflictExceptionIdempotency-Key किसी दूसरी body के साथ दोबारा इस्तेमाल हुई, या state में टकराव है।
429RateLimitExceptionरुककर दोबारा कोशिश करें; सर्वर Retry-After भेजे तो वह मान उपलब्ध रहता है।
5xxUpstreamExceptionसर्वर या upstream की ओर की विफलता। 502, 503 और 504 पर अपने आप retry होता है।
network / timeoutApiConnectionException / ApiTimeoutExceptionकोई उपयोगी response नहीं मिला: कनेक्शन विफल, timeout या cancel।
webhook verificationSignatureVerificationExceptionगलत या पुराना webhook signature, खाली secret, या गलत timestamp। डिलीवरी को 400 से अस्वीकार करें।
C#
try
{
    await client.Verify.GstinAsync(new GstinVerifyRequest { Gstin = "27ABCDE1234F1Z5" }, ct);
}
catch (RateLimitException ex)
{
    Console.WriteLine($"back off {ex.RetryAfter}");
}
catch (StampMitraException ex)
{
    Console.Error.WriteLine($"{ex.RequestId}: {ex.Message}");
}
Retries और idempotency

Retries और idempotency

Validation

क्लाइंट-साइड validation

भेजने से पहले इनपुट जाँचे जाते हैं (PAN, GSTIN, IFSC, CIN, Udyam, vehicle number, 9 से 18 अंकों का account number, passport file number, driving licence, voter ID, phone, ISO तारीखें), जहाँ API करती है वहाँ trim और upper-case होकर। विफलता पर local ValidationException उठता है (Field सेट होता है), बिना network call और बिना शुल्क के। MH-12 AB 1234 जैसे separators हटाए नहीं जाते, ठीक API की तरह। Validators सार्वजनिक हैं (Validators.Pan(...)) और ValidateInput = false से बंद किए जा सकते हैं।

Webhooks

Webhook signatures जाँचें

हर डिलीवरी में X-Webhook-Signature, X-Webhook-Timestamp (Unix सेकंड) और X-Webhook-Event-Id होते हैं। Signature, timestamp, एक बिंदु और raw body का hex HMAC-SHA256 है, जो आपके endpoint secret से बनता है। जाँच हमेशा raw body पर करें, दोबारा serialise किए गए JSON पर नहीं।

WebhookSignature.ConstructEvent आठों events में से हर एक के लिए typed record लौटाता है (Type दस्तावेज़ित नाम है, जैसे order.created) या UnknownWebhookEvent; अतिरिक्त fields AdditionalProperties में रखे जाते हैं। तुलना constant-time में होती है, अभी से 300 s (बदला जा सकता है) से ज़्यादा दूर के timestamps अस्वीकार होते हैं, खाली secret अस्वीकार होता है, और विफलता पर SignatureVerificationException उठता है (Code: SIGNATURE_MISMATCH, INVALID_TIMESTAMP, TIMESTAMP_OUTSIDE_TOLERANCE, INVALID_PAYLOAD, MISSING_HEADERS या MISSING_SECRET)। WebhookSignature.Verify bool लौटाता है, और ConstructEventFromHeaders तीनों headers एक lookup से पढ़ता है।

C#
using StampMitra.Webhooks;

// rawBody: the request body bytes, untouched (never parse and re-serialise)
WebhookEvent evt = WebhookSignature.ConstructEvent(rawBody, timestamp, signature, secret, eventId: eventId);

switch (evt)
{
    case OrderPaymentConfirmedEvent paid:
        Console.WriteLine($"paid: {paid.CoreOrderId}");
        break;
    case OrderDocumentReadyEvent ready:
        Console.WriteLine($"document ready: {ready.CoreOrderId}");
        break;
}

bool authentic = WebhookSignature.Verify(rawBody, timestamp, signature, secret, toleranceSeconds: 300);

प्लेटफ़ॉर्म डिलीवरी retry करता है, इसलिए कोई भी काम करने से पहले EventId (X-Webhook-Event-Id से) पर duplicates हटाएँ।

Framework के उदाहरण

Framework के उदाहरण
using StampMitra;
using StampMitra.Webhooks;

var secret = Environment.GetEnvironmentVariable("STAMPMITRA_WEBHOOK_SECRET");
if (string.IsNullOrEmpty(secret))
{
    Console.Error.WriteLine("Set STAMPMITRA_WEBHOOK_SECRET to the endpoint's signing secret.");
    return 1;
}

var app = WebApplication.CreateBuilder(args).Build();

app.MapPost("/webhooks/stampmitra", async (HttpRequest request, ILogger<Program> logger) =>
{
    // The signature covers the raw request bytes: read the body untouched, never re-serialise it.
    using var ms = new MemoryStream();
    await request.Body.CopyToAsync(ms);
    var raw = ms.ToArray();

    WebhookEvent evt;
    try
    {
        evt = WebhookSignature.ConstructEventFromHeaders(raw, name => request.Headers[name].FirstOrDefault(), secret);
    }
    catch (SignatureVerificationException ex)
    {
        logger.LogWarning("Rejected webhook: {Code}", ex.Code);
        return Results.BadRequest(new { error = "invalid signature" });
    }

    // Deliveries are retried: de-duplicate on evt.EventId before doing any work.
    switch (evt)
    {
        case OrderPaymentConfirmedEvent paid:
            logger.LogInformation("Payment confirmed for order {Order}", paid.CoreOrderId);
            break;
        case OrderDocumentReadyEvent ready:
            logger.LogInformation("Document ready for order {Order}", ready.CoreOrderId);
            break;
        default:
            logger.LogInformation("{Type} for order {Order} (event {EventId})", evt.Type ?? evt.CoreEventType, evt.CoreOrderId, evt.EventId);
            break;
    }

    return Results.Ok(new { received = true });
});

await app.RunAsync();
return 0;

// Makes ILogger<Program> resolvable in a top-level-statements program.
public partial class Program
{
}

Webhooks: इवेंट के प्रकार, payload और retry नीति Webhooks पेज पर हैं।

सुरक्षित logging

सुरक्षित logging

डिफ़ॉल्ट रूप से कुछ भी log नहीं होता। हर request और response की एक पंक्ति पाने के लिए DebugLog (एक Action<string>) सेट करें। Authorization और x-api-key redact रहते हैं, identifiers (PAN, GSTIN, account number आदि) आख़िरी चार अक्षरों तक masked रहते हैं और नाम व जन्मतिथि हटा दी जाती हैं; वही masking कहीं और चाहिए तो Redactor सार्वजनिक है। DI package के साथ ये पंक्तियाँ ILogger पर Debug level में जाती हैं। फिर भी logs को संवेदनशील मानें।

SDKs

इंटीग्रेट करने के दूसरे तरीके

REST API किसी भी भाषा से चलती है, और बाकी SDKs भी उसी contract पर बने हैं।

API कॉल करने के लिए तैयार हैं?

मुफ़्त sandbox key लें और ऊपर का quickstart लगभग 5 मिनट में चलाएँ।