├── .gitignore ├── .npmignore ├── README.md ├── index.js ├── package.json └── test └── socketTest.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | .tmp/ 4 | npm-debug.log 5 | .DS_Store 6 | *.DS_Store 7 | *.sublime-project 8 | *.sublime-workspace 9 | 10 | # developement mode 11 | dev/* 12 | dev/ -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | .tmp/ 4 | npm-debug.log 5 | .DS_Store 6 | *.DS_Store 7 | *.sublime-project 8 | *.sublime-workspace -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## SendOtp - Node.js SDK 2 | 3 | This SDK enables sendOTP and allows you to send OTP 4 | 5 | ### Set-up: 6 | 7 | 1. Download the NPM module 8 | ``` 9 | npm install sendotp --save 10 | ``` 11 | 2. Require the package in your code. 12 | ``` 13 | const SendOtp = require('sendotp'); 14 | ``` 15 | 3. Initialize with your [MSG91](https://msg91.com) auth key 16 | ``` 17 | const sendOtp = new SendOtp('AuthKey'); 18 | ``` 19 | That's all, your SDK is set up! 20 | 21 | ### Requests 22 | 23 | You now have the send, retry and verify otp via following methods. 24 | ```javascript 25 | sendOtp.send(contactNumber, senderId, otp, callback); //otp is optional if not sent it'll be generated automatically 26 | sendOtp.retry(contactNumber, retryVoice, callback); 27 | sendOtp.verify(contactNumber, otpToVerify, callback); 28 | ``` 29 | 30 | ### Note: 31 | In `callback` function you'll get two parameters but you have to always listen for second param instead of direct error object. 32 | Error object sample code 33 | ```javascript 34 | {"type":"error","message":"ERROR_MESSAGE"} 35 | ``` 36 | 37 | ### Usage: 38 | 39 | To send OTP, without optional parameters 40 | ```javascript 41 | sendOtp.send("919999999999", "PRIIND", function (error, data) { 42 | console.log(data); 43 | }); 44 | ``` 45 | 46 | To send OTP, with optional parameters 47 | ```javascript 48 | sendOtp.send("919999999999", "PRIIND", "4635", function (error, data) { 49 | console.log(data); 50 | }); 51 | ``` 52 | 53 | If you want to set custom expiry of OTP verification 54 | ```javascript 55 | sendOtp.setOtpExpiry('90'); //in minutes 56 | ``` 57 | 58 | To retry OTP 59 | ```javascript 60 | sendOtp.retry("919999999999", false, function (error, data) { 61 | console.log(data); 62 | }); 63 | ``` 64 | **Note:** In sendOtp.retry() set retryVoice false if you want to retry otp via text, default value is true 65 | 66 | To verify OTP 67 | ```javascript 68 | sendOtp.verify("919999999999", "4365", function (error, data) { 69 | console.log(data); // data object with keys 'message' and 'type' 70 | if(data.type == 'success') console.log('OTP verified successfully') 71 | if(data.type == 'error') console.log('OTP verification failed') 72 | }); 73 | ``` 74 | 75 | ### Options: 76 | 77 | By default sendotp uses default message template, but custom message template can also set in constructor like 78 | ```javascript 79 | const SendOtp = require('sendotp'); 80 | const sendOtp = new SendOtp('AuthKey', 'Otp for your order is {{otp}}, please do not share it with anybody'); 81 | ``` 82 | 83 | `{{otp}}` expression is used to inject generated otp in message. 84 | 85 | ### Licence: 86 | 87 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 88 | 89 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 90 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | let request = require('request'); 4 | 5 | class SendOtp { 6 | 7 | /** 8 | * Creates a new SendOtp instance 9 | * @param {string} authKey Authentication key 10 | * @param {string, optional} messageTemplate 11 | */ 12 | constructor(authKey, messageTemplate) { 13 | this.authKey = authKey; 14 | if(messageTemplate){ 15 | this.messageTemplate = messageTemplate; 16 | }else{ 17 | this.messageTemplate = "Your otp is {{otp}}. Please do not share it with anybody"; 18 | } 19 | this.otp_expiry = 1440; //1 Day =1440 minutes 20 | } 21 | 22 | /** 23 | * Returns the base URL for MSG91 api call 24 | * @returns {string} Base URL for MSG91 api call 25 | */ 26 | static getBaseURL() { 27 | return "https://control.msg91.com/api/"; 28 | } 29 | 30 | /** 31 | * Set the OTP expiry minutes for MSG91 api call 32 | */ 33 | setOtpExpiry(otp_expiry) { 34 | this.otp_expiry=otp_expiry; 35 | return; 36 | } 37 | 38 | /** 39 | * Returns the 4 digit otp 40 | * @returns {integer} 4 digit otp 41 | */ 42 | static generateOtp() { 43 | return Math.floor(1000 + Math.random() * 9000); 44 | } 45 | 46 | /** 47 | * Send Otp to given mobile number 48 | * @param {string} contactNumber receiver's mobile number along with country code 49 | * @param {string} senderId 50 | * @param {string, optional} otp 51 | * Return promise if no callback is passed and promises available 52 | */ 53 | send(contactNumber, senderId, otp, callback) { 54 | if (typeof otp === 'function') { 55 | callback = otp; 56 | otp = SendOtp.generateOtp() 57 | } 58 | let args = { 59 | authkey: this.authKey, 60 | mobile: contactNumber, 61 | sender: senderId, 62 | message: this.messageTemplate.replace('{{otp}}', otp), 63 | otp: otp, 64 | otp_expiry: this.otp_expiry 65 | }; 66 | return SendOtp.doRequest('get', "sendotp.php", args, callback); 67 | } 68 | 69 | /** 70 | * Retry Otp to given mobile number 71 | * @param {string} contactNumber receiver's mobile number along with country code 72 | * @param {boolean} retryVoice, false to retry otp via text call, default true 73 | * Return promise if no callback is passed and promises available 74 | */ 75 | retry(contactNumber, retryVoice, callback) { 76 | let retryType = 'voice'; 77 | if (!retryVoice) { 78 | retryType = 'text' 79 | } 80 | let args = { 81 | authkey: this.authKey, 82 | mobile: contactNumber, 83 | retrytype: retryType 84 | }; 85 | 86 | return SendOtp.doRequest('get', "retryotp.php", args, callback); 87 | } 88 | 89 | /** 90 | * Verify Otp to given mobile number 91 | * @param {string} contactNumber receiver's mobile number along with country code 92 | * @param {string} otp otp to verify 93 | * Return promise if no callback is passed and promises available 94 | */ 95 | verify(contactNumber, otp, callback) { 96 | let args = { 97 | authkey: this.authKey, 98 | mobile: contactNumber, 99 | otp: otp 100 | }; 101 | return SendOtp.doRequest('get', "verifyRequestOTP.php", args, callback); 102 | } 103 | 104 | static doRequest (method, path, params, callback) { 105 | 106 | if (typeof params === 'function') { 107 | callback = params; 108 | params = {}; 109 | } 110 | // Return promise if no callback is passed and promises available 111 | else if (callback === undefined && this.allow_promise) { 112 | promise = true; 113 | } 114 | 115 | let options = { 116 | method: method, 117 | url: SendOtp.getBaseURL() + "" + path 118 | }; 119 | 120 | if (method === 'get') { 121 | options.qs = params; 122 | } 123 | 124 | // Pass form data if post 125 | if (method === 'post') { 126 | let formKey = 'form'; 127 | 128 | if (typeof params.media !== 'undefined') { 129 | formKey = 'formData'; 130 | } 131 | options[formKey] = params; 132 | } 133 | 134 | request(options, function(error, response, data) { 135 | // request error 136 | if (error) { 137 | return callback(error, data); 138 | } 139 | 140 | // JSON parse error or empty strings 141 | try { 142 | // An empty string is a valid response 143 | if (data === '') { 144 | data = {}; 145 | } 146 | else { 147 | data = JSON.parse(data); 148 | } 149 | } 150 | catch(parseError) { 151 | return callback( 152 | new Error('JSON parseError with HTTP Status: ' + response.statusCode + ' ' + response.statusMessage), 153 | data 154 | ); 155 | } 156 | 157 | 158 | // response object errors 159 | // This should return an error object not an array of errors 160 | if (data.errors !== undefined) { 161 | return callback(data.errors, data); 162 | } 163 | 164 | // status code errors 165 | if(response.statusCode < 200 || response.statusCode > 299) { 166 | return callback( 167 | new Error('HTTP Error: ' + response.statusCode + ' ' + response.statusMessage), 168 | data 169 | ); 170 | } 171 | // no errors 172 | callback(null, data); 173 | }); 174 | 175 | }; 176 | 177 | } 178 | 179 | module.exports = SendOtp; 180 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sendotp", 3 | "version": "1.2.8", 4 | "description": "Integrate sendotp", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha --reporter spec" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/MSG91/sendotp-node" 12 | }, 13 | "keywords": [ 14 | "SendOtp", 15 | "Flow", 16 | "Node", 17 | "API" 18 | ], 19 | "author": "Priyanka P", 20 | "license": "MIT", 21 | "homepage": "https://github.com/MSG91/sendotp-node", 22 | "dependencies": { 23 | "request": "^2.81.0" 24 | }, 25 | "bugs": { 26 | "url": "https://github.com/MSG91/sendotp-node/issues" 27 | }, 28 | "directories": { 29 | "test": "test" 30 | }, 31 | "devDependencies": { 32 | "express": "^4.16.3", 33 | "mocha": "*", 34 | "nock": "*" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/socketTest.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const SendOtp = require('./../index'); 4 | const nock = require('nock'); 5 | const assert = require('assert'); 6 | 7 | describe('SendOtp', () => { 8 | it('create new instance', () => { 9 | let sendOtp = new SendOtp(); 10 | assert(sendOtp instanceof SendOtp); 11 | }); 12 | 13 | describe('constructor', () => { 14 | it('set default template if no message template is provided', () => { 15 | let sendOtp = new SendOtp('auth-key'); 16 | assert.equal(sendOtp.authKey, 'auth-key'); 17 | assert.equal(sendOtp.messageTemplate, 'Your otp is {{otp}}. Please do not share it with anybody'); 18 | }); 19 | it('message template is provided', () => { 20 | let sendOtp = new SendOtp('auth-key', 'new template to send otp'); 21 | assert.equal(sendOtp.authKey, 'auth-key'); 22 | assert.equal(sendOtp.messageTemplate, 'new template to send otp'); 23 | }); 24 | }); 25 | 26 | describe('doRequest', () => { 27 | before(() => { 28 | this.nock = nock('https://control.msg91.com'); 29 | }); 30 | 31 | it('accepts any 2xx response', (done) => { 32 | let jsonResponse = {favorites: []}; 33 | this.nock.get(/.*/).reply(201, jsonResponse); 34 | SendOtp.doRequest('get', 'sendotp.php', (error, data, response) => { 35 | assert.equal(error, null); 36 | assert.deepEqual(data, jsonResponse); 37 | assert.notEqual(response, null); 38 | done(); 39 | }); 40 | }); 41 | 42 | it('errors when there is an error object', (done) => { 43 | let jsonResponse = {errors: ['nope']}; 44 | this.nock.get(/.*/).reply(203, jsonResponse); 45 | SendOtp.doRequest('get', 'sendotp.php', (error, data, response) => { 46 | assert.deepEqual(error, ['nope']); 47 | assert.deepEqual(data, jsonResponse); 48 | assert.notEqual(response, null); 49 | done(); 50 | }); 51 | }); 52 | }); 53 | 54 | }); --------------------------------------------------------------------------------