SMS gateway api service for android application
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled

This commit is contained in:
Min Zeya Phyo
2026-01-27 11:24:16 +06:30
parent c883fa7128
commit 1fdff55544
3 changed files with 789 additions and 4 deletions

719
SAAS_INTEGRATION_GUIDE.md Normal file
View File

@@ -0,0 +1,719 @@
# SaaS Application Integration Guide
## Overview
This guide shows how to integrate MySMSAPio into your SaaS application to send SMS and OTP messages.
## Prerequisites
1. **MySMSAPio Server**: Running and accessible (e.g., `http://192.168.18.66:3005`)
2. **API Key**: Client API key (starts with `api_live_...`)
3. **Gateway Device**: At least one Android gateway online
## Quick Start
### 1. Create an API Key
**Via Admin Interface**:
1. Go to `http://your-server:3005/admin/api_keys`
2. Click "New API Key"
3. Enter name (e.g., "My SaaS App")
4. Enable permissions: `send_sms`, `receive_sms`
5. Copy the API key (shown only once!)
**Via Rails Console**:
```ruby
result = ApiKey.generate!(
name: "My SaaS App",
permissions: {
send_sms: true,
receive_sms: true,
send_otp: true,
verify_otp: true
}
)
puts result[:raw_key]
# Save this key: api_live_abc123...
```
### 2. Test Connection
```bash
curl -X GET http://your-server:3005/up \
-H "Authorization: Bearer api_live_your_key_here"
```
Expected: `200 OK`
## API Endpoints
### Base URL
```
http://your-server:3005/api/v1
```
### Authentication
All requests require the `Authorization` header:
```
Authorization: Bearer api_live_your_key_here
```
## Sending SMS
### Endpoint
```
POST /api/v1/sms/send
```
### Request
```json
{
"to": "+959123456789",
"message": "Your message here"
}
```
### Response (Success - 202 Accepted)
```json
{
"success": true,
"message_id": "msg_abc123def456...",
"status": "queued"
}
```
### Response (Error - 422 Unprocessable Entity)
```json
{
"error": "Invalid phone number format"
}
```
### Example: cURL
```bash
curl -X POST http://your-server:3005/api/v1/sms/send \
-H "Authorization: Bearer api_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"to": "+959123456789",
"message": "Hello from My SaaS App!"
}'
```
### Example: JavaScript/Node.js
```javascript
const axios = require('axios');
async function sendSMS(phoneNumber, message) {
try {
const response = await axios.post(
'http://your-server:3005/api/v1/sms/send',
{
to: phoneNumber,
message: message
},
{
headers: {
'Authorization': 'Bearer api_live_your_key_here',
'Content-Type': 'application/json'
}
}
);
console.log('SMS sent!');
console.log('Message ID:', response.data.message_id);
console.log('Status:', response.data.status);
return response.data.message_id;
} catch (error) {
console.error('Failed to send SMS:', error.response?.data || error.message);
throw error;
}
}
// Usage
sendSMS('+959123456789', 'Welcome to our service!')
.then(messageId => console.log('Sent:', messageId))
.catch(err => console.error('Error:', err));
```
### Example: Python
```python
import requests
def send_sms(phone_number, message):
url = "http://your-server:3005/api/v1/sms/send"
headers = {
"Authorization": "Bearer api_live_your_key_here",
"Content-Type": "application/json"
}
data = {
"to": phone_number,
"message": message
}
response = requests.post(url, json=data, headers=headers)
if response.status_code == 202:
result = response.json()
print(f"SMS sent! Message ID: {result['message_id']}")
return result['message_id']
else:
print(f"Failed: {response.json()}")
raise Exception(response.json().get('error', 'Unknown error'))
# Usage
message_id = send_sms("+959123456789", "Welcome to our service!")
```
### Example: PHP
```php
<?php
function sendSMS($phoneNumber, $message) {
$url = 'http://your-server:3005/api/v1/sms/send';
$data = [
'to' => $phoneNumber,
'message' => $message
];
$options = [
'http' => [
'header' => [
"Authorization: Bearer api_live_your_key_here",
"Content-Type: application/json"
],
'method' => 'POST',
'content' => json_encode($data)
]
];
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === FALSE) {
throw new Exception('Failed to send SMS');
}
$response = json_decode($result, true);
echo "SMS sent! Message ID: " . $response['message_id'] . "\n";
return $response['message_id'];
}
// Usage
$messageId = sendSMS("+959123456789", "Welcome to our service!");
?>
```
### Example: Ruby
```ruby
require 'httparty'
def send_sms(phone_number, message)
response = HTTParty.post(
'http://your-server:3005/api/v1/sms/send',
headers: {
'Authorization' => 'Bearer api_live_your_key_here',
'Content-Type' => 'application/json'
},
body: {
to: phone_number,
message: message
}.to_json
)
if response.code == 202
puts "SMS sent! Message ID: #{response['message_id']}"
response['message_id']
else
puts "Failed: #{response['error']}"
raise StandardError, response['error']
end
end
# Usage
message_id = send_sms("+959123456789", "Welcome to our service!")
```
## Sending OTP
### Endpoint
```
POST /api/v1/otp/send
```
### Request
```json
{
"phone_number": "+959123456789"
}
```
### Response (Success - 200 OK)
```json
{
"success": true,
"message": "OTP sent successfully",
"expires_in": 300
}
```
### Response (Error - Rate Limited)
```json
{
"error": "Rate limit exceeded. Maximum 3 OTP per hour for this number"
}
```
### Example: JavaScript
```javascript
async function sendOTP(phoneNumber) {
try {
const response = await axios.post(
'http://your-server:3005/api/v1/otp/send',
{
phone_number: phoneNumber
},
{
headers: {
'Authorization': 'Bearer api_live_your_key_here',
'Content-Type': 'application/json'
}
}
);
console.log('OTP sent!');
console.log('Expires in:', response.data.expires_in, 'seconds');
return true;
} catch (error) {
console.error('Failed to send OTP:', error.response?.data || error.message);
throw error;
}
}
// Usage
sendOTP('+959123456789')
.then(() => console.log('OTP sent successfully'))
.catch(err => console.error('Error:', err));
```
### Example: Python
```python
def send_otp(phone_number):
url = "http://your-server:3005/api/v1/otp/send"
headers = {
"Authorization": "Bearer api_live_your_key_here",
"Content-Type": "application/json"
}
data = {
"phone_number": phone_number
}
response = requests.post(url, json=data, headers=headers)
if response.status_code == 200:
result = response.json()
print(f"OTP sent! Expires in {result['expires_in']} seconds")
return True
else:
print(f"Failed: {response.json()}")
raise Exception(response.json().get('error', 'Unknown error'))
# Usage
send_otp("+959123456789")
```
## Verifying OTP
### Endpoint
```
POST /api/v1/otp/verify
```
### Request
```json
{
"phone_number": "+959123456789",
"code": "123456"
}
```
### Response (Success - 200 OK)
```json
{
"success": true,
"message": "OTP verified successfully"
}
```
### Response (Error - Invalid)
```json
{
"error": "Invalid or expired OTP"
}
```
### Example: JavaScript
```javascript
async function verifyOTP(phoneNumber, code) {
try {
const response = await axios.post(
'http://your-server:3005/api/v1/otp/verify',
{
phone_number: phoneNumber,
code: code
},
{
headers: {
'Authorization': 'Bearer api_live_your_key_here',
'Content-Type': 'application/json'
}
}
);
console.log('OTP verified!');
return true;
} catch (error) {
console.error('OTP verification failed:', error.response?.data || error.message);
return false;
}
}
// Usage
const isValid = await verifyOTP('+959123456789', '123456');
if (isValid) {
console.log('User verified!');
} else {
console.log('Invalid OTP');
}
```
### Example: Python
```python
def verify_otp(phone_number, code):
url = "http://your-server:3005/api/v1/otp/verify"
headers = {
"Authorization": "Bearer api_live_your_key_here",
"Content-Type": "application/json"
}
data = {
"phone_number": phone_number,
"code": code
}
response = requests.post(url, json=data, headers=headers)
if response.status_code == 200:
print("OTP verified successfully!")
return True
else:
print(f"Verification failed: {response.json()}")
return False
# Usage
if verify_otp("+959123456789", "123456"):
print("User authenticated!")
else:
print("Authentication failed")
```
## Checking Message Status
### Endpoint
```
GET /api/v1/sms/status/:message_id
```
### Response
```json
{
"message_id": "msg_abc123def456...",
"status": "delivered",
"sent_at": "2025-10-22T10:30:00Z",
"delivered_at": "2025-10-22T10:30:05Z",
"failed_at": null,
"error_message": null
}
```
### Status Values
- `queued` - Waiting for gateway
- `pending` - No gateway available, will retry
- `sent` - Sent to gateway device
- `delivered` - Successfully delivered to recipient
- `failed` - Failed to send
### Example: JavaScript
```javascript
async function checkMessageStatus(messageId) {
try {
const response = await axios.get(
`http://your-server:3005/api/v1/sms/status/${messageId}`,
{
headers: {
'Authorization': 'Bearer api_live_your_key_here'
}
}
);
console.log('Status:', response.data.status);
console.log('Sent at:', response.data.sent_at);
console.log('Delivered at:', response.data.delivered_at);
return response.data;
} catch (error) {
console.error('Failed to check status:', error.response?.data || error.message);
throw error;
}
}
// Usage
const status = await checkMessageStatus('msg_abc123def456');
```
## Complete Integration Example
### User Registration with OTP (JavaScript)
```javascript
const MySMSAPI = {
baseURL: 'http://your-server:3005/api/v1',
apiKey: 'api_live_your_key_here',
headers() {
return {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
};
},
async sendSMS(phoneNumber, message) {
const response = await axios.post(
`${this.baseURL}/sms/send`,
{ to: phoneNumber, message },
{ headers: this.headers() }
);
return response.data;
},
async sendOTP(phoneNumber) {
const response = await axios.post(
`${this.baseURL}/otp/send`,
{ phone_number: phoneNumber },
{ headers: this.headers() }
);
return response.data;
},
async verifyOTP(phoneNumber, code) {
try {
const response = await axios.post(
`${this.baseURL}/otp/verify`,
{ phone_number: phoneNumber, code },
{ headers: this.headers() }
);
return response.data.success;
} catch (error) {
return false;
}
},
async checkStatus(messageId) {
const response = await axios.get(
`${this.baseURL}/sms/status/${messageId}`,
{ headers: this.headers() }
);
return response.data;
}
};
// Usage Example: User Registration Flow
async function registerUser(phoneNumber, userData) {
try {
// Step 1: Send OTP
console.log('Sending OTP...');
await MySMSAPI.sendOTP(phoneNumber);
console.log('OTP sent! Check your phone.');
// Step 2: Wait for user to enter OTP (in real app, this would be a form)
const userEnteredCode = '123456'; // Get from user input
// Step 3: Verify OTP
console.log('Verifying OTP...');
const isValid = await MySMSAPI.verifyOTP(phoneNumber, userEnteredCode);
if (isValid) {
console.log('✅ Phone verified!');
// Step 4: Create user account
const user = await createUserInDatabase(phoneNumber, userData);
// Step 5: Send welcome SMS
const result = await MySMSAPI.sendSMS(
phoneNumber,
`Welcome to our service, ${userData.name}! Your account is now active.`
);
console.log('Welcome SMS sent:', result.message_id);
return { success: true, user };
} else {
console.log('❌ Invalid OTP');
return { success: false, error: 'Invalid OTP' };
}
} catch (error) {
console.error('Registration failed:', error);
return { success: false, error: error.message };
}
}
// Example usage
registerUser('+959123456789', { name: 'John Doe', email: 'john@example.com' });
```
## Error Handling
### Common Errors
| Status | Error | Cause | Solution |
|--------|-------|-------|----------|
| 401 | Unauthorized | Invalid API key | Check API key is correct |
| 422 | Unprocessable Entity | Invalid phone number | Use international format (+959...) |
| 429 | Too Many Requests | Rate limit exceeded | Wait before retrying |
| 404 | Not Found | Message ID not found | Check message ID is correct |
| 503 | Service Unavailable | No gateway online | Ensure gateway is connected |
### Rate Limits
- **SMS Send**: 100 per minute per API key
- **OTP Send**: 3 per hour per phone number
- **OTP Verify**: 3 attempts per OTP code
## Best Practices
### 1. Store API Key Securely
```javascript
// ❌ Don't hardcode in frontend
const apiKey = 'api_live_abc123...';
// ✅ Use environment variables
const apiKey = process.env.SMS_API_KEY;
// ✅ Store in backend config
const apiKey = config.get('sms.apiKey');
```
### 2. Handle Errors Gracefully
```javascript
async function sendSMSWithRetry(phoneNumber, message, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await MySMSAPI.sendSMS(phoneNumber, message);
} catch (error) {
if (i === maxRetries - 1) throw error;
await sleep(1000 * (i + 1)); // Exponential backoff
}
}
}
```
### 3. Validate Phone Numbers
```javascript
function validatePhoneNumber(phone) {
// Myanmar phone numbers
const myanmarRegex = /^\+959\d{8,9}$/;
return myanmarRegex.test(phone);
}
```
### 4. Monitor Message Status
```javascript
async function sendAndTrack(phoneNumber, message) {
const result = await MySMSAPI.sendSMS(phoneNumber, message);
// Poll status until delivered
const maxAttempts = 30; // 5 minutes (30 * 10 seconds)
for (let i = 0; i < maxAttempts; i++) {
await sleep(10000); // Wait 10 seconds
const status = await MySMSAPI.checkStatus(result.message_id);
if (status.status === 'delivered') {
console.log('✅ Message delivered!');
return status;
} else if (status.status === 'failed') {
console.log('❌ Message failed:', status.error_message);
throw new Error(status.error_message);
}
}
console.log('⏱️ Delivery timeout');
return null;
}
```
## Testing
### Test API Key
Use the API Tester in the admin interface:
```
http://your-server:3005/admin/api_tester
```
### Test from Command Line
```bash
# Send SMS
curl -X POST http://your-server:3005/api/v1/sms/send \
-H "Authorization: Bearer api_live_your_key" \
-H "Content-Type: application/json" \
-d '{"to":"+959123456789","message":"Test"}'
# Send OTP
curl -X POST http://your-server:3005/api/v1/otp/send \
-H "Authorization: Bearer api_live_your_key" \
-H "Content-Type: application/json" \
-d '{"phone_number":"+959123456789"}'
# Verify OTP
curl -X POST http://your-server:3005/api/v1/otp/verify \
-H "Authorization: Bearer api_live_your_key" \
-H "Content-Type: application/json" \
-d '{"phone_number":"+959123456789","code":"123456"}'
```
## Troubleshooting
### Messages Stuck in "pending"
**Cause**: No gateway online
**Solution**: Ensure at least one Android gateway is connected
### "Invalid phone number" error
**Cause**: Wrong format
**Solution**: Use international format: `+[country_code][number]`
### "Rate limit exceeded"
**Cause**: Too many requests
**Solution**: Implement exponential backoff and caching
### "Unauthorized" error
**Cause**: Invalid or expired API key
**Solution**: Generate new API key in admin interface
## Support
- **API Documentation**: See `API_DOCUMENTATION.md`
- **Admin Interface**: `http://your-server:3005/admin`
- **API Tester**: `http://your-server:3005/admin/api_tester`
- **Logs**: `http://your-server:3005/admin/logs`
## Summary
**Create API Key** → Get `api_live_...` key
**Send SMS**`POST /api/v1/sms/send`
**Send OTP**`POST /api/v1/otp/send`
**Verify OTP**`POST /api/v1/otp/verify`
**Check Status**`GET /api/v1/sms/status/:id`
Your SaaS app is now ready to send SMS and OTP! 🚀