1
+ import { SMSData , SMSProvider , SMSResponse , SMSResponseStatus } from "../sms.interface" ;
2
+ import { InjectRedis } from '@nestjs-modules/ioredis' ;
3
+ import Redis from 'ioredis' ;
4
+ import { Injectable } from "@nestjs/common" ;
5
+
6
+
7
+ @Injectable ( )
8
+ export class GupshupWhatsappService {
9
+
10
+ constructor (
11
+ @InjectRedis ( ) private readonly redis : Redis
12
+ ) {
13
+ }
14
+
15
+ async sendWhatsappOTP ( smsData : SMSData ) : Promise < SMSResponse > {
16
+ const status : SMSResponse = {
17
+ providerResponseCode : null ,
18
+ status : SMSResponseStatus . failure ,
19
+ messageID : null ,
20
+ error : null ,
21
+ providerSuccessResponse : null ,
22
+ phone : smsData . phone ,
23
+ networkResponseCode : null ,
24
+ provider : SMSProvider . gupshup
25
+ } ;
26
+
27
+ // Generate 4 digit OTP
28
+ const otp = Math . floor ( 1000 + Math . random ( ) * 9000 ) ;
29
+
30
+ try {
31
+ // First opt-in the user
32
+ const optInParams = new URLSearchParams ( ) ;
33
+ optInParams . append ( "method" , "OPT_IN" ) ;
34
+ optInParams . append ( "format" , "text" ) ;
35
+ optInParams . append ( "userid" , process . env . GUPSHUP_WHATSAPP_USERID ) ;
36
+ optInParams . append ( "password" , process . env . GUPSHUP_WHATSAPP_PASSWORD ) ;
37
+ optInParams . append ( "phone_number" , `91${ smsData . phone } ` ) ;
38
+ optInParams . append ( "v" , "1.1" ) ;
39
+ optInParams . append ( "auth_scheme" , "plain" ) ;
40
+ optInParams . append ( "channel" , "WHATSAPP" ) ;
41
+
42
+ let optinURL = process . env . GUPSHUP_WHATSAPP_BASEURL + '?' + optInParams . toString ( ) ;
43
+
44
+ await fetch ( optinURL , {
45
+ method : 'GET' ,
46
+ headers : {
47
+ 'Content-Type' : 'application/json'
48
+ }
49
+ } ) ;
50
+
51
+ // Then send OTP message
52
+ const otpMessage = `${ otp } is your verification code. For your security, do not share this code.` ;
53
+
54
+ const sendOtpParams = new URLSearchParams ( ) ;
55
+ sendOtpParams . append ( "method" , "SENDMESSAGE" ) ;
56
+ sendOtpParams . append ( "userid" , process . env . GUPSHUP_WHATSAPP_USERID ) ;
57
+ sendOtpParams . append ( "password" , process . env . GUPSHUP_WHATSAPP_PASSWORD ) ;
58
+ sendOtpParams . append ( "send_to" , smsData . phone ) ;
59
+ sendOtpParams . append ( "v" , "1.1" ) ;
60
+ sendOtpParams . append ( "format" , "json" ) ;
61
+ sendOtpParams . append ( "msg_type" , "TEXT" ) ;
62
+ sendOtpParams . append ( "msg" , otpMessage ) ;
63
+ sendOtpParams . append ( "isTemplate" , "true" ) ;
64
+ sendOtpParams . append ( "footer" , "This code expires in 30 minute." ) ;
65
+
66
+ let sendOtpURL = process . env . GUPSHUP_WHATSAPP_BASEURL + '?' + sendOtpParams . toString ( ) ;
67
+
68
+ const response = await fetch ( sendOtpURL , {
69
+ method : 'GET' ,
70
+ headers : {
71
+ 'Content-Type' : 'application/json'
72
+ }
73
+ } ) ;
74
+
75
+ if ( response . status === 200 ) {
76
+ // Store OTP in Redis with 30 minute expiry
77
+ await this . redis . set ( `whatsapp_otp:${ smsData . phone } ` , otp . toString ( ) , 'EX' , 1800 ) ;
78
+
79
+ status . providerSuccessResponse = await response . text ( ) ;
80
+ status . status = SMSResponseStatus . success ;
81
+ status . messageID = otp . toString ( ) ;
82
+ }
83
+
84
+ return status ;
85
+
86
+ } catch ( error ) {
87
+ status . error = {
88
+ errorCode : error . code || 'WHATSAPP_ERROR' ,
89
+ errorText : error . message
90
+ } ;
91
+ return status ;
92
+ }
93
+ }
94
+
95
+ async verifyWhatsappOTP ( phone : string , otp : string ) : Promise < SMSResponse > {
96
+ const status : SMSResponse = {
97
+ providerResponseCode : null ,
98
+ status : SMSResponseStatus . failure ,
99
+ messageID : null ,
100
+ error : null ,
101
+ providerSuccessResponse : null ,
102
+ phone : phone ,
103
+ networkResponseCode : null ,
104
+ provider : SMSProvider . gupshup
105
+ } ;
106
+
107
+ try {
108
+ // Get stored OTP from Redis
109
+ const storedOTP = await this . redis . get ( `whatsapp_otp:${ phone } ` ) ;
110
+ console . log ( "storedOTP" , storedOTP )
111
+
112
+ if ( ! storedOTP ) {
113
+ status . error = {
114
+ errorCode : 'OTP_EXPIRED' ,
115
+ errorText : 'OTP has expired or does not exist'
116
+ } ;
117
+ return status ;
118
+ }
119
+
120
+ if ( storedOTP === otp ) {
121
+ // OTP matches
122
+ status . status = SMSResponseStatus . success ;
123
+ status . providerSuccessResponse = 'OTP verified successfully' ;
124
+
125
+ // Delete the OTP from Redis after successful verification
126
+ await this . redis . del ( `whatsapp_otp:${ phone } ` ) ;
127
+ } else {
128
+ status . error = {
129
+ errorCode : 'INVALID_OTP' ,
130
+ errorText : 'Invalid OTP provided'
131
+ } ;
132
+ }
133
+
134
+ return status ;
135
+
136
+ } catch ( error ) {
137
+ status . error = {
138
+ errorCode : error . code || 'VERIFICATION_ERROR' ,
139
+ errorText : error . message
140
+ } ;
141
+ return status ;
142
+ }
143
+ }
144
+ }
0 commit comments