sms-verification-api 0.9.1
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/.env.example +20 -0
- package/DEPLOYMENT.md +151 -0
- package/README.md +475 -0
- package/docs/app/(home)/layout.tsx +7 -0
- package/docs/app/(home)/page.tsx +38 -0
- package/docs/app/docs/[[...slug]]/page.tsx +59 -0
- package/docs/app/docs/layout.tsx +12 -0
- package/docs/app/docs-og/[...slug]/route.ts +24 -0
- package/docs/app/globals.css +587 -0
- package/docs/app/layout.config.tsx +13 -0
- package/docs/app/layout.tsx +27 -0
- package/docs/app/logo.tsx +35 -0
- package/docs/content/docs/API_AUTHENTICATION.md +91 -0
- package/docs/content/docs/DEPLOYMENT.md +181 -0
- package/docs/content/docs/api/post.mdx +35 -0
- package/docs/content/docs/api/verify.mdx +34 -0
- package/docs/content/docs/meta.json +8 -0
- package/docs/content/docs/verify-legal-name.md +339 -0
- package/docs/lib/source.ts +14 -0
- package/docs/mdx-components.tsx +12 -0
- package/docs/next.config.mjs +51 -0
- package/docs/openapi.json +329 -0
- package/docs/package.json +37 -0
- package/docs/postcss.config.mjs +5 -0
- package/docs/scripts/generate-docs.mjs +23 -0
- package/docs/source.config.ts +5 -0
- package/docs/tsconfig.json +29 -0
- package/docs/worker.js +35 -0
- package/docs/wrangler.toml +26 -0
- package/examples/client.js +119 -0
- package/examples/demo.html +325 -0
- package/examples/libphonenumber-example.js +120 -0
- package/openapi.json +329 -0
- package/package.json +71 -0
- package/scripts/deploy.sh +63 -0
- package/src/identity-verification-server.ts +553 -0
- package/src/index.js +8 -0
- package/src/sns.js +236 -0
- package/src/verify-phone-server.js +448 -0
- package/src/verify-phone.ts +551 -0
- package/test/api.test.js +201 -0
- package/test/integration.test.js +152 -0
- package/test/metadata-test.js +73 -0
- package/test/server.test.js +143 -0
- package/test/setup.js +32 -0
- package/test/utils.test.js +186 -0
- package/test/verify.test.js +23 -0
- package/test/voip.test.js +112 -0
- package/vitest.config.js +10 -0
- package/wrangler.toml +27 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
{
|
|
2
|
+
"openapi": "3.0.0",
|
|
3
|
+
"info": {
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"title": "SMS Verification API",
|
|
6
|
+
"description": "A comprehensive SMS verification API using AWS SNS HTTP API with Hono server on Cloudflare Workers. **Authentication Required**: All endpoints except `/health`, `/docs`, and `/openapi.json` require API key authentication.",
|
|
7
|
+
"contact": { "name": "API Support", "email": "support@example.com" },
|
|
8
|
+
"license": { "name": "MIT", "url": "https://opensource.org/licenses/MIT" }
|
|
9
|
+
},
|
|
10
|
+
"servers": [
|
|
11
|
+
{
|
|
12
|
+
"url": "https://sms-verification-api.your-subdomain.workers.dev",
|
|
13
|
+
"description": "Production server"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"url": "https://sms-verification-api-staging.your-subdomain.workers.dev",
|
|
17
|
+
"description": "Staging server"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"components": { "schemas": {}, "parameters": {} },
|
|
21
|
+
"security": [{ "ApiKeyAuth": [] }, { "BearerAuth": [] }],
|
|
22
|
+
"paths": {
|
|
23
|
+
"/health": {
|
|
24
|
+
"get": {
|
|
25
|
+
"tags": ["health"],
|
|
26
|
+
"summary": "Health check",
|
|
27
|
+
"description": "Returns the health status of the API server",
|
|
28
|
+
"responses": {
|
|
29
|
+
"200": {
|
|
30
|
+
"description": "Health check successful",
|
|
31
|
+
"content": {
|
|
32
|
+
"application/json": {
|
|
33
|
+
"schema": {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"properties": {
|
|
36
|
+
"status": { "type": "string", "example": "ok" },
|
|
37
|
+
"timestamp": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"example": "2024-01-01T00:00:00.000Z"
|
|
40
|
+
},
|
|
41
|
+
"server": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"example": "Hono on Cloudflare Workers"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"required": ["status", "timestamp", "server"]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"/api/send-verification": {
|
|
55
|
+
"post": {
|
|
56
|
+
"tags": ["verification"],
|
|
57
|
+
"summary": "Send SMS verification code",
|
|
58
|
+
"description": "Sends a 6-digit verification code to the specified phone number via SMS. Optionally accepts a custom code.",
|
|
59
|
+
"security": [{ "ApiKeyAuth": [] }, { "BearerAuth": [] }],
|
|
60
|
+
"requestBody": {
|
|
61
|
+
"content": {
|
|
62
|
+
"application/json": {
|
|
63
|
+
"schema": {
|
|
64
|
+
"type": "object",
|
|
65
|
+
"properties": {
|
|
66
|
+
"phoneNumber": {
|
|
67
|
+
"type": "string",
|
|
68
|
+
"pattern": "^\\+[1-9]\\d{1,14}$",
|
|
69
|
+
"description": "Phone number in E.164 format",
|
|
70
|
+
"example": "+1234567890"
|
|
71
|
+
},
|
|
72
|
+
"code": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"minLength": 6,
|
|
75
|
+
"maxLength": 6,
|
|
76
|
+
"pattern": "^\\d{6}$",
|
|
77
|
+
"description": "Optional custom 6-digit verification code. If not provided, a random code will be generated.",
|
|
78
|
+
"example": "123456"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"required": ["phoneNumber"]
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"responses": {
|
|
87
|
+
"200": {
|
|
88
|
+
"description": "Verification code sent successfully",
|
|
89
|
+
"content": {
|
|
90
|
+
"application/json": {
|
|
91
|
+
"schema": {
|
|
92
|
+
"type": "object",
|
|
93
|
+
"properties": {
|
|
94
|
+
"success": { "type": "boolean", "example": true },
|
|
95
|
+
"message": {
|
|
96
|
+
"type": "string",
|
|
97
|
+
"example": "Verification code sent successfully"
|
|
98
|
+
},
|
|
99
|
+
"messageId": {
|
|
100
|
+
"type": "string",
|
|
101
|
+
"example": "aws-message-id-123"
|
|
102
|
+
},
|
|
103
|
+
"expiresIn": {
|
|
104
|
+
"type": "number",
|
|
105
|
+
"description": "Expiration time in seconds",
|
|
106
|
+
"example": 600
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"required": ["success", "message", "messageId", "expiresIn"]
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"400": {
|
|
115
|
+
"description": "Bad request - invalid phone number or other validation error",
|
|
116
|
+
"content": {
|
|
117
|
+
"application/json": {
|
|
118
|
+
"schema": {
|
|
119
|
+
"type": "object",
|
|
120
|
+
"properties": {
|
|
121
|
+
"error": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"example": "Validation error"
|
|
124
|
+
},
|
|
125
|
+
"details": {
|
|
126
|
+
"type": "string",
|
|
127
|
+
"example": "Additional error details"
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
"required": ["error"]
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
"429": {
|
|
136
|
+
"description": "Too many requests - rate limited",
|
|
137
|
+
"content": {
|
|
138
|
+
"application/json": {
|
|
139
|
+
"schema": {
|
|
140
|
+
"type": "object",
|
|
141
|
+
"properties": {
|
|
142
|
+
"error": {
|
|
143
|
+
"type": "string",
|
|
144
|
+
"example": "Please wait before requesting another code"
|
|
145
|
+
},
|
|
146
|
+
"retryAfter": {
|
|
147
|
+
"type": "number",
|
|
148
|
+
"description": "Seconds to wait before retry",
|
|
149
|
+
"example": 30
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
"required": ["error", "retryAfter"]
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"500": {
|
|
158
|
+
"description": "Internal server error",
|
|
159
|
+
"content": {
|
|
160
|
+
"application/json": {
|
|
161
|
+
"schema": {
|
|
162
|
+
"type": "object",
|
|
163
|
+
"properties": {
|
|
164
|
+
"error": {
|
|
165
|
+
"type": "string",
|
|
166
|
+
"example": "Validation error"
|
|
167
|
+
},
|
|
168
|
+
"details": {
|
|
169
|
+
"type": "string",
|
|
170
|
+
"example": "Additional error details"
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
"required": ["error"]
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
"/api/verify-code": {
|
|
182
|
+
"post": {
|
|
183
|
+
"tags": ["verification"],
|
|
184
|
+
"summary": "Verify SMS code",
|
|
185
|
+
"description": "Verifies the submitted code against the sent verification code",
|
|
186
|
+
"security": [{ "ApiKeyAuth": [] }, { "BearerAuth": [] }],
|
|
187
|
+
"requestBody": {
|
|
188
|
+
"content": {
|
|
189
|
+
"application/json": {
|
|
190
|
+
"schema": {
|
|
191
|
+
"type": "object",
|
|
192
|
+
"properties": {
|
|
193
|
+
"phoneNumber": {
|
|
194
|
+
"type": "string",
|
|
195
|
+
"pattern": "^\\+[1-9]\\d{1,14}$",
|
|
196
|
+
"description": "Phone number in E.164 format",
|
|
197
|
+
"example": "+1234567890"
|
|
198
|
+
},
|
|
199
|
+
"code": {
|
|
200
|
+
"type": "string",
|
|
201
|
+
"minLength": 6,
|
|
202
|
+
"maxLength": 6,
|
|
203
|
+
"pattern": "^\\d{6}$",
|
|
204
|
+
"description": "6-digit verification code",
|
|
205
|
+
"example": "123456"
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
"required": ["phoneNumber", "code"]
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
"responses": {
|
|
214
|
+
"200": {
|
|
215
|
+
"description": "Phone number verified successfully",
|
|
216
|
+
"content": {
|
|
217
|
+
"application/json": {
|
|
218
|
+
"schema": {
|
|
219
|
+
"type": "object",
|
|
220
|
+
"properties": {
|
|
221
|
+
"success": { "type": "boolean", "example": true },
|
|
222
|
+
"message": {
|
|
223
|
+
"type": "string",
|
|
224
|
+
"example": "Phone number verified successfully"
|
|
225
|
+
},
|
|
226
|
+
"verificationToken": {
|
|
227
|
+
"type": "string",
|
|
228
|
+
"example": "unique-verification-token"
|
|
229
|
+
},
|
|
230
|
+
"phoneNumber": {
|
|
231
|
+
"type": "string",
|
|
232
|
+
"pattern": "^\\+[1-9]\\d{1,14}$",
|
|
233
|
+
"description": "Phone number in E.164 format",
|
|
234
|
+
"example": "+1234567890"
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
"required": [
|
|
238
|
+
"success",
|
|
239
|
+
"message",
|
|
240
|
+
"verificationToken",
|
|
241
|
+
"phoneNumber"
|
|
242
|
+
]
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
"400": {
|
|
248
|
+
"description": "Bad request - invalid code or expired",
|
|
249
|
+
"content": {
|
|
250
|
+
"application/json": {
|
|
251
|
+
"schema": {
|
|
252
|
+
"anyOf": [
|
|
253
|
+
{
|
|
254
|
+
"type": "object",
|
|
255
|
+
"properties": {
|
|
256
|
+
"error": {
|
|
257
|
+
"type": "string",
|
|
258
|
+
"example": "Validation error"
|
|
259
|
+
},
|
|
260
|
+
"details": {
|
|
261
|
+
"type": "string",
|
|
262
|
+
"example": "Additional error details"
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
"required": ["error"]
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
"type": "object",
|
|
269
|
+
"properties": {
|
|
270
|
+
"error": {
|
|
271
|
+
"type": "string",
|
|
272
|
+
"example": "Invalid verification code"
|
|
273
|
+
},
|
|
274
|
+
"attemptsRemaining": { "type": "number", "example": 3 }
|
|
275
|
+
},
|
|
276
|
+
"required": ["error", "attemptsRemaining"]
|
|
277
|
+
}
|
|
278
|
+
]
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
"429": {
|
|
284
|
+
"description": "Too many verification attempts",
|
|
285
|
+
"content": {
|
|
286
|
+
"application/json": {
|
|
287
|
+
"schema": {
|
|
288
|
+
"type": "object",
|
|
289
|
+
"properties": {
|
|
290
|
+
"error": {
|
|
291
|
+
"type": "string",
|
|
292
|
+
"example": "Validation error"
|
|
293
|
+
},
|
|
294
|
+
"details": {
|
|
295
|
+
"type": "string",
|
|
296
|
+
"example": "Additional error details"
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
"required": ["error"]
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
"500": {
|
|
305
|
+
"description": "Internal server error",
|
|
306
|
+
"content": {
|
|
307
|
+
"application/json": {
|
|
308
|
+
"schema": {
|
|
309
|
+
"type": "object",
|
|
310
|
+
"properties": {
|
|
311
|
+
"error": {
|
|
312
|
+
"type": "string",
|
|
313
|
+
"example": "Validation error"
|
|
314
|
+
},
|
|
315
|
+
"details": {
|
|
316
|
+
"type": "string",
|
|
317
|
+
"example": "Additional error details"
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
"required": ["error"]
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "example-openapi",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "next build",
|
|
7
|
+
"build:api": "bun run ./scripts/generate-docs.mjs",
|
|
8
|
+
"dev": "next dev --turbo",
|
|
9
|
+
"start": "next start --port 3001",
|
|
10
|
+
"types:check": "fumadocs-mdx && tsc --noEmit",
|
|
11
|
+
"deploy": "wrangler deploy",
|
|
12
|
+
"deploy:staging": "wrangler deploy --env staging",
|
|
13
|
+
"deploy:production": "wrangler deploy --env production"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@fumadocs/mdx-remote": "^1.3.4",
|
|
17
|
+
"fumadocs-core": "15.6.1",
|
|
18
|
+
"fumadocs-mdx": "11.6.10",
|
|
19
|
+
"fumadocs-openapi": "^9.1.0",
|
|
20
|
+
"fumadocs-ui": "15.6.1",
|
|
21
|
+
"lucide-react": "^0.525.0",
|
|
22
|
+
"next": "15.3.4",
|
|
23
|
+
"react": "19.1.0",
|
|
24
|
+
"react-dom": "19.1.0",
|
|
25
|
+
"shiki": "^3.7.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@tailwindcss/postcss": "^4.1.11",
|
|
29
|
+
"@types/mdx": "^2.0.13",
|
|
30
|
+
"@types/react": "^19.1.8",
|
|
31
|
+
"@types/react-dom": "^19.1.6",
|
|
32
|
+
"postcss": "^8.5.6",
|
|
33
|
+
"rimraf": "^6.0.1",
|
|
34
|
+
"tailwindcss": "^4.1.11",
|
|
35
|
+
"typescript": "^5.8.3"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as OpenAPI from 'fumadocs-openapi';
|
|
2
|
+
import { rimraf } from 'rimraf';
|
|
3
|
+
|
|
4
|
+
const out = './content/docs/(api)';
|
|
5
|
+
|
|
6
|
+
async function generate() {
|
|
7
|
+
// clean generated files
|
|
8
|
+
await rimraf(out, {
|
|
9
|
+
filter(v) {
|
|
10
|
+
return !v.endsWith('index.mdx') && !v.endsWith('meta.json');
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
await OpenAPI.generateFiles({
|
|
15
|
+
// input files
|
|
16
|
+
input: ['./openapi.json'],
|
|
17
|
+
output: out,
|
|
18
|
+
includeDescription: false,
|
|
19
|
+
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
void generate();
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"baseUrl": ".",
|
|
4
|
+
"target": "ESNext",
|
|
5
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
6
|
+
"allowJs": true,
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"strict": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"esModuleInterop": true,
|
|
12
|
+
"module": "esnext",
|
|
13
|
+
"moduleResolution": "bundler",
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
"isolatedModules": true,
|
|
16
|
+
"jsx": "preserve",
|
|
17
|
+
"incremental": true,
|
|
18
|
+
"paths": {
|
|
19
|
+
"@/*": ["./*"]
|
|
20
|
+
},
|
|
21
|
+
"plugins": [
|
|
22
|
+
{
|
|
23
|
+
"name": "next"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
28
|
+
"exclude": ["node_modules"]
|
|
29
|
+
}
|
package/docs/worker.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Simple worker to serve static Next.js export files
|
|
2
|
+
export default {
|
|
3
|
+
async fetch(request, env, ctx) {
|
|
4
|
+
const url = new URL(request.url);
|
|
5
|
+
let path = url.pathname;
|
|
6
|
+
|
|
7
|
+
// Default to index.html for root
|
|
8
|
+
if (path === '/') {
|
|
9
|
+
path = '/index.html';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Try to serve the file from the out directory
|
|
13
|
+
try {
|
|
14
|
+
const file = await env.ASSETS.fetch(new Request(url.origin + path));
|
|
15
|
+
if (file.status === 200) {
|
|
16
|
+
return file;
|
|
17
|
+
}
|
|
18
|
+
} catch (e) {
|
|
19
|
+
// File not found, continue to fallback
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Fallback to index.html for SPA routing
|
|
23
|
+
try {
|
|
24
|
+
const indexFile = await env.ASSETS.fetch(new Request(url.origin + '/index.html'));
|
|
25
|
+
if (indexFile.status === 200) {
|
|
26
|
+
return indexFile;
|
|
27
|
+
}
|
|
28
|
+
} catch (e) {
|
|
29
|
+
// Index file not found
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Return 404
|
|
33
|
+
return new Response('Not Found', { status: 404 });
|
|
34
|
+
}
|
|
35
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name = "sms-verification-docs"
|
|
2
|
+
main = "worker.js"
|
|
3
|
+
compatibility_date = "2024-01-01"
|
|
4
|
+
|
|
5
|
+
# Static assets
|
|
6
|
+
[assets]
|
|
7
|
+
directory = "out"
|
|
8
|
+
|
|
9
|
+
# Build configuration
|
|
10
|
+
[build]
|
|
11
|
+
command = "bun run build"
|
|
12
|
+
watch_dir = "."
|
|
13
|
+
|
|
14
|
+
# Environment variables
|
|
15
|
+
[vars]
|
|
16
|
+
NODE_ENV = "production"
|
|
17
|
+
|
|
18
|
+
# Staging environment
|
|
19
|
+
[env.staging]
|
|
20
|
+
name = "sms-verification-docs-staging"
|
|
21
|
+
vars = { ENVIRONMENT = "staging" }
|
|
22
|
+
|
|
23
|
+
# Production environment
|
|
24
|
+
[env.production]
|
|
25
|
+
name = "sms-verification-docs-production"
|
|
26
|
+
vars = { ENVIRONMENT = "production" }
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import grab from 'grab-api.js'
|
|
2
|
+
/**
|
|
3
|
+
* Example client for the SMS Verification API
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const API_BASE_URL = 'http://localhost:8787'; // Change to your deployed URL
|
|
7
|
+
const API_KEY = 'sms_1234567890abcdef1234567890abcdef'; // Change to your API key
|
|
8
|
+
|
|
9
|
+
class SMSVerificationClient {
|
|
10
|
+
constructor(baseUrl = API_BASE_URL, apiKey = API_KEY) {
|
|
11
|
+
this.baseUrl = baseUrl;
|
|
12
|
+
this.apiKey = apiKey;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async sendVerificationCode(phoneNumber, options = {}) {
|
|
16
|
+
await grab(`${this.baseUrl}/api/send`, {
|
|
17
|
+
headers: {
|
|
18
|
+
'X-API-Key': this.apiKey
|
|
19
|
+
},
|
|
20
|
+
phoneNumber,
|
|
21
|
+
...options
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async verifyCode(phoneNumber, code) {
|
|
26
|
+
const response = await fetch(`${this.baseUrl}/api/verify`, {
|
|
27
|
+
method: 'POST',
|
|
28
|
+
headers: {
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
'X-API-Key': this.apiKey
|
|
31
|
+
},
|
|
32
|
+
body: JSON.stringify({
|
|
33
|
+
phoneNumber,
|
|
34
|
+
code
|
|
35
|
+
})
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return response.json();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async sendGeneralSMS(phoneNumber, message, options = {}) {
|
|
42
|
+
const response = await fetch(`${this.baseUrl}/api/sms`, {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: {
|
|
45
|
+
'Content-Type': 'application/json',
|
|
46
|
+
'X-API-Key': this.apiKey
|
|
47
|
+
},
|
|
48
|
+
body: JSON.stringify({
|
|
49
|
+
phoneNumber,
|
|
50
|
+
message,
|
|
51
|
+
...options
|
|
52
|
+
})
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return response.json();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async checkHealth() {
|
|
59
|
+
const response = await fetch(`${this.baseUrl}/health`);
|
|
60
|
+
return response.json();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Example usage
|
|
65
|
+
async function example() {
|
|
66
|
+
const client = new SMSVerificationClient();
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
// Check server health
|
|
70
|
+
console.log('Checking server health...');
|
|
71
|
+
const health = await client.checkHealth();
|
|
72
|
+
console.log('Health:', health);
|
|
73
|
+
|
|
74
|
+
// Send verification code
|
|
75
|
+
console.log('\nSending verification code...');
|
|
76
|
+
const sendResult = await client.sendVerificationCode('+1234567890', {
|
|
77
|
+
blockVoip: true,
|
|
78
|
+
senderId: 'MyApp',
|
|
79
|
+
messageTemplate: 'Your verification code is: {code}. Valid for 10 minutes.'
|
|
80
|
+
});
|
|
81
|
+
console.log('Send result:', sendResult);
|
|
82
|
+
|
|
83
|
+
if (sendResult.success) {
|
|
84
|
+
// Verify the code (in real app, user would enter this)
|
|
85
|
+
console.log('\nVerifying code...');
|
|
86
|
+
const verifyResult = await client.verifyCode('+1234567890', sendResult.code);
|
|
87
|
+
console.log('Verify result:', verifyResult);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Send general SMS
|
|
91
|
+
console.log('\nSending general SMS...');
|
|
92
|
+
const smsResult = await client.sendGeneralSMS(
|
|
93
|
+
'+1234567890',
|
|
94
|
+
'Hello from your app! This is a test message.',
|
|
95
|
+
{ senderId: 'MyApp' }
|
|
96
|
+
);
|
|
97
|
+
console.log('SMS result:', smsResult);
|
|
98
|
+
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.error('Error:', error);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Node.js usage
|
|
105
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
106
|
+
module.exports = SMSVerificationClient;
|
|
107
|
+
|
|
108
|
+
// Run example if this file is executed directly
|
|
109
|
+
if (require.main === module) {
|
|
110
|
+
example();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Browser usage
|
|
115
|
+
if (typeof window !== 'undefined') {
|
|
116
|
+
window.SMSVerificationClient = SMSVerificationClient;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export default SMSVerificationClient;
|