@smart-pay-chain/otp 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +581 -0
- package/dist/errors.d.ts +101 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +161 -0
- package/dist/http-client.d.ts +33 -0
- package/dist/http-client.d.ts.map +1 -0
- package/dist/http-client.js +136 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/otp-client.d.ts +184 -0
- package/dist/otp-client.d.ts.map +1 -0
- package/dist/otp-client.js +314 -0
- package/dist/types.d.ts +275 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +72 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Smart Pay Chain
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
# @smartpaychain/otp-sdk
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@smartpaychain/otp-sdk)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
|
|
7
|
+
Official TypeScript/JavaScript SDK for the Smart Pay Chain OTP Verification Service. Send and verify one-time passwords via SMS, WhatsApp, and Voice with ease.
|
|
8
|
+
|
|
9
|
+
## What's New in v2.0
|
|
10
|
+
|
|
11
|
+
๐ **Test Mode** - Development-friendly testing with fixed OTP codes and test phone numbers
|
|
12
|
+
๐ **Status Endpoints** - Check OTP status and get codes for testing
|
|
13
|
+
๐ **Auto-Configuration** - SDK auto-configures from server settings
|
|
14
|
+
๐ **Mobile Examples** - Complete React Native integration example
|
|
15
|
+
๐ **Idempotency** - Automatic idempotency keys for retry safety
|
|
16
|
+
๐ **Enhanced Errors** - Machine-readable error codes for better handling
|
|
17
|
+
|
|
18
|
+
[See Migration Guide](#migration-from-v1-to-v2)
|
|
19
|
+
|
|
20
|
+
## Features
|
|
21
|
+
|
|
22
|
+
โจ **Multiple Channels** - Send OTPs via SMS, WhatsApp, or Voice
|
|
23
|
+
๐ **Secure** - Built-in encryption, timing-safe comparisons, and rate limiting
|
|
24
|
+
๐ฏ **Type-Safe** - Full TypeScript support with comprehensive type definitions
|
|
25
|
+
๐ **Retry Logic** - Automatic retries for transient failures
|
|
26
|
+
๐ฑ **Mobile Ready** - React Native example and mobile integration guide
|
|
27
|
+
๐งช **Test Mode** - Development testing with fixed codes
|
|
28
|
+
๐ฆ **Lightweight** - Minimal dependencies
|
|
29
|
+
๐งช **Well-Tested** - Comprehensive test coverage
|
|
30
|
+
๐ **Well-Documented** - Extensive documentation and examples
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install @smartpaychain/otp-sdk
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
or
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
yarn add @smartpaychain/otp-sdk
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { OtpClient, OtpChannel } from '@smartpaychain/otp-sdk';
|
|
48
|
+
|
|
49
|
+
// Initialize the client
|
|
50
|
+
const client = new OtpClient({
|
|
51
|
+
apiKey: 'your-api-key-here',
|
|
52
|
+
autoConfig: true, // v2.0: Auto-fetch server configuration
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Send an OTP
|
|
56
|
+
const result = await client.sendOtp({
|
|
57
|
+
phoneNumber: '+995555123456',
|
|
58
|
+
channel: OtpChannel.SMS,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
console.log('OTP sent! Request ID:', result.requestId);
|
|
62
|
+
|
|
63
|
+
// v2.0: Check status
|
|
64
|
+
const status = await client.getStatus(result.requestId);
|
|
65
|
+
console.log('Current status:', status.status);
|
|
66
|
+
|
|
67
|
+
// Verify the OTP
|
|
68
|
+
const verification = await client.verifyOtp({
|
|
69
|
+
requestId: result.requestId,
|
|
70
|
+
code: '123456', // Code entered by user
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (verification.success) {
|
|
74
|
+
console.log('Phone number verified! โ');
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Table of Contents
|
|
79
|
+
|
|
80
|
+
- [What's New in v2.0](#whats-new-in-v20)
|
|
81
|
+
- [Installation](#installation)
|
|
82
|
+
- [Quick Start](#quick-start)
|
|
83
|
+
- [Test Mode](#test-mode)
|
|
84
|
+
- [Configuration](#configuration)
|
|
85
|
+
- [Usage](#usage)
|
|
86
|
+
- [Sending OTP](#sending-otp)
|
|
87
|
+
- [Verifying OTP](#verifying-otp)
|
|
88
|
+
- [Resending OTP](#resending-otp)
|
|
89
|
+
- [Checking Status](#checking-status)
|
|
90
|
+
- [SDK Configuration](#sdk-configuration)
|
|
91
|
+
- [Mobile App Integration](#mobile-app-integration)
|
|
92
|
+
- [Error Handling](#error-handling)
|
|
93
|
+
- [Idempotency](#idempotency)
|
|
94
|
+
- [API Reference](#api-reference)
|
|
95
|
+
- [Examples](#examples)
|
|
96
|
+
- [Migration from v1 to v2](#migration-from-v1-to-v2)
|
|
97
|
+
- [Testing](#testing)
|
|
98
|
+
- [Contributing](#contributing)
|
|
99
|
+
- [License](#license)
|
|
100
|
+
|
|
101
|
+
## Test Mode
|
|
102
|
+
|
|
103
|
+
Test mode allows you to test OTP flows without sending real SMS messages. Perfect for development and automated testing!
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// Check if server is in test mode
|
|
107
|
+
const testMode = await client.isTestMode();
|
|
108
|
+
if (testMode) {
|
|
109
|
+
console.log('Test mode enabled - use test phone numbers');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Use test phone numbers
|
|
113
|
+
import { TEST_PHONE_NUMBERS, TEST_OTP_CODE } from '@smartpaychain/otp-sdk';
|
|
114
|
+
|
|
115
|
+
const result = await client.sendOtp({
|
|
116
|
+
phoneNumber: TEST_PHONE_NUMBERS.SUCCESS, // +15005550006
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// In test mode, OTP code is always: 123456
|
|
120
|
+
const verification = await client.verifyOtp({
|
|
121
|
+
requestId: result.requestId,
|
|
122
|
+
code: TEST_OTP_CODE, // '123456'
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Test Phone Numbers:**
|
|
127
|
+
- `+15005550006` - Always succeeds
|
|
128
|
+
- `+15005550007` - SMS send fails
|
|
129
|
+
- `+15005550008` - Rate limit exceeded
|
|
130
|
+
- `+15005550009` - Insufficient balance
|
|
131
|
+
- `+15005550010` - Brand not authorized
|
|
132
|
+
|
|
133
|
+
**For Development/Testing Only:**
|
|
134
|
+
```typescript
|
|
135
|
+
// Get actual OTP code (WARNING: Never use in production!)
|
|
136
|
+
const status = await client.getStatusWithCode(result.requestId);
|
|
137
|
+
console.log('OTP Code:', status.otpCode); // For automated tests
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Configuration
|
|
141
|
+
|
|
142
|
+
### Client Options
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
const client = new OtpClient({
|
|
146
|
+
apiKey: 'your-api-key-here', // Required: Your API key
|
|
147
|
+
baseUrl: 'https://custom.api.com', // Optional: Custom base URL
|
|
148
|
+
timeout: 30000, // Optional: Request timeout (ms)
|
|
149
|
+
maxRetries: 3, // Optional: Max retry attempts
|
|
150
|
+
autoConfig: true, // Optional: Auto-fetch server config
|
|
151
|
+
platform: 'react-native', // Optional: SDK platform
|
|
152
|
+
language: 'typescript', // Optional: SDK language
|
|
153
|
+
headers: { // Optional: Custom headers
|
|
154
|
+
'X-App-Version': '1.0.0',
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Usage
|
|
160
|
+
|
|
161
|
+
### Sending OTP
|
|
162
|
+
|
|
163
|
+
Send an OTP to a phone number using your preferred channel:
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
const result = await client.sendOtp({
|
|
167
|
+
phoneNumber: '+995555123456', // Required: E.164 format
|
|
168
|
+
channel: OtpChannel.SMS, // Optional: SMS, WHATSAPP, or VOICE
|
|
169
|
+
ttl: 300, // Optional: Time-to-live (60-600 seconds)
|
|
170
|
+
length: 6, // Optional: Code length (4-8 digits)
|
|
171
|
+
metadata: { // Optional: Custom metadata
|
|
172
|
+
userId: 'user_12345',
|
|
173
|
+
action: 'login',
|
|
174
|
+
},
|
|
175
|
+
idempotencyKey: 'unique-key', // Optional: Prevent duplicate sends
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
console.log(result.requestId); // Save this for verification
|
|
179
|
+
console.log(result.expiresAt); // When the OTP expires
|
|
180
|
+
console.log(result.status); // Current status
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Verifying OTP
|
|
184
|
+
|
|
185
|
+
Verify the OTP code entered by the user:
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
const result = await client.verifyOtp({
|
|
189
|
+
requestId: 'req_123456', // From sendOtp response
|
|
190
|
+
code: '123456', // User-entered code
|
|
191
|
+
ipAddress: '192.168.1.1', // Optional but recommended
|
|
192
|
+
userAgent: 'Mozilla/5.0...', // Optional
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
if (result.success) {
|
|
196
|
+
console.log('Verified!', result.message);
|
|
197
|
+
// Proceed with authentication
|
|
198
|
+
} else {
|
|
199
|
+
console.log('Failed:', result.message);
|
|
200
|
+
// Show error to user
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Resending OTP
|
|
205
|
+
|
|
206
|
+
Resend the OTP if the user didn't receive it:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
const result = await client.resendOtp({
|
|
210
|
+
requestId: 'req_123456',
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
console.log('New request ID:', result.requestId);
|
|
214
|
+
console.log('Expires at:', result.expiresAt);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Checking Status
|
|
218
|
+
|
|
219
|
+
Check the current status of an OTP request:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
const status = await client.getStatus('req_123456');
|
|
223
|
+
|
|
224
|
+
console.log('Status:', status.status); // 'PENDING' | 'SENT' | 'VERIFIED' | 'EXPIRED' | 'FAILED'
|
|
225
|
+
console.log('Attempts:', status.attempts);
|
|
226
|
+
console.log('Max attempts:', status.maxAttempts);
|
|
227
|
+
console.log('Is expired:', status.isExpired);
|
|
228
|
+
console.log('Expires at:', status.expiresAt);
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### SDK Configuration
|
|
232
|
+
|
|
233
|
+
Get server configuration (auto-cached for 1 hour):
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
const config = await client.getConfig();
|
|
237
|
+
|
|
238
|
+
console.log('OTP config:', config.otpConfig);
|
|
239
|
+
console.log('Rate limits:', config.rateLimits);
|
|
240
|
+
console.log('Test mode:', config.features.testMode);
|
|
241
|
+
console.log('Supported countries:', config.supportedCountries);
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Mobile App Integration
|
|
245
|
+
|
|
246
|
+
The SDK is designed to run on your backend server, not directly in mobile apps. This keeps your API keys secure.
|
|
247
|
+
|
|
248
|
+
See our comprehensive [Mobile Apps Guide](./examples/MOBILE_APPS.md) for:
|
|
249
|
+
- React Native integration ([complete example](./examples/react-native-example.tsx))
|
|
250
|
+
- Flutter/Dart integration
|
|
251
|
+
- iOS native (Swift)
|
|
252
|
+
- Android native (Kotlin)
|
|
253
|
+
- Passwordless authentication with refresh tokens
|
|
254
|
+
- Security best practices
|
|
255
|
+
|
|
256
|
+
**Quick Example:**
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
// Backend API
|
|
260
|
+
router.post('/auth/send-otp', async (req, res) => {
|
|
261
|
+
const result = await otpClient.sendOtp({
|
|
262
|
+
phoneNumber: req.body.phoneNumber,
|
|
263
|
+
});
|
|
264
|
+
res.json({ requestId: result.requestId });
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// Mobile app calls your backend
|
|
268
|
+
fetch('https://yourapi.com/auth/send-otp', {
|
|
269
|
+
method: 'POST',
|
|
270
|
+
body: JSON.stringify({ phoneNumber: '+995555123456' }),
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Error Handling
|
|
275
|
+
|
|
276
|
+
The SDK provides specific error classes for different scenarios:
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
import {
|
|
280
|
+
OtpError,
|
|
281
|
+
AuthenticationError,
|
|
282
|
+
ValidationError,
|
|
283
|
+
RateLimitError,
|
|
284
|
+
OtpExpiredError,
|
|
285
|
+
InvalidOtpError,
|
|
286
|
+
} from '@smartpaychain/otp-sdk';
|
|
287
|
+
|
|
288
|
+
try {
|
|
289
|
+
const result = await client.verifyOtp({ ... });
|
|
290
|
+
} catch (error) {
|
|
291
|
+
if (error instanceof InvalidOtpError) {
|
|
292
|
+
console.log('Wrong code. Please try again.');
|
|
293
|
+
} else if (error instanceof OtpExpiredError) {
|
|
294
|
+
console.log('OTP expired. Request a new one.');
|
|
295
|
+
} else if (error instanceof RateLimitError) {
|
|
296
|
+
console.log('Too many attempts. Please wait.');
|
|
297
|
+
} else if (error instanceof OtpError) {
|
|
298
|
+
// Generic OTP error
|
|
299
|
+
console.log('Error:', error.message);
|
|
300
|
+
console.log('Code:', error.code);
|
|
301
|
+
console.log('Retryable:', error.retryable);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Error Properties
|
|
307
|
+
|
|
308
|
+
All OTP errors include:
|
|
309
|
+
|
|
310
|
+
- `message`: Human-readable error message
|
|
311
|
+
- `code`: Error code (e.g., `OTP_EXPIRED`, `INVALID_OTP_CODE`)
|
|
312
|
+
- `statusCode`: HTTP status code
|
|
313
|
+
- `retryable`: Whether the request can be retried
|
|
314
|
+
- `details`: Additional error details (optional)
|
|
315
|
+
- `requestId`: Request ID for debugging (optional)
|
|
316
|
+
|
|
317
|
+
## Idempotency
|
|
318
|
+
|
|
319
|
+
The SDK automatically generates unique idempotency keys for send/resend operations to prevent duplicate requests:
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
// Automatic idempotency (recommended)
|
|
323
|
+
const result = await client.sendOtp({
|
|
324
|
+
phoneNumber: '+995555123456',
|
|
325
|
+
// SDK auto-generates: X-Idempotency-Key: {timestamp}-{random}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// Manual idempotency key (for custom requirements)
|
|
329
|
+
const result = await client.sendOtp({
|
|
330
|
+
phoneNumber: '+995555123456',
|
|
331
|
+
idempotencyKey: 'my-unique-key-123',
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
**Benefits:**
|
|
336
|
+
- Prevents duplicate OTPs on network retries
|
|
337
|
+
- Safe for flaky mobile networks
|
|
338
|
+
- Automatic for all send/resend operations
|
|
339
|
+
|
|
340
|
+
## API Reference
|
|
341
|
+
|
|
342
|
+
### OtpClient
|
|
343
|
+
|
|
344
|
+
#### `constructor(config: OtpClientConfig)`
|
|
345
|
+
|
|
346
|
+
Create a new OTP client instance.
|
|
347
|
+
|
|
348
|
+
#### `sendOtp(options: SendOtpOptions): Promise<SendOtpResponse>`
|
|
349
|
+
|
|
350
|
+
Send an OTP to a phone number.
|
|
351
|
+
|
|
352
|
+
**Parameters:**
|
|
353
|
+
- `phoneNumber` (string, required): Phone number in E.164 format
|
|
354
|
+
- `channel` (OtpChannel, optional): Delivery channel (default: SMS)
|
|
355
|
+
- `ttl` (number, optional): Time-to-live in seconds (60-600, default: 300)
|
|
356
|
+
- `length` (number, optional): OTP code length (4-8, default: 6)
|
|
357
|
+
- `metadata` (object, optional): Custom metadata
|
|
358
|
+
- `idempotencyKey` (string, optional): Idempotency key
|
|
359
|
+
|
|
360
|
+
**Returns:**
|
|
361
|
+
- `requestId` (string): Unique request identifier
|
|
362
|
+
- `expiresAt` (Date): Expiration timestamp
|
|
363
|
+
- `status` (string): Current status
|
|
364
|
+
|
|
365
|
+
#### `verifyOtp(options: VerifyOtpOptions): Promise<VerifyOtpResponse>`
|
|
366
|
+
|
|
367
|
+
Verify an OTP code.
|
|
368
|
+
|
|
369
|
+
**Parameters:**
|
|
370
|
+
- `requestId` (string, required): Request ID from sendOtp
|
|
371
|
+
- `code` (string, required): OTP code to verify
|
|
372
|
+
- `ipAddress` (string, optional): User's IP address
|
|
373
|
+
- `userAgent` (string, optional): User's user agent
|
|
374
|
+
|
|
375
|
+
**Returns:**
|
|
376
|
+
- `success` (boolean): Whether verification succeeded
|
|
377
|
+
- `message` (string): Result message
|
|
378
|
+
|
|
379
|
+
#### `resendOtp(options: ResendOtpOptions): Promise<SendOtpResponse>`
|
|
380
|
+
|
|
381
|
+
Resend an OTP.
|
|
382
|
+
|
|
383
|
+
**Parameters:**
|
|
384
|
+
- `requestId` (string, required): Original request ID
|
|
385
|
+
|
|
386
|
+
**Returns:**
|
|
387
|
+
Same as `sendOtp()`
|
|
388
|
+
|
|
389
|
+
#### `getStatus(requestId: string): Promise<OtpStatus>`
|
|
390
|
+
|
|
391
|
+
Get OTP status (authenticated endpoint).
|
|
392
|
+
|
|
393
|
+
**Parameters:**
|
|
394
|
+
- `requestId` (string, required): Request ID
|
|
395
|
+
|
|
396
|
+
**Returns:**
|
|
397
|
+
- `id` (string): Request ID
|
|
398
|
+
- `phoneNumber` (string): Phone number (masked)
|
|
399
|
+
- `channel` (OtpChannel): Delivery channel
|
|
400
|
+
- `status` (string): Current status
|
|
401
|
+
- `attempts` (number): Verification attempts
|
|
402
|
+
- `maxAttempts` (number): Maximum attempts allowed
|
|
403
|
+
- `expiresAt` (Date): Expiration time
|
|
404
|
+
- `verifiedAt` (Date | null): Verification time
|
|
405
|
+
- `createdAt` (Date): Creation time
|
|
406
|
+
- `isExpired` (boolean): Whether OTP is expired
|
|
407
|
+
|
|
408
|
+
#### `getStatusWithCode(requestId: string): Promise<OtpStatusWithCode>`
|
|
409
|
+
|
|
410
|
+
Get OTP status with code (public endpoint, for testing/development only).
|
|
411
|
+
|
|
412
|
+
**WARNING**: This returns the actual OTP code. Only use in test/development!
|
|
413
|
+
|
|
414
|
+
**Returns:** Same as `getStatus()` plus:
|
|
415
|
+
- `otpCode` (string): The actual OTP code
|
|
416
|
+
- `smsProvider` (string | null): SMS provider used
|
|
417
|
+
- `smsMessageId` (string | null): Provider message ID
|
|
418
|
+
|
|
419
|
+
#### `getConfig(forceRefresh?: boolean): Promise<SdkConfiguration>`
|
|
420
|
+
|
|
421
|
+
Get SDK configuration from server (cached for 1 hour).
|
|
422
|
+
|
|
423
|
+
**Parameters:**
|
|
424
|
+
- `forceRefresh` (boolean, optional): Force refresh cached config
|
|
425
|
+
|
|
426
|
+
**Returns:** Server configuration including rate limits, features, test mode status, etc.
|
|
427
|
+
|
|
428
|
+
#### `testConnection(): Promise<boolean>`
|
|
429
|
+
|
|
430
|
+
Test connectivity to the OTP service.
|
|
431
|
+
|
|
432
|
+
**Returns:** `true` if connected, `false` otherwise
|
|
433
|
+
|
|
434
|
+
#### `isTestMode(): Promise<boolean>`
|
|
435
|
+
|
|
436
|
+
Check if server is in test mode.
|
|
437
|
+
|
|
438
|
+
**Returns:** `true` if test mode is enabled
|
|
439
|
+
|
|
440
|
+
### Types
|
|
441
|
+
|
|
442
|
+
#### `OtpChannel`
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
enum OtpChannel {
|
|
446
|
+
SMS = 'SMS',
|
|
447
|
+
WHATSAPP = 'WHATSAPP',
|
|
448
|
+
VOICE = 'VOICE',
|
|
449
|
+
}
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
#### `ErrorCode`
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
enum ErrorCode {
|
|
456
|
+
AUTHENTICATION_FAILED = 'AUTHENTICATION_FAILED',
|
|
457
|
+
INVALID_API_KEY = 'INVALID_API_KEY',
|
|
458
|
+
INVALID_PHONE_NUMBER = 'INVALID_PHONE_NUMBER',
|
|
459
|
+
OTP_EXPIRED = 'OTP_EXPIRED',
|
|
460
|
+
OTP_MAX_ATTEMPTS = 'OTP_MAX_ATTEMPTS',
|
|
461
|
+
INVALID_OTP_CODE = 'INVALID_OTP_CODE',
|
|
462
|
+
OTP_NOT_FOUND = 'OTP_NOT_FOUND',
|
|
463
|
+
RATE_LIMIT_EXCEEDED = 'RATE_LIMIT_EXCEEDED',
|
|
464
|
+
// ... and more
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
See [types.ts](./src/types.ts) for complete type definitions.
|
|
469
|
+
|
|
470
|
+
## Examples
|
|
471
|
+
|
|
472
|
+
The SDK includes comprehensive examples:
|
|
473
|
+
|
|
474
|
+
- **[Basic Usage](./examples/basic-usage.ts)** - Simple send and verify flow
|
|
475
|
+
- **[Advanced Usage](./examples/advanced-usage.ts)** - Error handling, metadata, retries
|
|
476
|
+
- **[Test Mode](./examples/test-mode-example.ts)** ๐ - Testing with fixed OTP codes
|
|
477
|
+
- **[Express Integration](./examples/express-integration.ts)** - Backend API with Express.js
|
|
478
|
+
- **[React Integration](./examples/react-example.tsx)** - Frontend form with React
|
|
479
|
+
- **[React Native](./examples/react-native-example.tsx)** ๐ - Mobile app integration
|
|
480
|
+
- **[Mobile Apps Guide](./examples/MOBILE_APPS.md)** ๐ - Flutter, iOS, Android examples
|
|
481
|
+
|
|
482
|
+
See the [examples directory](./examples/) for more details.
|
|
483
|
+
|
|
484
|
+
## Migration from v1 to v2
|
|
485
|
+
|
|
486
|
+
All v1.0 code continues to work in v2.0! No breaking changes.
|
|
487
|
+
|
|
488
|
+
**New Features in v2.0:**
|
|
489
|
+
|
|
490
|
+
```typescript
|
|
491
|
+
// v2.0 additions (optional)
|
|
492
|
+
const client = new OtpClient({
|
|
493
|
+
apiKey: 'your-key',
|
|
494
|
+
autoConfig: true, // NEW: Auto-fetch server config
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
// NEW: Check OTP status
|
|
498
|
+
const status = await client.getStatus(requestId);
|
|
499
|
+
|
|
500
|
+
// NEW: Get OTP code for testing
|
|
501
|
+
const testStatus = await client.getStatusWithCode(requestId);
|
|
502
|
+
|
|
503
|
+
// NEW: Check test mode
|
|
504
|
+
const testMode = await client.isTestMode();
|
|
505
|
+
|
|
506
|
+
// NEW: Test connectivity
|
|
507
|
+
const connected = await client.testConnection();
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
**Automatic Improvements:**
|
|
511
|
+
- Idempotency keys auto-generated for all send/resend operations
|
|
512
|
+
- SDK version tracking headers automatically included
|
|
513
|
+
- Enhanced error codes for better error handling
|
|
514
|
+
|
|
515
|
+
## Testing
|
|
516
|
+
|
|
517
|
+
Run the test suite:
|
|
518
|
+
|
|
519
|
+
```bash
|
|
520
|
+
npm test
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
Run tests in watch mode:
|
|
524
|
+
|
|
525
|
+
```bash
|
|
526
|
+
npm run test:watch
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
Generate coverage report:
|
|
530
|
+
|
|
531
|
+
```bash
|
|
532
|
+
npm test -- --coverage
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
## Best Practices
|
|
536
|
+
|
|
537
|
+
1. **Security**
|
|
538
|
+
- Never expose your API key in frontend code
|
|
539
|
+
- Use backend endpoints to proxy OTP requests
|
|
540
|
+
- Implement rate limiting on your endpoints
|
|
541
|
+
- Use HTTPS for all requests
|
|
542
|
+
|
|
543
|
+
2. **User Experience**
|
|
544
|
+
- Show clear error messages to users
|
|
545
|
+
- Display OTP expiration time
|
|
546
|
+
- Implement resend functionality
|
|
547
|
+
- Limit verification attempts (3-5 max)
|
|
548
|
+
|
|
549
|
+
3. **Production**
|
|
550
|
+
- Use environment variables for API keys
|
|
551
|
+
- Implement proper logging and monitoring
|
|
552
|
+
- Handle all error cases gracefully
|
|
553
|
+
- Use idempotency keys for critical flows
|
|
554
|
+
|
|
555
|
+
## Requirements
|
|
556
|
+
|
|
557
|
+
- Node.js >= 16.0.0
|
|
558
|
+
- TypeScript >= 5.0.0 (if using TypeScript)
|
|
559
|
+
|
|
560
|
+
## Support
|
|
561
|
+
|
|
562
|
+
- **Documentation**: [https://docs.smartpaychain.com](https://docs.smartpaychain.com)
|
|
563
|
+
- **Issues**: [GitHub Issues](https://github.com/Smart-Pay-Chain/otp/issues)
|
|
564
|
+
- **Email**: support@smartpaychain.com
|
|
565
|
+
|
|
566
|
+
## Contributing
|
|
567
|
+
|
|
568
|
+
Contributions are welcome! Please read our [Contributing Guide](./CONTRIBUTING.md) for details.
|
|
569
|
+
|
|
570
|
+
## License
|
|
571
|
+
|
|
572
|
+
MIT License - see the [LICENSE](./LICENSE) file for details.
|
|
573
|
+
|
|
574
|
+
## Changelog
|
|
575
|
+
|
|
576
|
+
See [CHANGELOG.md](./CHANGELOG.md) for version history.
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
Made with โค๏ธ by [Smart Pay Chain](https://smartpaychain.com)
|
|
581
|
+
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { ErrorCode, ApiErrorResponse } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Base error class for all OTP SDK errors
|
|
4
|
+
*/
|
|
5
|
+
export declare class OtpError extends Error {
|
|
6
|
+
readonly code: ErrorCode;
|
|
7
|
+
readonly statusCode: number;
|
|
8
|
+
readonly retryable: boolean;
|
|
9
|
+
readonly details?: Record<string, any>;
|
|
10
|
+
readonly requestId?: string;
|
|
11
|
+
constructor(message: string, code: ErrorCode, statusCode: number, retryable: boolean, details?: Record<string, any>, requestId?: string);
|
|
12
|
+
/**
|
|
13
|
+
* Create an OtpError from an API error response
|
|
14
|
+
*/
|
|
15
|
+
static fromApiError(response: ApiErrorResponse): OtpError;
|
|
16
|
+
/**
|
|
17
|
+
* Convert error to a plain object
|
|
18
|
+
*/
|
|
19
|
+
toJSON(): {
|
|
20
|
+
name: string;
|
|
21
|
+
message: string;
|
|
22
|
+
code: ErrorCode;
|
|
23
|
+
statusCode: number;
|
|
24
|
+
retryable: boolean;
|
|
25
|
+
details: Record<string, any> | undefined;
|
|
26
|
+
requestId: string | undefined;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Error thrown when authentication fails
|
|
31
|
+
*/
|
|
32
|
+
export declare class AuthenticationError extends OtpError {
|
|
33
|
+
constructor(message: string, requestId?: string);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Error thrown when validation fails
|
|
37
|
+
*/
|
|
38
|
+
export declare class ValidationError extends OtpError {
|
|
39
|
+
constructor(message: string, details?: Record<string, any>, requestId?: string);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Error thrown when rate limit is exceeded
|
|
43
|
+
*/
|
|
44
|
+
export declare class RateLimitError extends OtpError {
|
|
45
|
+
constructor(message: string, requestId?: string);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Error thrown when OTP is not found
|
|
49
|
+
*/
|
|
50
|
+
export declare class OtpNotFoundError extends OtpError {
|
|
51
|
+
constructor(message: string, requestId?: string);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Error thrown when OTP has expired
|
|
55
|
+
*/
|
|
56
|
+
export declare class OtpExpiredError extends OtpError {
|
|
57
|
+
constructor(message: string, requestId?: string);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Error thrown when OTP code is invalid
|
|
61
|
+
*/
|
|
62
|
+
export declare class InvalidOtpError extends OtpError {
|
|
63
|
+
constructor(message: string, requestId?: string);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Error thrown when service is unavailable
|
|
67
|
+
*/
|
|
68
|
+
export declare class ServiceUnavailableError extends OtpError {
|
|
69
|
+
constructor(message: string, requestId?: string);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Error thrown when account has insufficient balance
|
|
73
|
+
*/
|
|
74
|
+
export declare class InsufficientBalanceError extends OtpError {
|
|
75
|
+
constructor(message: string, requestId?: string);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Error thrown when brand is not configured (Georgian numbers)
|
|
79
|
+
*/
|
|
80
|
+
export declare class BrandNotConfiguredError extends OtpError {
|
|
81
|
+
constructor(message: string, requestId?: string);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Error thrown when brand is pending approval (Georgian numbers)
|
|
85
|
+
*/
|
|
86
|
+
export declare class BrandPendingApprovalError extends OtpError {
|
|
87
|
+
constructor(message: string, requestId?: string);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Error thrown when idempotency key conflict occurs
|
|
91
|
+
*/
|
|
92
|
+
export declare class IdempotencyConflictError extends OtpError {
|
|
93
|
+
constructor(message: string, requestId?: string);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Error thrown when API key has been revoked
|
|
97
|
+
*/
|
|
98
|
+
export declare class ApiKeyRevokedError extends OtpError {
|
|
99
|
+
constructor(message: string, requestId?: string);
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEtD;;GAEG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACjC,SAAgB,IAAI,EAAE,SAAS,CAAC;IAChC,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,SAAS,EAAE,OAAO,CAAC;IACnC,SAAgB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC9C,SAAgB,SAAS,CAAC,EAAE,MAAM,CAAC;gBAGjC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,EACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,OAAO,EAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC7B,SAAS,CAAC,EAAE,MAAM;IAcpB;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,gBAAgB,GAAG,QAAQ;IAWzD;;OAEG;IACH,MAAM;;;;;;;;;CAWP;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,QAAQ;gBACnC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;CAIhD;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,QAAQ;gBAC/B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM;CAI/E;AAED;;GAEG;AACH,qBAAa,cAAe,SAAQ,QAAQ;gBAC9B,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;CAIhD;AAED;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,QAAQ;gBAChC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;CAIhD;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,QAAQ;gBAC/B,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;CAIhD;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,QAAQ;gBAC/B,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;CAIhD;AAED;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,QAAQ;gBACvC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;CAIhD;AAED;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,QAAQ;gBACxC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;CAIhD;AAED;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,QAAQ;gBACvC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;CAIhD;AAED;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,QAAQ;gBACzC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;CAIhD;AAED;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,QAAQ;gBACxC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;CAIhD;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,QAAQ;gBAClC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;CAIhD"}
|