PHP SDK
StampMitra Developer API का आधिकारिक PHP क्लाइंट: typed immutable models, PSR-18 transport और Laravel व सादे PHP के लिए signed-webhook helpers के साथ identity verification और e-Stamp।
Pre-release 0.1.0 - package registry पर प्रकाशन जारी है
- Package
stampmitra/stampmitra-php- संस्करण
- 0.1.0
- आवश्यकता
- PHP 8.1 या उससे नया। SDK PSR-18 और PSR-17 से बात करता है, इसलिए कोई भी संगत HTTP क्लाइंट चलता है: Guzzle अपने आप मिल जाता है, या अपना httpClient दें।
- लाइसेंस
- MIT
- Changelog
- CHANGELOG.md
- Typed, immutable response models; जो fields SDK model नहीं करता उन्हें raw() और get() से पाएँ।
- अपना PSR-18 HTTP क्लाइंट और कोई भी PSR-3 logger इस्तेमाल करें।
- Jitter वाले backoff के साथ retries, और हर उस POST पर अपने आप Idempotency-Key जिसके लिए API यह header दस्तावेज़ित करती है।
- Laravel और सादे PHP के उदाहरणों वाले webhook helpers; logs का pagination lazy Generator से।
Package इंस्टॉल करें
composer require stampmitra/stampmitra-php guzzlehttp/guzzleप्रकाशित होने के बाद उपलब्ध। अभी Packagist पर प्रकाशित नहीं हुआ है। यह कमांड package के रिलीज़ होने के बाद काम करेगा; तब तक रिलीज़ के लिए GitHub repository देखते रहें।
composer config repositories.stampmitra vcs https://github.com/Bani-Global-Industries-LLP/stampmitra-developer-php
composer require stampmitra/stampmitra-php:dev-main guzzlehttp/guzzleतब तक GitHub से इंस्टॉल करें: composer.json repository के root में है और package का नाम stampmitra/stampmitra-php है, इसलिए Composer इसे VCS repository के रूप में पढ़ सकता है।
पाँच पंक्तियों में PAN verify करें
बनाते समय sandbox key (sk_test_...) इस्तेमाल करें। यह मुफ़्त है, आपके वॉलेट से कुछ नहीं कटता और sandbox डेटा पर चलती है।
use StampMitra\StampMitra;
$client = new StampMitra(apiKey: getenv('STAMPMITRA_API_KEY') ?: null); // or just new StampMitra()
$pan = $client->verify->pan('ABCDE1234F', 'Rahul Kumar');
echo $pan->valid ? 'valid' : 'invalid', ' ', $pan->nameMatchResult, ' ', $pan->referenceId, PHP_EOL;API keys और environments
Sandbox
Sandbox keys sk_test_ से शुरू होती हैं। ये मुफ़्त हैं और sandbox डेटा लौटाती हैं।
Live
Live keys sk_live_ से शुरू होती हैं। इनके लिए स्वीकृत production access request चाहिए और हर सफल कॉल पर शुल्क लगता है।
Key STAMPMITRA_API_KEY environment variable से पढ़ी जाती है, जब तक आप apiKey: न दें। $client->environment का मान 'test' या 'live' होता है। जो key ^sk_(test|live)_[A-Za-z0-9_-]{8,}$ से मेल नहीं खाती वह अस्वीकार होती है, जब तक आप allowCustomKey: true न दें।
$client = new StampMitra(
apiKey: 'sk_test_...',
baseUrl: 'https://api-b2b.stampmitra.in',
timeout: 30,
maxRetries: 2,
httpClient: null, // any PSR-18 client; Guzzle is auto-discovered
defaultHeaders: [],
appInfo: ['name' => 'my-app', 'version' => '1.0.0', 'url' => 'https://example.com'],
logger: null, // any PSR-3 logger
validateInput: true,
allowCustomKey: false,
);
// Every method also takes per-request options (a RequestOptions or an array):
$client->orders->create($body, ['idempotencyKey' => 'order-123']);हर resource एक जगह
Responses typed, immutable models हैं ($pan->registeredName)। जो fields SDK नहीं जानता वे कभी नहीं छूटते: $pan->raw() पूरी decoded body देता है और $pan->get('field') कोई भी field पढ़ता है। HTTP metadata (status, headers, request id, attempts) $pan->response() पर है। जिन endpoints का schema अभी तय नहीं, वे GenericResponse लौटाते हैं।
// Verification (every call is billed, including valid=false results)
$client->verify->pan('ABCDE1234F', 'Rahul Kumar');
$client->verify->gstin('27ABCDE1234F1Z5');
$client->verify->rc('MH12AB1234');
$client->verify->voterId('ABC1234567');
$client->verify->drivingLicense('MH1420110012345', '1990-01-31'); // IP-allowlisted key required
$client->verify->passport('PA1079341954215', '1990-01-31'); // IP-allowlisted key required
$client->verify->cin('U74999DL2015PTC284009');
$client->verify->udyam('UDYAM-MH-12-1234567');
$client->verify->panToGstin('ABCDE1234F');
$client->verify->panToUdyam('ABCDE1234F');
$client->verify->bankAccount('000123456789', 'ICIC0001234', name: 'Rahul Kumar');
// Services and e-Stamp reference data
$client->services->list();
$client->services->get('estamp.instrument.rent_agreement');
$client->estamp->getSchema('estamp.instrument.rent_agreement');
$client->estamp->listStates();
$client->estamp->listInstruments('MH');
$client->estamp->listArticles('rent_agreement');
$client->estamp->calculateDuty(['stateCode' => 'MH', 'articleCode' => '5']);
$client->estamp->addParties('ord_123', ['customerName' => '...', 'partyName' => '...']);
// Orders and payments
$order = $client->orders->create(['externalOrderId' => 'o1', 'externalCustomerId' => 'c1', 'serviceCode' => 'estamp....']);
$client->orders->get('o1');
$client->orders->getStatus('o1');
$client->orders->getPayment('o1');
$client->orders->listDocuments('o1');
$client->orders->getDocumentDownload('o1', 'doc_1');
$client->payments->intents->create(['externalOrderId' => 'o1', 'amountMinor' => 50000, 'currency' => 'INR', 'paymentConnectionId' => '...']);
$client->payments->intents->get('pi_1');
// Usage, logs, webhook events
$client->usage->summary();
$client->logs->list(['service' => 'pan.verify', 'status' => 'success', 'limit' => 50]);
$client->webhooks->events->list(limit: 20);
$client->webhooks->events->replay('evt_1');Pagination
iterate() एक lazy Generator लौटाता है जो nextCursor को आगे बढ़ाता है; list() एक पेज लौटाता है।
foreach ($client->logs->iterate(['service' => 'pan.verify', 'limit' => 100]) as $entry) {
echo $entry->id, ' ', $entry->createdAt, PHP_EOL; // follows nextCursor lazily (a Generator)
}
$page = $client->logs->list(['limit' => 50]); // $page->items, $page->nextCursorError classes
सभी errors StampMitra\Exception में हैं और StampMitraError से बने हैं (getStatus(), getErrorCode(), getMessage(), getRequestId(), getField(), getBody())। Header मौजूद हो तो RateLimitError में getRetryAfter() जुड़ता है। दूसरे statuses पर base StampMitraError उठता है, और non-JSON error body भी status से map होती है।
| HTTP status | Class | टिप्पणी |
|---|---|---|
| 400 (and local validation) | ValidationError | गलत इनपुट। Validation helpers इसे request भेजने से पहले local रूप से भी उठाते हैं। |
| 401 | AuthenticationError | API key न मिली, गलत है या रद्द हो चुकी है। |
| 402 | PaymentRequiredError | वॉलेट में बैलेंस या क्रेडिट कम है। |
| 403 | PermissionError | सर्वर का संदेश जस का तस दिया जाता है, जैसे Passport और Driving Licence के लिए IP-allowlisted key चाहिए। |
| 404 | NotFoundError | ऑर्डर, सेवा या इवेंट की id मौजूद नहीं है। |
| 409 | ConflictError | Idempotency-Key किसी दूसरी body के साथ दोबारा इस्तेमाल हुई, या state में टकराव है। |
| 429 | RateLimitError | रुककर दोबारा कोशिश करें; सर्वर Retry-After भेजे तो वह मान उपलब्ध रहता है। |
| 5xx | UpstreamError | सर्वर या upstream की ओर की विफलता। 502, 503 और 504 पर अपने आप retry होता है। |
| network / timeout | ConnectionError / TimeoutError | कोई उपयोगी response नहीं मिला: कनेक्शन विफल, timeout या cancel। |
try {
$client->verify->gstin('27ABCDE1234F1Z5');
} catch (\StampMitra\Exception\RateLimitError $e) {
// back off: $e->getRetryAfter()
} catch (\StampMitra\Exception\StampMitraError $e) {
error_log($e->getRequestId() . ': ' . $e->getMessage());
}Retries और idempotency
- डिफ़ॉल्ट maxRetries 2 है, exponential backoff और full jitter के साथ (base 0.5 s, cap 8 s)। सर्वर Retry-After भेजे तो उसका पालन होता है।
- Retry होते हैं: 429, 502, 503, 504, connection errors और timeouts। GET हमेशा retry के लिए सुरक्षित है; POST तभी retry होता है जब उसमें Idempotency-Key हो।
- SDK हर उस POST के लिए UUIDv4 Idempotency-Key बनाता है जिसके लिए API यह header दस्तावेज़ित करती है (verification, duty calculation, e-Stamp parties, orders, payment intents) और retries में वही key दोहराता है। अपनी key ['idempotencyKey' => '...'] से दें।
- बाकी POSTs (webhook replay) तब तक retry नहीं होते जब तक आप key न दें। Guzzle के साथ timeout option हर request पर लागू होता है; किसी दूसरे PSR-18 क्लाइंट में timeout उसी क्लाइंट पर सेट करें।
क्लाइंट-साइड validation
भेजने से पहले इनपुट जाँचे जाते हैं (PAN, GSTIN, IFSC, CIN, Udyam, vehicle number, 9 से 18 अंकों का account number, passport file number, driving licence, voter ID, phone, ISO तारीखें) और upper case में बदले जाते हैं। विफलता पर local ValidationError उठता है, बिना network call और बिना शुल्क के। इसे validateInput: false से बंद करें।
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 पर नहीं।
Webhook::constructEvent, body को ठीक वैसी जैसी मिली उसी पर signature जाँचता है, तुलना constant-time में करता है और उन timestamps को अस्वीकार करता है जो अभी से 300 s (बदला जा सकता है) से ज़्यादा दूर हों। विफलता पर SignatureVerificationError उठता है। Webhook::verifySignature bool लौटाता है और Webhook::constructEventFromHeaders headers खुद पढ़ लेता है।
use StampMitra\Webhook;
$payload = file_get_contents('php://input'); // raw bytes, do not re-encode
$event = Webhook::constructEvent(
$payload,
$_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'],
$_SERVER['HTTP_X_WEBHOOK_SIGNATURE'],
$secret,
);
// or: Webhook::verifySignature($payload, $timestamp, $signature, $secret, toleranceSeconds: 300): bool
// or: Webhook::constructEventFromHeaders($payload, $headers, $secret)डिलीवरी retry होती है, इसलिए events को idempotent तरीके से संभालें और X-Webhook-Event-Id की event id से duplicates हटाएँ।
Framework के उदाहरण
<?php
declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
use StampMitra\Exception\SignatureVerificationError;
use StampMitra\Webhook;
$secret = getenv('STAMPMITRA_WEBHOOK_SECRET') ?: '';
// The signature covers the raw bytes: read the body untouched, never re-encode it.
$payload = (string) file_get_contents('php://input');
try {
$event = Webhook::constructEvent(
$payload,
$_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '',
$_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '',
$secret,
eventId: $_SERVER['HTTP_X_WEBHOOK_EVENT_ID'] ?? null,
);
} catch (SignatureVerificationError $e) {
http_response_code(400);
echo 'invalid signature';
exit;
}
// Handle idempotently: deliveries are retried, so de-duplicate on $event->eventId.
http_response_code(200);
echo 'ok';Webhooks: इवेंट के प्रकार, payload और retry नीति Webhooks पेज पर हैं।
सुरक्षित logging
डिफ़ॉल्ट रूप से कुछ भी log नहीं होता। Requests और responses के debug logs के लिए logger: के रूप में कोई भी PSR-3 logger दें; इनमें Authorization और x-api-key redact रहते हैं और bodies में identifiers व नाम masked रहते हैं।
इंटीग्रेट करने के दूसरे तरीके
REST API किसी भी भाषा से चलती है, और बाकी SDKs भी उसी contract पर बने हैं।
API कॉल करने के लिए तैयार हैं?
मुफ़्त sandbox key लें और ऊपर का quickstart लगभग 5 मिनट में चलाएँ।